← Back to team overview

zorba-coders team mailing list archive

[Merge] lp:~zorba-coders/zorba/process-2 into lp:zorba

 

Nicolae Brinza has proposed merging lp:~zorba-coders/zorba/process-2 into lp:zorba.

Commit message:
Changed module's errors to the new modules guidelines; Potential fix for execvpe() on Macs

Requested reviews:
  Nicolae Brinza (nbrinza)
Related bugs:
  Bug #1188053 in Zorba: "Update non-core module "process""
  https://bugs.launchpad.net/zorba/+bug/1188053

For more details, see:
https://code.launchpad.net/~zorba-coders/zorba/process-2/+merge/171822

Changed module's errors to the new modules guidelines; Potential fix for execvpe() on Macs
-- 
https://code.launchpad.net/~zorba-coders/zorba/process-2/+merge/171822
Your team Zorba Coders is subscribed to branch lp:zorba.
=== added file 'CMakeLists.txt'
--- CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ CMakeLists.txt	2013-06-27 14:44:39 +0000
@@ -0,0 +1,34 @@
+# Copyright 2006-2010 The FLWOR Foundation.
+# 
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# 
+# http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+
+PROJECT (zorba_process_module)
+ENABLE_TESTING ()
+INCLUDE (CTest)
+
+LIST (APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake_modules")
+
+FIND_PACKAGE (Zorba REQUIRED HINTS "${ZORBA_BUILD_DIR}")
+INCLUDE ("${Zorba_USE_FILE}")
+
+ADD_SUBDIRECTORY("src")
+
+ADD_TEST_DIRECTORY ("${PROJECT_SOURCE_DIR}/test")
+
+IF (WIN32)
+  EXPECTED_FAILURE (zorba_process_module/process.xq 866979)
+ENDIF (WIN32)
+
+DONE_DECLARING_ZORBA_URIS()

=== renamed file 'CMakeLists.txt' => 'CMakeLists.txt.moved'
=== added directory 'src'
=== renamed directory 'src' => 'src.moved'
=== added file 'src/CMakeLists.txt'
--- src/CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ src/CMakeLists.txt	2013-06-27 14:44:39 +0000
@@ -0,0 +1,16 @@
+# Copyright 2006-2013 The FLWOR Foundation.
+# 
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# 
+# http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+DECLARE_ZORBA_MODULE (URI "http://zorba.io/modules/process"; VERSION 1.0 FILE "process-2.xq")
+DECLARE_ZORBA_MODULE (URI "http://www.zorba-xquery.com/modules/process"; VERSION 1.0 FILE "process-1.xq")

=== added file 'src/process-1.xq'
--- src/process-1.xq	1970-01-01 00:00:00 +0000
+++ src/process-1.xq	2013-06-27 14:44:39 +0000
@@ -0,0 +1,95 @@
+xquery version "3.0";
+
+(:
+ : Copyright 2006-2013 The FLWOR Foundation.
+ :
+ : Licensed under the Apache License, Version 2.0 (the "License");
+ : you may not use this file except in compliance with the License.
+ : You may obtain a copy of the License at
+ :
+ : http://www.apache.org/licenses/LICENSE-2.0
+ :
+ : Unless required by applicable law or agreed to in writing, software
+ : distributed under the License is distributed on an "AS IS" BASIS,
+ : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ : See the License for the specific language governing permissions and
+ : limitations under the License.
+:)
+
+(:~
+ : This module provides functions to create a native process and return the result
+ : (i.e. exit code, result on standard out and error).
+ : <p>
+ : Example:
+ :<pre class="ace-static" ace-mode="xquery">
+ :  import module namespace proc = "http://www.zorba-xquery.com/modules/process";;
+ :  proc:exec("ls")
+ :</pre>
+ : </p>
+ : <p>
+ : Potential result:
+ : <pre class="ace-static" ace-mode="xquery"><![CDATA[
+ : <result xmlns="http://www.zorba-xquery.com/modules/process";>
+ :   <stdout>myfile.txt</stout>
+ :   <stderr/>
+ :   <exit-code>0</exit-code>
+ : </result>
+ : ]]></pre>
+ : </p>
+ :
+ : @author Cezar Andrei
+ : @project Zorba/IO/Process
+ :
+ :)
+module namespace process = "http://www.zorba-xquery.com/modules/process";;
+
+declare namespace an = "http://www.zorba-xquery.com/annotations";;
+
+declare namespace ver = "http://www.zorba-xquery.com/options/versioning";;
+declare option ver:module-version "1.0";
+
+(:~
+ : Executes the specified string command in a separate process.
+ : This function does not allow arguments to be passed to
+ : the command.
+ :
+ : @param $cmd command to be executed (without arguments)
+ :
+ : @return the result of the execution as an element as
+ :         shown in the documentation of this module. The exit-code
+ :         element returns the exit code of the child process.
+ : For POSIX compliant platforms: returns the process exit code. If process is 
+ : terminated or stopped: 128 + termination signal code. 
+ : For Windows platforms: returns the return value of the process or the exit 
+ : or terminate process specified value.
+ :
+ : @error process:PROC01 if an error occurred while communicating 
+ :   with the executed process.
+ :)
+declare %an:sequential function process:exec(
+  $cmd as xs:string
+) as element(process:result) external;
+
+(:~
+ : Executes the specified string command in a separate process.
+ : Each of the strings in the sequence passed in as the second
+ : argument is passed as an argument to the executed command.
+ :
+ : @param $cmd command to be executed (without arguments)
+ : @param $args the arguments passed to the executed command (e.g. "-la")
+ :
+ : @return the result of the execution as an element as
+ :         shown in the documentation of this module. The exit-code
+ :         element returns the exit code of the child process.
+ : For POSIX compliant platforms: returns the process exit code. If process is 
+ : terminated or stopped: 128 + termination signal code. 
+ : For Windows platforms: returns the return value of the process or the exit 
+ : or terminate process specified value.
+ :
+ : @error process:PROC01 if an error occurred while communicating 
+ :   with the executed process.
+ :)
+declare %an:sequential function process:exec(
+  $cmd as xs:string,
+  $args as xs:string*
+) as element(process:result) external;

=== added directory 'src/process-1.xq.src'
=== added file 'src/process-1.xq.src/process.cpp'
--- src/process-1.xq.src/process.cpp	1970-01-01 00:00:00 +0000
+++ src/process-1.xq.src/process.cpp	2013-06-27 14:44:39 +0000
@@ -0,0 +1,577 @@
+/*
+ * Copyright 2006-2008 The FLWOR Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sstream>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <vector>
+#include <iostream>
+#include <limits.h>
+#include <algorithm>
+
+#ifdef WIN32
+#  include <windows.h>
+
+#  ifndef NDEBUG
+#    define _CRTDBG_MAP_ALLOC
+#    include <stdlib.h>
+#    include <crtdbg.h>
+#   endif
+#else
+#  include <unistd.h>
+#  ifdef __APPLE__
+#    include <sys/wait.h>
+#  else
+#    include <wait.h>
+#  endif
+#endif
+
+#include <zorba/item_factory.h>
+#include <zorba/singleton_item_sequence.h>
+#include <zorba/diagnostic_list.h>
+#include <zorba/user_exception.h>
+#include <zorba/empty_sequence.h>
+
+#include "process.h"
+
+namespace zorba {
+namespace processmodule {
+
+/******************************************************************************
+ *****************************************************************************/
+void create_result_node(
+    zorba::Item&        aResult,
+    const std::string&  aStandardOut,
+    const std::string&  aErrorOut,
+    int                 aExitCode,
+    zorba::ItemFactory* aFactory)
+{
+  zorba::Item lResultQName =
+    aFactory->createQName("http://www.zorba-xquery.com/modules/process";, "result");
+  zorba::Item lExitCodeQName =
+    aFactory->createQName("http://www.zorba-xquery.com/modules/process";, "exit-code");
+  zorba::Item lOutputQName =
+    aFactory->createQName("http://www.zorba-xquery.com/modules/process";, "stdout");
+  zorba::Item lErrorQName =
+    aFactory->createQName("http://www.zorba-xquery.com/modules/process";, "stderr");
+  zorba::Item lNullItem;
+  zorba::Item lTypeName =
+    aFactory->createQName("http://www.w3.org/2001/XMLSchema";, "untyped");
+
+  zorba::NsBindings lNSBindings;
+
+  // root node called result
+  aResult = aFactory->createElementNode(
+      lNullItem, lResultQName, lTypeName, false, false, lNSBindings);
+
+  // <result><output> aStandardOut </output></result>
+  zorba::Item lOutput;
+  lOutput = aFactory->createElementNode(
+      aResult, lOutputQName, lTypeName, true, false, lNSBindings);
+  aFactory->createTextNode(lOutput, aStandardOut);
+
+  // <result><error> aErrorOut </error></result>
+  zorba::Item lError;
+  lError = aFactory->createElementNode(
+      aResult, lErrorQName, lTypeName, true, false, lNSBindings);
+  aFactory->createTextNode(lError, aErrorOut);
+
+  // <result><exit-code> aExitCode </exit-code></result>
+  zorba::Item lExitCode;
+  lExitCode = aFactory->createElementNode(
+      aResult, lExitCodeQName, lTypeName, true, false, lNSBindings);
+  std::ostringstream lExitCodeString;
+  lExitCodeString << aExitCode;
+  aFactory->createTextNode(lExitCode, lExitCodeString.str());
+}
+
+#ifdef WIN32
+
+/***********************************************
+*  throw a descriptive message of the last error
+*  accessible with GetLastError() on windows
+*/
+void throw_last_error(const zorba::String& aFilename, unsigned int aLineNumber){
+  LPVOID lpvMessageBuffer;
+  TCHAR lErrorBuffer[512];
+  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
+          NULL, GetLastError(),
+          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+          (LPTSTR)&lpvMessageBuffer, 0, NULL);
+  wsprintf(lErrorBuffer,TEXT("Process Error Code: %d - Message= %s"),GetLastError(), (TCHAR *)lpvMessageBuffer);
+  LocalFree(lpvMessageBuffer);
+  Item lQName = ProcessModule::getItemFactory()->createQName(
+    "http://www.zorba-xquery.com/modules/process";,
+    "PROC01");
+#ifdef UNICODE
+  char error_str[1024];
+  WideCharToMultiByte(CP_UTF8, 0, lErrorBuffer, -1, error_str, sizeof(error_str), NULL, NULL);
+  throw USER_EXCEPTION(lQName, error_str);
+#else
+  throw USER_EXCEPTION(lQName, lErrorBuffer);
+#endif
+}
+
+/******************************************
+*  read output from child process on windows
+*/
+void read_child_output(HANDLE aOutputPipe, std::ostringstream& aTargetStream)
+{
+  CHAR lBuffer[256];
+  DWORD lBytesRead;
+  
+  while(TRUE)
+  {
+    if (
+      !ReadFile(aOutputPipe,lBuffer,sizeof(lBuffer),&lBytesRead,NULL) 
+      || !lBytesRead
+    )
+    {
+      if (GetLastError() == ERROR_BROKEN_PIPE)
+        break; // finished
+      else{
+      
+         // couldn't read from pipe
+         throw_last_error(__FILE__, __LINE__);
+      }
+    }
+    
+    // remove the windows specific carriage return outputs
+   // std::stringstream lTmp;
+   // lTmp.write(lBuffer,lBytesRead);
+   // std::string lRawString=lTmp.str();
+   // std::replace( lRawString.begin(), lRawString.end(), '\r', ' ' );
+   // aTargetStream.write(lRawString.c_str(),static_cast<std::streamsize>(lRawString.length()));
+    for(DWORD i=0;i<lBytesRead;i++)
+    {
+      if(lBuffer[i] != '\r')
+        aTargetStream << lBuffer[i];
+    }
+    lBytesRead = 0;
+  }
+}
+
+/******************************************
+*  Create a child process on windows with
+*  redirected output
+*/
+BOOL create_child_process(HANDLE aStdOutputPipe,HANDLE aStdErrorPipe,const std::string& aCommand,PROCESS_INFORMATION& aProcessInformation){
+  STARTUPINFO lChildStartupInfo;
+  BOOL result=FALSE;
+  
+  // set the output handles
+  FillMemory(&lChildStartupInfo,sizeof(lChildStartupInfo),0);
+  lChildStartupInfo.cb = sizeof(lChildStartupInfo);
+  GetStartupInfo(&lChildStartupInfo);
+  lChildStartupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
+  lChildStartupInfo.wShowWindow = SW_HIDE; // don't show the command window
+  lChildStartupInfo.hStdOutput = aStdOutputPipe;
+  lChildStartupInfo.hStdError  = aStdErrorPipe;
+
+  // convert from const char* to char*
+  size_t length = strlen(aCommand.c_str());
+#ifdef UNICODE
+  WCHAR *tmpCommand = new WCHAR[length+1];
+  MultiByteToWideChar(CP_UTF8, 0, aCommand.c_str(), -1, tmpCommand, length+1);
+#else
+  char *tmpCommand=new char[length+1];
+  strcpy (tmpCommand,aCommand.c_str());
+  tmpCommand[length]='\0';
+#endif
+
+  try{
+  
+    // settings for the child process      
+    LPCTSTR lApplicationName=NULL;
+    LPTSTR lCommandLine=tmpCommand;
+    LPSECURITY_ATTRIBUTES lProcessAttributes=NULL;
+    LPSECURITY_ATTRIBUTES lThreadAttributes=NULL;
+    BOOL lInheritHandles=TRUE; // that's what we want
+    DWORD lCreationFlags=CREATE_NEW_CONSOLE;
+    LPVOID lEnvironment=NULL;
+    LPCTSTR lCurrentDirectory=NULL; // same as main process
+    
+    // start child
+    result=CreateProcess(
+          lApplicationName,lCommandLine,lProcessAttributes,
+          lThreadAttributes,lInheritHandles,lCreationFlags,
+          lEnvironment,lCurrentDirectory,&lChildStartupInfo,
+          &aProcessInformation);
+          
+  }catch(...){
+    delete[] tmpCommand;
+    tmpCommand=0;
+    throw;  
+  }
+        
+  delete[] tmpCommand;
+  tmpCommand=0;
+        
+  return result;
+}
+
+/******************************************
+*  run a process that executes the aCommand
+*  in a new console and reads the output
+*/
+int run_process(
+  const std::string& aCommand,
+  std::ostringstream& aTargetOutStream,
+  std::ostringstream& aTargetErrStream)
+{
+  HANDLE lOutRead, lErrRead, lStdOut, lStdErr;
+  SECURITY_ATTRIBUTES lSecurityAttributes;
+  PROCESS_INFORMATION lChildProcessInfo;
+  DWORD exitCode=0;
+    
+  // prepare security attributes
+  lSecurityAttributes.nLength= sizeof(lSecurityAttributes);
+  lSecurityAttributes.lpSecurityDescriptor = NULL;
+  lSecurityAttributes.bInheritHandle = TRUE;
+
+  // create output pipes
+  if(
+      !CreatePipe(&lOutRead,&lStdOut,&lSecurityAttributes,1024*1024) // std::cout >> lOutRead
+      || !CreatePipe(&lErrRead,&lStdErr,&lSecurityAttributes,1024*1024) // std::cerr >> lErrRead
+    ){
+    Item lQName = ProcessModule::getItemFactory()->createQName(
+      "http://www.zorba-xquery.com/modules/process";, "PROC01");
+    throw USER_EXCEPTION(lQName,
+      "Couldn't create one of std::cout/std::cerr pipe for child process execution."
+    );
+  };
+  
+  //start child process
+  BOOL ok = create_child_process(lStdOut,lStdErr,aCommand,lChildProcessInfo);
+  if(ok==TRUE)
+  {
+
+    // close unneeded handle  
+    CloseHandle(lChildProcessInfo.hThread);
+    
+    // wait for the process to finish
+    WaitForSingleObject(lChildProcessInfo.hProcess,INFINITE);
+    if (!GetExitCodeProcess(lChildProcessInfo.hProcess, &exitCode))
+    {
+      std::stringstream lErrorMsg;
+      lErrorMsg 
+        << "Couldn't get exit code from child process. Executed command: '" << aCommand << "'.";
+      Item lQName = ProcessModule::getItemFactory()->createQName(
+        "http://www.zorba-xquery.com/modules/process";, "PROC01");
+      throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
+    }
+  
+    CloseHandle(lChildProcessInfo.hProcess);
+    CloseHandle(lStdOut);
+    CloseHandle(lStdErr);
+
+    // read child's output
+    read_child_output(lOutRead,aTargetOutStream);
+    read_child_output(lErrRead,aTargetErrStream);
+
+    // close 
+    CloseHandle(lOutRead);
+    CloseHandle(lErrRead);
+
+ }else{
+    CloseHandle(lStdOut);
+    CloseHandle(lStdErr);
+    CloseHandle(lOutRead);
+    CloseHandle(lErrRead);
+  
+     // couldn't launch process
+     throw_last_error(__FILE__, __LINE__);
+  };
+  
+  
+  return exitCode;
+}
+
+#else
+
+#define READ  0
+#define WRITE 1
+
+pid_t zorba_popen(const char *command, int *infp, int *outfp, int *errfp)
+{
+    int p_stdin[2];
+    int p_stdout[2];
+    int p_stderr[2];
+    pid_t pid;
+
+    if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0 || pipe(p_stderr) != 0)
+      return -1;
+
+    pid = fork();
+
+    if (pid < 0)
+      return pid;
+    else if (pid == 0)
+    {
+      close(p_stdin[WRITE]);
+      dup2(p_stdin[READ], 0);   // duplicate stdin
+
+      close(p_stdout[READ]);
+      dup2(p_stdout[WRITE], 1); // duplicate stdout
+
+      close(p_stderr[READ]);
+      dup2(p_stderr[WRITE], 2); // duplicate stderr
+
+      execl("/bin/sh", "sh", "-c", command, NULL);
+      perror("execl"); // output the result to standard error
+      exit(errno);
+    }
+
+    if (infp == NULL)
+      close(p_stdin[WRITE]);
+    else
+      *infp = p_stdin[WRITE];
+
+    if (outfp == NULL)
+      close(p_stdout[READ]);
+    else
+      *outfp = p_stdout[READ];
+
+    if (errfp == NULL)
+      close(p_stderr[READ]);
+    else
+      *errfp = p_stderr[READ];
+
+    close(p_stdin[READ]);   // We only write to the forks stdin anyway
+    close(p_stdout[WRITE]); // and we only read from its stdout
+    close(p_stderr[WRITE]); // and we only read from its stderr
+        
+    return pid;
+}
+#endif
+
+/******************************************************************************
+ *****************************************************************************/
+zorba::ItemSequence_t
+ExecFunction::evaluate(
+  const Arguments_t& aArgs,
+  const zorba::StaticContext* aSctx,
+  const zorba::DynamicContext* aDctx) const
+{
+  std::string lCommand;
+  std::vector<std::string> lArgs;
+  int exit_code = 0;
+
+  lCommand = getOneStringArgument(aArgs, 0).c_str();
+
+  if (aArgs.size() > 1)
+  {
+    zorba::Item lArg;
+    Iterator_t arg1_iter = aArgs[1]->getIterator();
+    arg1_iter->open();
+    while (arg1_iter->next(lArg))
+    {
+      lArgs.push_back(lArg.getStringValue().c_str());
+    }
+    arg1_iter->close();
+  }
+
+  std::ostringstream lTmp;
+
+#ifdef WIN32
+  // execute process command in a new commandline
+  // with quotes at the beggining and at the end
+  lTmp << "cmd /C \"";
+#endif
+ 
+  lTmp << "\"" << lCommand << "\""; //quoted for spaced paths/filenames
+  size_t pos=0;
+  for (std::vector<std::string>::const_iterator lIter = lArgs.begin();
+       lIter != lArgs.end(); ++lIter)
+  {
+    pos = (*lIter).rfind('\\')+(*lIter).rfind('/');
+    if (int(pos)>=0)
+      lTmp << " \"" << *lIter << "\"";
+    else
+      lTmp << " " << *lIter;
+  }
+#ifdef WIN32
+  lTmp << "\"";   // with quotes at the end for commandline
+#endif
+  
+  std::ostringstream lStdout;
+  std::ostringstream lStderr;
+
+#ifdef WIN32
+  std::string lCommandLineString = lTmp.str();
+  int code = run_process(lCommandLineString, lStdout, lStderr);
+  
+  if (code != 0)
+  {
+    std::stringstream lErrorMsg;
+    lErrorMsg << "Failed to execute the command (" << code << ")";
+    Item lQName = ProcessModule::getItemFactory()->createQName(
+      "http://www.zorba-xquery.com/modules/process";, "PROC01");
+    throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
+  }
+  exit_code = code;
+
+#else //not WIN32
+
+  int outfp;
+  int errfp;
+  int status;
+  pid_t pid;
+
+  pid = zorba_popen(lTmp.str().c_str(), NULL, &outfp, &errfp);
+  if ( pid == -1 )
+  {
+    std::stringstream lErrorMsg;
+    lErrorMsg << "Failed to execute the command (" << pid << ")";
+    Item lQName = ProcessModule::getItemFactory()->createQName(
+      "http://www.zorba-xquery.com/modules/process";, "PROC01");
+    throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
+  }
+  else
+  {
+    char lBuf[PATH_MAX];
+    ssize_t length = 0;
+    while ( (length=read(outfp, lBuf, PATH_MAX)) > 0 )
+    {
+      lStdout.write(lBuf, length);
+    }
+
+    status = close(outfp);
+
+    while ( (length=read(errfp, lBuf, PATH_MAX)) > 0 )
+    {
+      lStderr.write(lBuf, length);
+    }
+
+    status = close(errfp);
+
+    if ( status < 0 )
+    {
+      std::stringstream lErrorMsg;
+      lErrorMsg << "Failed to close the err stream (" << status << ")";
+      Item lQName = ProcessModule::getItemFactory()->createQName(
+        "http://www.zorba-xquery.com/modules/process";, "PROC01");
+      throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
+    }
+
+    int  stat = 0;
+    
+    pid_t w = waitpid(pid, &stat, 0);
+    
+    if (w == -1) 
+    { 
+        std::stringstream lErrorMsg;
+        lErrorMsg << "Failed to wait for child process ";
+        Item lQName = ProcessModule::getItemFactory()->createQName(
+          "http://www.zorba-xquery.com/modules/process";, "PROC01");
+        throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());          
+    }
+
+    if (WIFEXITED(stat)) 
+    {
+        //std::cout << " WEXITSTATUS : " << WEXITSTATUS(stat) << std::endl; std::cout.flush();
+        exit_code = WEXITSTATUS(stat);
+    } 
+    else if (WIFSIGNALED(stat)) 
+    {
+        //std::cout << " WTERMSIG : " << WTERMSIG(stat) << std::endl; std::cout.flush();
+        exit_code = 128 + WTERMSIG(stat);
+    }
+    else if (WIFSTOPPED(stat)) 
+    {
+        //std::cout << " STOPSIG : " << WSTOPSIG(stat) << std::endl; std::cout.flush();
+        exit_code = 128 + WSTOPSIG(stat);
+    }
+    else
+    {
+        //std::cout << " else : " << std::endl; std::cout.flush();
+        exit_code = 255;
+    }
+    
+    //std::cout << " exit_code : " << exit_code << std::endl; std::cout.flush();
+
+  }
+#endif // WIN32
+
+  zorba::Item lResult;
+  create_result_node(lResult, lStdout.str(), lStderr.str(), exit_code,
+                     theModule->getItemFactory());
+
+  return zorba::ItemSequence_t(new zorba::SingletonItemSequence(lResult));
+}
+
+String ExecFunction::getOneStringArgument (const Arguments_t& aArgs, int aPos)
+  const
+{
+  Item lItem;
+  Iterator_t  args_iter = aArgs[aPos]->getIterator();
+  args_iter->open();
+  args_iter->next(lItem);
+  zorba::String lTmpString = lItem.getStringValue();
+  args_iter->close();
+  return lTmpString;
+}
+
+/******************************************************************************
+ *****************************************************************************/
+ProcessModule::~ProcessModule()
+{
+  for (FuncMap_t::const_iterator lIter = theFunctions.begin();
+       lIter != theFunctions.end(); ++lIter) {
+    delete lIter->second;
+  }
+  theFunctions.clear();
+}
+
+zorba::ExternalFunction*
+ProcessModule::getExternalFunction(const zorba::String& aLocalname)
+{
+  FuncMap_t::const_iterator lFind = theFunctions.find(aLocalname);
+  zorba::ExternalFunction*& lFunc = theFunctions[aLocalname];
+  if (lFind == theFunctions.end())
+  {
+    if (!aLocalname.compare("exec"))
+    {
+      lFunc = new ExecFunction(this);
+    }
+  }
+  return lFunc;
+}
+
+void ProcessModule::destroy()
+{
+  if (!dynamic_cast<ProcessModule*>(this)) {
+    return;
+  }
+  delete this;
+}
+
+ItemFactory* ProcessModule::theFactory = 0;
+
+} /* namespace processmodule */
+} /* namespace zorba */
+
+#ifdef WIN32
+#  define DLL_EXPORT __declspec(dllexport)
+#else
+#  define DLL_EXPORT __attribute__ ((visibility("default")))
+#endif
+
+extern "C" DLL_EXPORT zorba::ExternalModule* createModule() {
+  return new zorba::processmodule::ProcessModule();
+}
+/* vim:set et sw=2 ts=2: */

=== added file 'src/process-1.xq.src/process.h'
--- src/process-1.xq.src/process.h	1970-01-01 00:00:00 +0000
+++ src/process-1.xq.src/process.h	2013-06-27 14:44:39 +0000
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2006-2008 The FLWOR Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ZORBA_PROCESSMODULE_PROCESS_H
+#define ZORBA_PROCESSMODULE_PROCESS_H
+
+#include <map>
+
+#include <zorba/zorba.h>
+#include <zorba/error.h>
+#include <zorba/external_module.h>
+#include <zorba/function.h>
+
+namespace zorba {
+namespace processmodule {
+
+/******************************************************************************
+ *****************************************************************************/
+class ProcessModule : public ExternalModule
+{
+private:
+  static ItemFactory* theFactory;
+
+protected:
+  class ltstr
+  {
+  public:
+    bool operator()(const String& s1, const String& s2) const
+    {
+      return s1.compare(s2) < 0;
+    }
+  };
+
+  typedef std::map<String, ExternalFunction*, ltstr> FuncMap_t;
+
+  FuncMap_t theFunctions;
+
+public:
+  virtual ~ProcessModule();
+
+  virtual zorba::String getURI() const
+  { return "http://www.zorba-xquery.com/modules/process";; }
+
+  virtual zorba::ExternalFunction*
+  getExternalFunction(const zorba::String& aLocalname);
+
+  virtual void destroy();
+
+  static ItemFactory* getItemFactory()
+  {
+    if(!theFactory)
+    {
+      theFactory = Zorba::getInstance(0)->getItemFactory();
+    }
+
+    return theFactory;
+  }
+};
+
+/******************************************************************************
+ *****************************************************************************/
+class ExecFunction : public ContextualExternalFunction
+{
+public:
+  ExecFunction(const ProcessModule* aModule) : theModule(aModule) {}
+
+  virtual ~ExecFunction() {}
+
+  virtual zorba::String
+  getLocalName() const { return "exec"; }
+
+  virtual zorba::ItemSequence_t
+  evaluate(const Arguments_t&,
+           const zorba::StaticContext*,
+           const zorba::DynamicContext*) const;
+
+  virtual String getURI() const
+  {
+    return theModule->getURI();
+  }
+
+protected:
+  const ProcessModule* theModule;
+
+  String getOneStringArgument (const Arguments_t& aArgs, int index) const;
+};
+
+
+
+} /* namespace processmodule */
+} /* namespace zorba */
+
+#endif // ZORBA_PROCESSMODULE_PROCESS_H

=== added file 'src/process-2.xq'
--- src/process-2.xq	1970-01-01 00:00:00 +0000
+++ src/process-2.xq	2013-06-27 14:44:39 +0000
@@ -0,0 +1,197 @@
+jsoniq version "1.0";
+
+(:
+ : Copyright 2006-2013 The FLWOR Foundation.
+ :
+ : Licensed under the Apache License, Version 2.0 (the "License");
+ : you may not use this file except in compliance with the License.
+ : You may obtain a copy of the License at
+ :
+ : http://www.apache.org/licenses/LICENSE-2.0
+ :
+ : Unless required by applicable law or agreed to in writing, software
+ : distributed under the License is distributed on an "AS IS" BASIS,
+ : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ : See the License for the specific language governing permissions and
+ : limitations under the License.
+:)
+
+(:~
+ : <p>
+ : This module provides functions to create a native process and return the result
+ : (i.e. exit code, result on standard out and error) of executing the given
+ : file or command.
+ : </p>
+ :
+ : <p>
+ : Example:
+ : <pre>
+ :   import module namespace p = "http://zorba.io/modules/process";;
+ :   p:exec("ls")
+ : </pre>
+ : </p>
+ :
+ : <p>
+ : Potential result:
+ : <pre>
+ : {
+ :   "exit-code": 0,
+ :   "stdout": "myfile.txt",
+ :   "stderr": ""
+ : }
+ : </pre>
+ : </p>
+ :
+ : <p>
+ : The <tt>exec-command</tt> set of functions allows execution of commands
+ : through the command line interpreter of the operating system, such as "sh"
+ : on Unix systems or "cmd.exe" on Windows. 
+ : </p>
+ :
+ : <p>
+ : For POSIX compliant platforms the functions return 128 + termination signal
+ : code of the process as their exit-code.
+ : On Windows platforms, the exit-code is the return value of the process or the exit 
+ : or terminate process specified value.
+ : </p>
+ :
+ : @author Cezar Andrei, Nicolae Brinza
+ : @project Zorba/Input Output/Process
+ :)
+module namespace p = "http://zorba.io/modules/process";;
+
+declare namespace an = "http://www.zorba-xquery.com/annotations";;
+
+declare namespace ver = "http://www.zorba-xquery.com/options/versioning";;
+declare option ver:module-version "1.0";
+
+
+(:~
+ : <p>
+ : Executes the specified program in a separate process.
+ : </p>
+ :
+ : <p>
+ : This function does not allow arguments to be passed to
+ : the command. The $filename parameter can contain the full path to the 
+ : executable. On Unix systems,  if the specified filename does not contain 
+ : a slash "/", the function duplicates the actions of the shell in searching 
+ : for an executable file. The file is sought in the colon-separated list of 
+ : directory pathnames specified in the PATH environment variable. If this 
+ : variable isn't defined, the path list defaults to the current directory 
+ : followed by the list of directories returned by the operating system.
+ : </p>
+ :
+ : @param $filename the name of program to be executed 
+ :
+ : @return the result of the execution as an object
+ :
+ : @error p:COMMUNICATION if an error occurred while communicating with the process.
+ :)
+declare %an:sequential function p:exec(
+  $filename as string
+) as object external;
+
+(:~
+ : <p>
+ : Executes the specified program in a separate process.
+ : </p>
+ :
+ : <p>
+ : The $filename parameter can contain the full path to the 
+ : executable. On Unix systems,  if the specified filename does not contain 
+ : a slash "/", the function duplicates the actions of the shell in searching 
+ : for an executable file. The file is sought in the colon-separated list of 
+ : directory pathnames specified in the PATH environment variable. If this 
+ : variable isn't defined, the path list defaults to the current directory 
+ : followed by the list of directories returned by the operating system.
+ : The $args parameters will be passed to the executable file as arguments.
+ : </p>
+ :
+ : @param $filename the name of program to be executed 
+ : @param $args arguments to be passed to the executable 
+ :
+ : @return the result of the execution as an object
+ :
+ : @error p:COMMUNICATION if an error occurred while communicating with the process.
+ :)
+declare %an:sequential function p:exec(
+  $filename as string,
+  $args as string*
+) as object external;
+
+(:~
+ : <p>
+ : Executes the specified program in a separate process.
+ : </p>
+ :
+ : <p>
+ : The $filename parameter can contain the full path to the 
+ : executable. On Unix systems,  if the specified filename does not contain 
+ : a slash "/", the function duplicates the actions of the shell in searching 
+ : for an executable file. The file is sought in the colon-separated list of 
+ : directory pathnames specified in the PATH environment variable. If this 
+ : variable isn't defined, the path list defaults to the current directory 
+ : followed by the list of directories returned by the operating system.
+ : </p>
+ :
+ : <p>
+ : The $env allows defining and passing environment variables to the target 
+ : process. They should be in the form "ENVVAR=value" where "ENVVAR" is the 
+ : name of the environment variable and "value' is the string value to set it to.
+ : </p>
+ :
+ : @param $filename the name of program to be executed 
+ : @param $args arguments to be passed to the executable 
+ : @param $env list of environment variables for the executable
+ :
+ : @return the result of the execution as an object
+ :
+ : @error p:COMMUNICATION if an error occurred while communicating with the process.
+ :)
+declare %an:sequential function p:exec(
+  $filename as string,
+  $args as string*,
+  $env as string*
+) as object external;
+
+(:~
+ : <p>
+ : Executes the specified string command in a separate process.
+ : </p>
+ :
+ : <p>
+ : This function does not allow arguments to be passed to the command.
+ : </p>
+ :
+ : @param $cmd command to be executed (without arguments)
+ :
+ : @return the result of the execution as an object
+ :
+ : @error p:COMMUNICATION if an error occurred while communicating with the process.
+ :)
+declare %an:sequential function p:exec-command(
+  $cmd as string
+) as object external;
+
+(:~
+ : <p>
+ : Executes the specified string command in a separate process.
+ : </p>
+ :
+ : <p>
+ : Each of the strings in the sequence passed in as the second
+ : argument is passed as an argument to the executed command.
+ : </p>
+ :
+ : @param $cmd command to be executed (without arguments)
+ : @param $args the arguments passed to the executed command (e.g. "-la")
+ :
+ : @return the result of the execution as an object
+ :
+ : @error p:COMMUNICATION if an error occurred while communicating with the process.
+ :)
+declare %an:sequential function p:exec-command(
+  $cmd as string,
+  $args as string*
+) as object external;

=== added directory 'src/process-2.xq.src'
=== added file 'src/process-2.xq.src/process.cpp'
--- src/process-2.xq.src/process.cpp	1970-01-01 00:00:00 +0000
+++ src/process-2.xq.src/process.cpp	2013-06-27 14:44:39 +0000
@@ -0,0 +1,626 @@
+/*
+ * Copyright 2006-2013 The FLWOR Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sstream>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <vector>
+#include <iostream>
+#include <limits.h>
+#include <algorithm>
+
+#ifdef WIN32
+#  include <windows.h>
+#  ifndef NDEBUG
+#    define _CRTDBG_MAP_ALLOC
+#    include <stdlib.h>
+#    include <crtdbg.h>
+#   endif
+#else
+#  include <unistd.h>
+#  ifdef __APPLE__
+#    include <sys/wait.h>
+#  else
+#    include <wait.h>
+#  endif
+#endif
+
+#include <zorba/item_factory.h>
+#include <zorba/singleton_item_sequence.h>
+#include <zorba/diagnostic_list.h>
+#include <zorba/user_exception.h>
+#include <zorba/empty_sequence.h>
+
+#include "process.h"
+
+// Provde the execvpe() function since some platforms don't have it
+#ifndef WIN32
+int execvpe(const char *program, char **argv, char **envp)
+{
+  clearenv();
+  int i = 0;
+  while (envp[i] != NULL)
+    putenv(envp[i++]);
+  
+  int rc = execvp(program, argv);
+  return rc;
+}
+#endif
+
+
+namespace zorba {
+namespace processmodule {
+
+/******************************************************************************
+ *****************************************************************************/
+void create_result_object(
+    zorba::Item&        aResult,
+    const std::string&  aStandardOut,
+    const std::string&  aErrorOut,
+    int                 aExitCode,
+    zorba::ItemFactory* aFactory)
+{  
+  std::vector<std::pair<zorba::Item,zorba::Item> > pairs;
+  
+  pairs.push_back(std::pair<zorba::Item,zorba::Item>(aFactory->createString("exit-code"), aFactory->createInt(aExitCode)));
+  pairs.push_back(std::pair<zorba::Item,zorba::Item>(aFactory->createString("stdout"), aFactory->createString(aStandardOut)));
+  pairs.push_back(std::pair<zorba::Item,zorba::Item>(aFactory->createString("stderr"), aFactory->createString(aErrorOut)));
+  
+  aResult = aFactory->createJSONObject(pairs);  
+}
+
+void free_char_vector(std::vector<char*> argv)
+{
+  for (unsigned int i=0; i<argv.size(); i++)
+    free(argv[i]);    
+}
+
+#ifdef WIN32
+
+/***********************************************
+*  throw a descriptive message of the last error
+*  accessible with GetLastError() on windows
+*/
+void throw_last_error(const zorba::String& aFilename, unsigned int aLineNumber){
+  LPVOID lpvMessageBuffer;
+  TCHAR lErrorBuffer[512];
+  FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
+          NULL, GetLastError(),
+          MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+          (LPTSTR)&lpvMessageBuffer, 0, NULL);
+  wsprintf(lErrorBuffer,TEXT("Process Error Code: %d - Message= %s"),GetLastError(), (TCHAR *)lpvMessageBuffer);
+  LocalFree(lpvMessageBuffer);
+  Item lQName = ProcessModule::getItemFactory()->createQName(
+    "http://zorba.io/modules/process";, "COMMUNICATION");
+#ifdef UNICODE
+  char error_str[1024];
+  WideCharToMultiByte(CP_UTF8, 0, lErrorBuffer, -1, error_str, sizeof(error_str), NULL, NULL);
+  throw USER_EXCEPTION(lQName, error_str);
+#else
+  throw USER_EXCEPTION(lQName, lErrorBuffer);
+#endif
+}
+
+/******************************************
+*  read output from child process on windows
+*/
+void read_child_output(HANDLE aOutputPipe, std::ostringstream& aTargetStream)
+{
+  CHAR lBuffer[256];
+  DWORD lBytesRead;
+  
+  while(TRUE)
+  {
+    if (
+      !ReadFile(aOutputPipe,lBuffer,sizeof(lBuffer),&lBytesRead,NULL) 
+      || !lBytesRead
+    )
+    {
+      if (GetLastError() == ERROR_BROKEN_PIPE)
+        break; // finished
+      else{
+      
+         // couldn't read from pipe
+         throw_last_error(__FILE__, __LINE__);
+      }
+    }
+    
+    // remove the windows specific carriage return outputs
+   // std::stringstream lTmp;
+   // lTmp.write(lBuffer,lBytesRead);
+   // std::string lRawString=lTmp.str();
+   // std::replace( lRawString.begin(), lRawString.end(), '\r', ' ' );
+   // aTargetStream.write(lRawString.c_str(),static_cast<std::streamsize>(lRawString.length()));
+    for(DWORD i=0;i<lBytesRead;i++)
+    {
+      if(lBuffer[i] != '\r')
+        aTargetStream << lBuffer[i];
+    }
+    lBytesRead = 0;
+  }
+}
+
+/******************************************
+*  Create a child process on windows with
+*  redirected output
+*/
+BOOL create_child_process(HANDLE aStdOutputPipe,HANDLE aStdErrorPipe,const std::string& aCommand,PROCESS_INFORMATION& aProcessInformation){
+  STARTUPINFO lChildStartupInfo;
+  BOOL result=FALSE;
+  
+  // set the output handles
+  FillMemory(&lChildStartupInfo,sizeof(lChildStartupInfo),0);
+  lChildStartupInfo.cb = sizeof(lChildStartupInfo);
+  GetStartupInfo(&lChildStartupInfo);
+  lChildStartupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
+  lChildStartupInfo.wShowWindow = SW_HIDE; // don't show the command window
+  lChildStartupInfo.hStdOutput = aStdOutputPipe;
+  lChildStartupInfo.hStdError  = aStdErrorPipe;
+
+  // convert from const char* to char*
+  size_t length = strlen(aCommand.c_str());
+#ifdef UNICODE
+  WCHAR *tmpCommand = new WCHAR[length+1];
+  MultiByteToWideChar(CP_UTF8, 0, aCommand.c_str(), -1, tmpCommand, length+1);
+#else
+  char *tmpCommand=new char[length+1];
+  strcpy (tmpCommand,aCommand.c_str());
+  tmpCommand[length]='\0';
+#endif
+
+  try{
+  
+    // settings for the child process      
+    LPCTSTR lApplicationName=NULL;
+    LPTSTR lCommandLine=tmpCommand;
+    LPSECURITY_ATTRIBUTES lProcessAttributes=NULL;
+    LPSECURITY_ATTRIBUTES lThreadAttributes=NULL;
+    BOOL lInheritHandles=TRUE; // that's what we want
+    DWORD lCreationFlags=CREATE_NEW_CONSOLE;
+    LPVOID lEnvironment=NULL;
+    LPCTSTR lCurrentDirectory=NULL; // same as main process
+    
+    // start child
+    result=CreateProcess(
+          lApplicationName,lCommandLine,lProcessAttributes,
+          lThreadAttributes,lInheritHandles,lCreationFlags,
+          lEnvironment,lCurrentDirectory,&lChildStartupInfo,
+          &aProcessInformation);
+          
+  }catch(...){
+    delete[] tmpCommand;
+    tmpCommand=0;
+    throw;  
+  }
+        
+  delete[] tmpCommand;
+  tmpCommand=0;
+        
+  return result;
+}
+
+/******************************************
+*  run a process that executes the aCommand
+*  in a new console and reads the output
+*/
+int run_process(
+  const std::string& aCommand,
+  std::ostringstream& aTargetOutStream,
+  std::ostringstream& aTargetErrStream)
+{
+  HANDLE lOutRead, lErrRead, lStdOut, lStdErr;
+  SECURITY_ATTRIBUTES lSecurityAttributes;
+  PROCESS_INFORMATION lChildProcessInfo;
+  DWORD exitCode=0;
+    
+  // prepare security attributes
+  lSecurityAttributes.nLength= sizeof(lSecurityAttributes);
+  lSecurityAttributes.lpSecurityDescriptor = NULL;
+  lSecurityAttributes.bInheritHandle = TRUE;
+
+  // create output pipes
+  if(
+      !CreatePipe(&lOutRead,&lStdOut,&lSecurityAttributes,1024*1024) // std::cout >> lOutRead
+      || !CreatePipe(&lErrRead,&lStdErr,&lSecurityAttributes,1024*1024) // std::cerr >> lErrRead
+    ){
+    Item lQName = ProcessModule::getItemFactory()->createQName(
+      "http://zorba.io/modules/process";, "COMMUNICATION");
+    throw USER_EXCEPTION(lQName,
+      "Couldn't create one of std::cout/std::cerr pipe for child process execution."
+    );
+  };
+  
+  //start child process
+  BOOL ok = create_child_process(lStdOut,lStdErr,aCommand,lChildProcessInfo);
+  if(ok==TRUE)
+  {
+
+    // close unneeded handle  
+    CloseHandle(lChildProcessInfo.hThread);
+    
+    // wait for the process to finish
+    WaitForSingleObject(lChildProcessInfo.hProcess,INFINITE);
+    if (!GetExitCodeProcess(lChildProcessInfo.hProcess, &exitCode))
+    {
+      std::stringstream lErrorMsg;
+      lErrorMsg 
+        << "Couldn't get exit code from child process. Executed command: '" << aCommand << "'.";
+      Item lQName = ProcessModule::getItemFactory()->createQName(
+        "http://zorba.io/modules/process";, "COMMUNICATION");
+      throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
+    }
+  
+    CloseHandle(lChildProcessInfo.hProcess);
+    CloseHandle(lStdOut);
+    CloseHandle(lStdErr);
+
+    // read child's output
+    read_child_output(lOutRead,aTargetOutStream);
+    read_child_output(lErrRead,aTargetErrStream);
+
+    // close 
+    CloseHandle(lOutRead);
+    CloseHandle(lErrRead);
+
+ }else{
+    CloseHandle(lStdOut);
+    CloseHandle(lStdErr);
+    CloseHandle(lOutRead);
+    CloseHandle(lErrRead);
+  
+     // couldn't launch process
+     throw_last_error(__FILE__, __LINE__);
+  };
+  
+  
+  return exitCode;
+}
+
+#else
+
+#define READ  0
+#define WRITE 1
+
+pid_t exec_helper(int *infp, int *outfp, int *errfp, const char *command, char* argv[], char* env[])
+{
+    int p_stdin[2];
+    int p_stdout[2];
+    int p_stderr[2];
+    pid_t pid;
+
+    if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0 || pipe(p_stderr) != 0)
+      return -1;
+
+    pid = fork();
+
+    if (pid < 0)
+      return pid;
+    else if (pid == 0)
+    {
+      close(p_stdin[WRITE]);
+      dup2(p_stdin[READ], 0);   // duplicate stdin
+
+      close(p_stdout[READ]);
+      dup2(p_stdout[WRITE], 1); // duplicate stdout
+
+      close(p_stderr[READ]);
+      dup2(p_stderr[WRITE], 2); // duplicate stderr
+
+      if (command)
+        execl("/bin/sh", "sh", "-c", command, NULL);
+      else if (env == NULL)      
+        execvp(argv[0], argv);
+      else
+        execvpe(argv[0], argv, env);      
+        
+      perror("execl"); // output the result to standard error
+      
+      // TODO:
+      // Currently, if the child process exits with an error, the following happens:
+      // -- exit(errno) is called
+      // -- static object destruction ocurrs
+      // -- Zorba store is destroyed in the child process and this leaks several URIs 
+      //    and prints error messages to stderr. An exception is thrown and this overwrites
+      //    the exit code of the invoked process.
+      //
+      // Until a proper solution is found, the child fork() process will call abort(), which
+      // will not trigger static object destruction.
+            
+      abort();
+      // exit(errno);
+    }
+
+    if (infp == NULL)
+      close(p_stdin[WRITE]);
+    else
+      *infp = p_stdin[WRITE];
+
+    if (outfp == NULL)
+      close(p_stdout[READ]);
+    else
+      *outfp = p_stdout[READ];
+
+    if (errfp == NULL)
+      close(p_stderr[READ]);
+    else
+      *errfp = p_stderr[READ];
+
+    close(p_stdin[READ]);   // We only write to the forks stdin anyway
+    close(p_stdout[WRITE]); // and we only read from its stdout
+    close(p_stderr[WRITE]); // and we only read from its stderr
+        
+    return pid;
+}
+
+#endif
+
+
+/******************************************************************************
+ *****************************************************************************/
+String ExecFunction::getOneStringArgument (const Arguments_t& aArgs, int aPos) const
+{
+  Item lItem;
+  Iterator_t  args_iter = aArgs[aPos]->getIterator();
+  args_iter->open();
+  args_iter->next(lItem);
+  zorba::String lTmpString = lItem.getStringValue();
+  args_iter->close();
+  return lTmpString;
+}
+
+zorba::ItemSequence_t
+ExecFunction::evaluate(
+  const Arguments_t& aArgs,
+  const zorba::StaticContext* aSctx,
+  const zorba::DynamicContext* aDctx) const
+{
+  std::string lCommand;
+  std::vector<std::string> lArgs;
+  std::vector<std::string> lEnv;
+  int exit_code = 0;
+
+  lCommand = getOneStringArgument(aArgs, 0).c_str();
+
+  if (aArgs.size() > 1)
+  {
+    zorba::Item lArg;
+    Iterator_t arg1_iter = aArgs[1]->getIterator();
+    arg1_iter->open();
+    while (arg1_iter->next(lArg))    
+      lArgs.push_back(lArg.getStringValue().c_str());
+    arg1_iter->close();
+  }
+  
+  if (aArgs.size() > 2)
+  {
+    zorba::Item lArg;
+    Iterator_t arg1_iter = aArgs[2]->getIterator();
+    arg1_iter->open();
+    while (arg1_iter->next(lArg))    
+      lEnv.push_back(lArg.getStringValue().c_str());
+    arg1_iter->close();    
+  }
+
+  std::ostringstream lTmp;
+
+#ifdef WIN32
+  // execute process command in a new commandline
+  // with quotes at the beggining and at the end
+  lTmp << "cmd /C \"";
+#endif
+ 
+  lTmp << "\"" << lCommand << "\""; //quoted for spaced paths/filenames
+  size_t pos=0;
+  for (std::vector<std::string>::const_iterator lIter = lArgs.begin();
+       lIter != lArgs.end(); ++lIter)
+  {
+    pos = (*lIter).rfind('\\')+(*lIter).rfind('/');
+    if (int(pos)>=0)
+      lTmp << " \"" << *lIter << "\"";
+    else
+      lTmp << " " << *lIter;
+  }
+#ifdef WIN32
+  lTmp << "\"";   // with quotes at the end for commandline
+#endif
+  
+  std::ostringstream lStdout;
+  std::ostringstream lStderr;
+
+#ifdef WIN32
+  std::string lCommandLineString = lTmp.str();
+  int code = run_process(lCommandLineString, lStdout, lStderr);
+  
+  if (code != 0)
+  {
+    std::stringstream lErrorMsg;
+    lErrorMsg << "Failed to execute the command (" << code << ")";
+    Item lQName = ProcessModule::getItemFactory()->createQName(
+      "http://zorba.io/modules/process";, "COMMUNICATION");
+    throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
+  }
+  exit_code = code;
+
+#else //not WIN32
+
+  int outfp;
+  int errfp;
+  int status;
+  pid_t pid;
+  
+  std::vector<char*> argv(lArgs.size()+2, NULL);
+  std::vector<char*> env(lEnv.size()+1, NULL);
+
+  try
+  {
+    if (theIsExecProgram)
+    {
+      argv[0] = strdup(lCommand.c_str());
+      for (unsigned int i=0; i<lArgs.size(); i++)
+        argv[i+1] = strdup(lArgs[i].c_str()); 
+      
+      for (unsigned int i=0; i<lEnv.size(); i++)
+        env[i] = strdup(lEnv[i].c_str());
+      
+      pid = exec_helper(NULL, &outfp, &errfp, NULL, argv.data(), lEnv.size() ? env.data() : NULL);
+    }
+    else
+    {
+      pid = exec_helper(NULL, &outfp, &errfp, lTmp.str().c_str(), argv.data(), NULL);
+    }
+    
+    if ( pid == -1 )
+    {
+      std::stringstream lErrorMsg;
+      lErrorMsg << "Failed to execute the command (" << pid << ")";
+      Item lQName = ProcessModule::getItemFactory()->createQName(
+            "http://zorba.io/modules/process";, "COMMUNICATION");
+      throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
+      return NULL;
+    }
+  
+    char lBuf[PATH_MAX];
+    ssize_t length = 0;
+    while ( (length=read(outfp, lBuf, PATH_MAX)) > 0 )
+    {
+      lStdout.write(lBuf, length);
+    }
+
+    status = close(outfp);
+
+    while ( (length=read(errfp, lBuf, PATH_MAX)) > 0 )
+    {
+      lStderr.write(lBuf, length);
+    }
+
+    status = close(errfp);
+
+    if ( status < 0 )
+    {
+      std::stringstream lErrorMsg;
+      lErrorMsg << "Failed to close the err stream (" << status << ")";
+      Item lQName = ProcessModule::getItemFactory()->createQName(
+        "http://zorba.io/modules/process";, "COMMUNICATION");
+      throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
+    }
+
+    int  stat = 0;
+    
+    pid_t w = waitpid(pid, &stat, 0);
+    
+    if (w == -1) 
+    { 
+        std::stringstream lErrorMsg;
+        lErrorMsg << "Failed to wait for child process ";
+        Item lQName = ProcessModule::getItemFactory()->createQName(
+          "http://zorba.io/modules/process";, "COMMUNICATION");
+        throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());          
+    }
+
+    if (WIFEXITED(stat)) 
+    {
+        //std::cout << " WEXITSTATUS : " << WEXITSTATUS(stat) << std::endl; std::cout.flush();
+        exit_code = WEXITSTATUS(stat);
+    } 
+    else if (WIFSIGNALED(stat)) 
+    {
+        //std::cout << " WTERMSIG : " << WTERMSIG(stat) << std::endl; std::cout.flush();
+        exit_code = 128 + WTERMSIG(stat);
+    }
+    else if (WIFSTOPPED(stat)) 
+    {
+        //std::cout << " STOPSIG : " << WSTOPSIG(stat) << std::endl; std::cout.flush();
+        exit_code = 128 + WSTOPSIG(stat);
+    }
+    else
+    {
+        //std::cout << " else : " << std::endl; std::cout.flush();
+        exit_code = 255;
+    }
+    
+    //std::cout << " exit_code : " << exit_code << std::endl; std::cout.flush();
+    free_char_vector(argv);
+    free_char_vector(env);
+  }
+  catch (...)
+  {
+    free_char_vector(argv);
+    free_char_vector(env);
+    throw;
+  }
+#endif // WIN32
+
+  zorba::Item lResult;
+  create_result_object(lResult, lStdout.str(), lStderr.str(), exit_code,
+                       theModule->getItemFactory());  
+  return zorba::ItemSequence_t(new zorba::SingletonItemSequence(lResult));
+}
+
+
+/******************************************************************************
+ *****************************************************************************/
+ProcessModule::~ProcessModule()
+{
+  for (FuncMap_t::const_iterator lIter = theFunctions.begin();
+       lIter != theFunctions.end(); ++lIter) {
+    delete lIter->second;
+  }
+  theFunctions.clear();
+}
+
+zorba::ExternalFunction*
+ProcessModule::getExternalFunction(const zorba::String& aLocalname)
+{
+  FuncMap_t::const_iterator lFind = theFunctions.find(aLocalname);
+  zorba::ExternalFunction*& lFunc = theFunctions[aLocalname];
+  if (lFind == theFunctions.end())
+  {
+    if (aLocalname.compare("exec-command") == 0)
+    {
+      lFunc = new ExecFunction(this);
+    }
+    else if (aLocalname.compare("exec") == 0)
+    {
+      lFunc = new ExecFunction(this, true);
+    }
+  }
+  return lFunc;
+}
+
+void ProcessModule::destroy()
+{
+  if (!dynamic_cast<ProcessModule*>(this)) {
+    return;
+  }
+  delete this;
+}
+
+ItemFactory* ProcessModule::theFactory = 0;
+
+} /* namespace processmodule */
+} /* namespace zorba */
+
+#ifdef WIN32
+#  define DLL_EXPORT __declspec(dllexport)
+#else
+#  define DLL_EXPORT __attribute__ ((visibility("default")))
+#endif
+
+extern "C" DLL_EXPORT zorba::ExternalModule* createModule() {
+  return new zorba::processmodule::ProcessModule();
+}
+/* vim:set et sw=2 ts=2: */

=== added file 'src/process-2.xq.src/process.h'
--- src/process-2.xq.src/process.h	1970-01-01 00:00:00 +0000
+++ src/process-2.xq.src/process.h	2013-06-27 14:44:39 +0000
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2006-2013 The FLWOR Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef ZORBA_PROCESSMODULE_PROCESS_H
+#define ZORBA_PROCESSMODULE_PROCESS_H
+
+#include <map>
+
+#include <zorba/zorba.h>
+#include <zorba/error.h>
+#include <zorba/external_module.h>
+#include <zorba/function.h>
+
+namespace zorba {
+namespace processmodule {
+
+/******************************************************************************
+ *****************************************************************************/
+class ProcessModule : public ExternalModule
+{
+private:
+  static ItemFactory* theFactory;
+
+protected:
+  class ltstr
+  {
+  public:
+    bool operator()(const String& s1, const String& s2) const
+    {
+      return s1.compare(s2) < 0;
+    }
+  };
+
+  typedef std::map<String, ExternalFunction*, ltstr> FuncMap_t;
+
+  FuncMap_t theFunctions;
+
+public:
+  virtual ~ProcessModule();
+
+  virtual zorba::String getURI() const
+  { return "http://zorba.io/modules/process";; }
+
+  virtual zorba::ExternalFunction*
+  getExternalFunction(const zorba::String& aLocalname);
+
+  virtual void destroy();
+
+  static ItemFactory* getItemFactory()
+  {
+    if(!theFactory)
+    {
+      theFactory = Zorba::getInstance(0)->getItemFactory();
+    }
+
+    return theFactory;
+  }
+};
+
+/******************************************************************************
+ *****************************************************************************/
+class ExecFunction : public ContextualExternalFunction
+{
+public:
+  ExecFunction(const ProcessModule* aModule, bool aExecProgram = false) 
+    : theModule(aModule), theIsExecProgram(aExecProgram) {}
+
+  virtual ~ExecFunction() {}
+
+  virtual zorba::String
+  getLocalName() const { if (theIsExecProgram) return "exec"; else return "exec-command"; }
+
+  virtual zorba::ItemSequence_t
+  evaluate(const Arguments_t&,
+           const zorba::StaticContext*,
+           const zorba::DynamicContext*) const;
+
+  virtual String getURI() const
+  {
+    return theModule->getURI();
+  }
+
+protected:
+  const ProcessModule* theModule;
+  
+  bool theIsExecProgram; // if set to true, will use the execvpe() version of the system function
+                         // if set to false, will build a command string and pass it to 
+                         // either "bash" or "cmd.exe" (through execl() on Linux)
+
+  String getOneStringArgument (const Arguments_t& aArgs, int index) const;
+};
+
+} /* namespace processmodule */
+} /* namespace zorba */
+
+#endif // ZORBA_PROCESSMODULE_PROCESS_H

=== added directory 'test'
=== renamed directory 'test' => 'test.moved'
=== added directory 'test/ExpQueryResults'
=== added file 'test/ExpQueryResults/process1-01.xml.res'
--- test/ExpQueryResults/process1-01.xml.res	1970-01-01 00:00:00 +0000
+++ test/ExpQueryResults/process1-01.xml.res	2013-06-27 14:44:39 +0000
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<result><out>hello world</out><err>Ooops. an error.</err></result>

=== added file 'test/ExpQueryResults/process2-01.xml.res'
--- test/ExpQueryResults/process2-01.xml.res	1970-01-01 00:00:00 +0000
+++ test/ExpQueryResults/process2-01.xml.res	2013-06-27 14:44:39 +0000
@@ -0,0 +1,1 @@
+true
\ No newline at end of file

=== added file 'test/ExpQueryResults/process2-02.xml.res'
--- test/ExpQueryResults/process2-02.xml.res	1970-01-01 00:00:00 +0000
+++ test/ExpQueryResults/process2-02.xml.res	2013-06-27 14:44:39 +0000
@@ -0,0 +1,1 @@
+true
\ No newline at end of file

=== added file 'test/ExpQueryResults/process2-03.xml.res'
--- test/ExpQueryResults/process2-03.xml.res	1970-01-01 00:00:00 +0000
+++ test/ExpQueryResults/process2-03.xml.res	2013-06-27 14:44:39 +0000
@@ -0,0 +1,1 @@
+true
\ No newline at end of file

=== added file 'test/ExpQueryResults/process2-04.xml.res'
--- test/ExpQueryResults/process2-04.xml.res	1970-01-01 00:00:00 +0000
+++ test/ExpQueryResults/process2-04.xml.res	2013-06-27 14:44:39 +0000
@@ -0,0 +1,1 @@
+true
\ No newline at end of file

=== added file 'test/ExpQueryResults/process2-05.xml.res'
--- test/ExpQueryResults/process2-05.xml.res	1970-01-01 00:00:00 +0000
+++ test/ExpQueryResults/process2-05.xml.res	2013-06-27 14:44:39 +0000
@@ -0,0 +1,1 @@
+true
\ No newline at end of file

=== added file 'test/ExpQueryResults/process2-06.xml.res'
--- test/ExpQueryResults/process2-06.xml.res	1970-01-01 00:00:00 +0000
+++ test/ExpQueryResults/process2-06.xml.res	2013-06-27 14:44:39 +0000
@@ -0,0 +1,1 @@
+true
\ No newline at end of file

=== added file 'test/ExpQueryResults/process2-07.xml.res'
--- test/ExpQueryResults/process2-07.xml.res	1970-01-01 00:00:00 +0000
+++ test/ExpQueryResults/process2-07.xml.res	2013-06-27 14:44:39 +0000
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<result><out>hello world</out><err>Ooops. an error.</err></result>

=== added directory 'test/Queries'
=== added file 'test/Queries/process1-01.xq'
--- test/Queries/process1-01.xq	1970-01-01 00:00:00 +0000
+++ test/Queries/process1-01.xq	2013-06-27 14:44:39 +0000
@@ -0,0 +1,24 @@
+import module namespace proc = "http://www.zorba-xquery.com/modules/process#1.0";; 
+
+
+{
+  variable $stdOutTest := proc:exec("echo","hello world") ;
+  variable $stdErrTest := proc:exec("echo","Ooops. an error. 1>&amp;2");
+  variable $stdOutWinTest := proc:exec("cmd", ("/c", "echo","hello world")) ;
+  variable $stdErrWinTest := proc:exec("cmd", ("/c", "echo","Ooops. an error. 1>&amp;2"));
+  
+  let $result :=
+    <result>
+      <out>{normalize-space(data($stdOutTest/proc:stdout))}</out>
+      <err>{normalize-space(data($stdErrTest/proc:stderr))}</err>
+    </result>
+  return 
+    if (contains($result/err/text(),"is not recognized as an internal or external command"))
+    then
+      <result>
+        <out>{normalize-space(data($stdOutWinTest/proc:stdout))}</out>
+        <err>{normalize-space(data($stdErrWinTest/proc:stderr))}</err>
+      </result>
+    else
+      $result
+}

=== added file 'test/Queries/process2-01.xq'
--- test/Queries/process2-01.xq	1970-01-01 00:00:00 +0000
+++ test/Queries/process2-01.xq	2013-06-27 14:44:39 +0000
@@ -0,0 +1,5 @@
+import module namespace proc = "http://zorba.io/modules/process";;
+
+let $result := proc:exec("echo")
+return $result("stdout") eq "
+"

=== added file 'test/Queries/process2-02.xq'
--- test/Queries/process2-02.xq	1970-01-01 00:00:00 +0000
+++ test/Queries/process2-02.xq	2013-06-27 14:44:39 +0000
@@ -0,0 +1,5 @@
+import module namespace proc = "http://zorba.io/modules/process";;
+
+let $result := proc:exec("echo",("hello","world"))
+return $result("stdout") eq "hello world
+"

=== added file 'test/Queries/process2-03.xq'
--- test/Queries/process2-03.xq	1970-01-01 00:00:00 +0000
+++ test/Queries/process2-03.xq	2013-06-27 14:44:39 +0000
@@ -0,0 +1,5 @@
+import module namespace proc = "http://zorba.io/modules/process";;
+
+let $result := proc:exec("printenv",("TEST_ENV_VAR"),"TEST_ENV_VAR=foo")
+return $result("stdout") eq "foo
+"

=== added file 'test/Queries/process2-04.xq'
--- test/Queries/process2-04.xq	1970-01-01 00:00:00 +0000
+++ test/Queries/process2-04.xq	2013-06-27 14:44:39 +0000
@@ -0,0 +1,6 @@
+import module namespace proc = "http://zorba.io/modules/process";;
+
+let $result := proc:exec("printenv",("TEST_ENV_VAR","VAR2"),("TEST_ENV_VAR=foo","VAR2=bar"))
+return $result("stdout") eq "foo
+bar
+"

=== added file 'test/Queries/process2-05.xq'
--- test/Queries/process2-05.xq	1970-01-01 00:00:00 +0000
+++ test/Queries/process2-05.xq	2013-06-27 14:44:39 +0000
@@ -0,0 +1,5 @@
+import module namespace proc = "http://zorba.io/modules/process";;
+
+let $result := proc:exec("echo","{}[]()()''~!@#$%^&amp;*_-+|<>/?,.")
+return $result("stdout") eq "{}[]()()''~!@#$%^&amp;*_-+|<>/?,.
+"

=== added file 'test/Queries/process2-06.xq'
--- test/Queries/process2-06.xq	1970-01-01 00:00:00 +0000
+++ test/Queries/process2-06.xq	2013-06-27 14:44:39 +0000
@@ -0,0 +1,4 @@
+import module namespace proc = "http://zorba.io/modules/process";;
+
+let $result := proc:exec("this_executable_does_not_exist")
+return $result("exit-code") ne 0

=== added file 'test/Queries/process2-07.xq'
--- test/Queries/process2-07.xq	1970-01-01 00:00:00 +0000
+++ test/Queries/process2-07.xq	2013-06-27 14:44:39 +0000
@@ -0,0 +1,24 @@
+import module namespace proc = "http://zorba.io/modules/process";;
+
+
+{
+  variable $stdOutTest := proc:exec-command("echo","hello world") ;
+  variable $stdErrTest := proc:exec-command("echo","Ooops. an error. 1>&amp;2");
+  variable $stdOutWinTest := proc:exec-command("cmd", ("/c", "echo","hello world")) ;
+  variable $stdErrWinTest := proc:exec-command("cmd", ("/c", "echo","Ooops. an error. 1>&amp;2"));
+  
+  let $result :=
+    <result>
+      <out>{normalize-space(data($stdOutTest("stdout")))}</out>
+      <err>{normalize-space(data($stdErrTest("stderr")))}</err>
+    </result>
+  return 
+    if (contains($result/err/text(),"is not recognized as an internal or external command"))
+    then
+      <result>
+        <out>{normalize-space(data($stdOutWinTest("stdout")))}</out>
+        <err>{normalize-space(data($stdErrWinTest("stderr")))}</err>
+      </result>
+    else
+      $result
+}


Follow ups