Merge lp:~zorba-coders/zorba/process-2 into lp:zorba/process-module

Proposed by Nicolae Brinza
Status: Merged
Approved by: Nicolae Brinza
Approved revision: 50
Merged at revision: 35
Proposed branch: lp:~zorba-coders/zorba/process-2
Merge into: lp:zorba/process-module
Diff against target: 1667 lines (+1131/-177)
25 files modified
src/CMakeLists.txt (+3/-2)
src/com/CMakeLists.txt (+0/-14)
src/com/zorba-xquery/CMakeLists.txt (+0/-14)
src/com/zorba-xquery/www/CMakeLists.txt (+0/-14)
src/process-1.xq (+95/-0)
src/process-1.xq.src/process.cpp (+577/-0)
src/process-1.xq.src/process.h (+105/-0)
src/process-2.xq (+149/-45)
src/process-2.xq.src/process.cpp (+122/-73)
src/process-2.xq.src/process.h (+9/-6)
test/ExpQueryResults/process1-01.xml.res (+2/-0)
test/ExpQueryResults/process2-01.xml.res (+1/-0)
test/ExpQueryResults/process2-02.xml.res (+1/-0)
test/ExpQueryResults/process2-03.xml.res (+1/-0)
test/ExpQueryResults/process2-04.xml.res (+1/-0)
test/ExpQueryResults/process2-05.xml.res (+1/-0)
test/ExpQueryResults/process2-06.xml.res (+1/-0)
test/Queries/process1-01.xq (+24/-0)
test/Queries/process2-01.xq (+5/-0)
test/Queries/process2-02.xq (+5/-0)
test/Queries/process2-03.xq (+5/-0)
test/Queries/process2-04.xq (+6/-0)
test/Queries/process2-05.xq (+5/-0)
test/Queries/process2-06.xq (+4/-0)
test/Queries/process2-07.xq (+9/-9)
To merge this branch: bzr merge lp:~zorba-coders/zorba/process-2
Reviewer Review Type Date Requested Status
Nicolae Brinza Approve
Matthias Brantner Approve
Review via email: mp+164415@code.launchpad.net

Commit message

Version 2.0 of the process module, allows running executables directly, without invoking bash/cmd.exe

Description of the change

Version 2.0 of the process module, allows running executables directly, without invoking bash/cmd.exe

To post a comment you must log in.
Revision history for this message
Nicolae Brinza (nbrinza) :
review: Approve
Revision history for this message
Zorba Build Bot (zorba-buildbot) wrote :
Revision history for this message
Zorba Build Bot (zorba-buildbot) wrote :

The attempt to merge lp:~zorba-coders/zorba/process-2 into lp:zorba/process-module failed. Below is the output from the failed tests.

CMake Error at /home/ceej/zo/testing/zorbatest/tester/TarmacLander.cmake:275 (message):
  Validation queue job process-2-2013-05-17T14-50-41.743Z is finished. The
  final status was:

  1 tests did not succeed - changes not commited.

Error in read script: /home/ceej/zo/testing/zorbatest/tester/TarmacLander.cmake

Revision history for this message
Zorba Build Bot (zorba-buildbot) wrote :
Revision history for this message
Zorba Build Bot (zorba-buildbot) wrote :

Validation queue job process-2-2013-05-17T15-29-41.901Z is finished. The final status was:

All tests succeeded!

Revision history for this message
Zorba Build Bot (zorba-buildbot) wrote :

Voting does not meet specified criteria. Required: Approve > 1, Disapprove < 1, Needs Fixing < 1, Pending < 1, Needs Information < 1, Resubmit < 1. Got: 1 Approve, 1 Pending.

Revision history for this message
Matthias Brantner (matthias-brantner) wrote :

- I like the signatures and the documentation

- The two modules should both live in the same branch process (subdirectories process/v1 and process/v2) for the versioning to work the v2 one needs to be added first and then the v1 one. If we don't want to change all of our infrastructure immediately, we need to have both versions installed.

- update license header to 2013

- create_result_node(...) in process.cpp is not used anymore

- such as "sh" on Linux or "cmd.exe" on Windows. => such as "sh" on Unix or "cmd.exe" on Windows.

- import module namespace process = "http://www.zorba-xquery.com/modules/process";

process:exec("export", "", "foo=bar")
  gives me

{
  "exit-code" : 139,
  "stdout" : "",
  "stderr" : "execl: No such file or directory\nID: 80 Referenced URI: http://www.zorba-xquery.com/modules/process\nID: 94 Referenced URI: http://www.zorba-xquery.com/options/versioning\n"
}

- the referenced uri message seems to be related to a memory leak

review: Needs Fixing
Revision history for this message
Nicolae Brinza (nbrinza) wrote :

Regarding the referenced URI messages -- these are indeed coming from the StringPool as leaked entries. When the main process is fork()'ed, the child process is basically a copy of the parent process with all the statically allocated objects duplicated. When the child process calls exit(), those objects are destroyed.

Since Zorba doesn't go through a normal shutdown, the StringPool still contains those entries and it prints the error messages and even throws an error. As a result the exit code of the process that was executed is actually lost. Even if the StringPool errors are bypassed somehow, another error is raised in the Lock() class.

This problem was present in 1.0 version as well. The root of the problem is the bad design of engine and store instantiation: they are static singletons which require explicit calls to shutdown.

Some possible solutions are:
1) Call abort() instead of exit() -- this will bypass all the static object destruction but I will need to find a way to pass the exit code to the parent process.

2) Pass the engine and store pointers to the process module and down to the child process so that it can call shutdown on them. This has the added problem of shutting down the store twice -- it is not an issue for an in-memory store, but could be for other stores.

Revision history for this message
Matthias Brantner (matthias-brantner) wrote :

Let's not worry about the leaks for now. Instead, we should simply disable the debug output printing the referenced uris.

I think we should bring the init/shutdown problem to the Zorba team. This has been bothering us and others for quite some time. Zorba 3.0 is the only time to fix this.

Could you please fix the other comments (especially, the v2 issue)?

Revision history for this message
Zorba Build Bot (zorba-buildbot) wrote :
Revision history for this message
Nicolae Brinza (nbrinza) wrote :

I've fixed all the issues except for the leaks problem. I have not removed the printing of the URIs because it would disable them for the entire Zorba engine. Even if the messages are removed there is still the problem of the exit code, which cannot be fixed easily now.

--

Revision history for this message
Zorba Build Bot (zorba-buildbot) wrote :

Validation queue job process-2-2013-05-27T14-21-35.131Z is finished. The final status was:

All tests succeeded!

Revision history for this message
Zorba Build Bot (zorba-buildbot) wrote :

Voting does not meet specified criteria. Required: Approve > 1, Disapprove < 1, Needs Fixing < 1, Pending < 1, Needs Information < 1, Resubmit < 1. Got: 1 Approve, 1 Needs Fixing.

Revision history for this message
Matthias Brantner (matthias-brantner) wrote :

Looks good now. Only one minor thing:

PATH=foo ./bin/zorba -q 'import module namespace p = "http://www.zorba-xquery.com/modules/process"; p:exec("ls")'

I get { "exit-code" : 139, "stdout" : "", "stderr" : "execl: No such file or directory\nID: 80 Referenced URI: http://www.zorba-xquery.com/modules/process\nID: 94 Referenced URI: http://www.zorba-xquery.com/options/versioning\n" }

still showing the referenced uris. The same happens for

./bin/zorba -q 'import module namespace p = "http://www.zorba-xquery.com/modules/process"; p:exec("ls", "-l", "PATH=foo")'

review: Needs Fixing
Revision history for this message
Nicolae Brinza (nbrinza) wrote :

Matthias,

They URI messages won't appear anymore, but I've done it by calling abort() instead of exit() in the child fork() in the process module, because:

-- it will keep the Zorba URIs leaks messages in the Release build
-- the exit code in the case of an error is lost anyway due to the thrown exception in Zorba
-- it is not actually correct to deallocate the Zorba store twice (once in the child and the second time in the parent). It could lead to problems if store is not an in-memory store.

I've added a lengthy comment in the code describing what happens and where the problem lies. If the error exit code could be sent to the main process (e.g. through another opened pipe) then it would work perfectly.

Revision history for this message
Matthias Brantner (matthias-brantner) wrote :

- I didn't see the changes you described in the last comment. Did you not push them?
- Why does the module use XQuery instead of JSONiq? I think it should use JSONiq and not use prefixes for types etc.
- we might have to change the namespace to zorba.io
- comments should use <p> to separate paragraphs instead of newlines.

review: Needs Fixing
Revision history for this message
Nicolae Brinza (nbrinza) wrote :

For some reason, the commits to my checked-out branch are not pushed automatically, and I forgot to do it manually. It is pushed now.

I've also fixed all issues you reported except for the namespace change to "zorba.io". As far as I understood, it will be done for all modules at once -- it is better than changing the namespace for one module at random, so I think it can be merged now with the current namespace.

Revision history for this message
Matthias Brantner (matthias-brantner) wrote :

I made some final changes, i.e. moved the files and improved the documentation. Concerning me, it's ready to go.

review: Approve
Revision history for this message
Zorba Build Bot (zorba-buildbot) wrote :
Revision history for this message
Zorba Build Bot (zorba-buildbot) wrote :

The attempt to merge lp:~zorba-coders/zorba/process-2 into lp:zorba/process-module failed. Below is the output from the failed tests.

CMake Error at /home/ceej/zo/testing/zorbatest/tester/TarmacLander.cmake:281 (message):
  Validation queue job process-2-2013-06-13T10-31-56.151Z is finished. The
  final status was:

  No tests were run - build or configure step must have failed.

  Not commiting changes.

Error in read script: /home/ceej/zo/testing/zorbatest/tester/TarmacLander.cmake

Revision history for this message
Chris Hillery (ceejatec) wrote :

FYI, you're going to need to merge changes from lp:zorba/process-module in order for the build to succeed. Paul's been moving #include files around on the Zorba trunk which affects many non-core modules.

lp:~zorba-coders/zorba/process-2 updated
50. By Nicolae Brinza

Removed unecessary includes

Revision history for this message
Nicolae Brinza (nbrinza) :
review: Approve
Revision history for this message
Nicolae Brinza (nbrinza) wrote :

I've merged Paul's change -- it was only a one line change.

Revision history for this message
Zorba Build Bot (zorba-buildbot) wrote :
Revision history for this message
Zorba Build Bot (zorba-buildbot) wrote :

Validation queue job process-2-2013-06-13T11-03-45.577Z is finished. The final status was:

All tests succeeded!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== removed file 'src/CMakeLists.txt'
2--- src/CMakeLists.txt 2011-07-26 10:43:43 +0000
3+++ src/CMakeLists.txt 1970-01-01 00:00:00 +0000
4@@ -1,19 +0,0 @@
5-# Copyright 2006-2008 The FLWOR Foundation.
6-#
7-# Licensed under the Apache License, Version 2.0 (the "License");
8-# you may not use this file except in compliance with the License.
9-# You may obtain a copy of the License at
10-#
11-# http://www.apache.org/licenses/LICENSE-2.0
12-#
13-# Unless required by applicable law or agreed to in writing, software
14-# distributed under the License is distributed on an "AS IS" BASIS,
15-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16-# See the License for the specific language governing permissions and
17-# limitations under the License.
18-
19-# all external module libraries are generated in the directory
20-# of the corresponding .xq file
21-MESSAGE(STATUS "Add com")
22-ADD_SUBDIRECTORY(com)
23-MESSAGE(STATUS "End modules")
24
25=== renamed file 'src/com/zorba-xquery/www/modules/CMakeLists.txt' => 'src/CMakeLists.txt'
26--- src/com/zorba-xquery/www/modules/CMakeLists.txt 2011-07-01 09:24:09 +0000
27+++ src/CMakeLists.txt 2013-06-13 10:54:38 +0000
28@@ -1,4 +1,4 @@
29-# Copyright 2006-2008 The FLWOR Foundation.
30+# Copyright 2006-2013 The FLWOR Foundation.
31 #
32 # Licensed under the Apache License, Version 2.0 (the "License");
33 # you may not use this file except in compliance with the License.
34@@ -12,4 +12,5 @@
35 # See the License for the specific language governing permissions and
36 # limitations under the License.
37
38-DECLARE_ZORBA_MODULE (URI "http://www.zorba-xquery.com/modules/process" VERSION 1.0 FILE "process.xq")
39+DECLARE_ZORBA_MODULE (URI "http://zorba.io/modules/process" VERSION 1.0 FILE "process-2.xq")
40+DECLARE_ZORBA_MODULE (URI "http://www.zorba-xquery.com/modules/process" VERSION 1.0 FILE "process-1.xq")
41
42=== removed directory 'src/com'
43=== removed file 'src/com/CMakeLists.txt'
44--- src/com/CMakeLists.txt 2011-10-06 08:19:19 +0000
45+++ src/com/CMakeLists.txt 1970-01-01 00:00:00 +0000
46@@ -1,14 +0,0 @@
47-# Copyright 2006-2008 The FLWOR Foundation.
48-#
49-# Licensed under the Apache License, Version 2.0 (the "License");
50-# you may not use this file except in compliance with the License.
51-# You may obtain a copy of the License at
52-#
53-# http://www.apache.org/licenses/LICENSE-2.0
54-#
55-# Unless required by applicable law or agreed to in writing, software
56-# distributed under the License is distributed on an "AS IS" BASIS,
57-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
58-# See the License for the specific language governing permissions and
59-# limitations under the License.
60-ADD_SUBDIRECTORY(zorba-xquery)
61
62=== removed directory 'src/com/zorba-xquery'
63=== removed file 'src/com/zorba-xquery/CMakeLists.txt'
64--- src/com/zorba-xquery/CMakeLists.txt 2011-10-06 08:19:19 +0000
65+++ src/com/zorba-xquery/CMakeLists.txt 1970-01-01 00:00:00 +0000
66@@ -1,14 +0,0 @@
67-# Copyright 2006-2008 The FLWOR Foundation.
68-#
69-# Licensed under the Apache License, Version 2.0 (the "License");
70-# you may not use this file except in compliance with the License.
71-# You may obtain a copy of the License at
72-#
73-# http://www.apache.org/licenses/LICENSE-2.0
74-#
75-# Unless required by applicable law or agreed to in writing, software
76-# distributed under the License is distributed on an "AS IS" BASIS,
77-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
78-# See the License for the specific language governing permissions and
79-# limitations under the License.
80-ADD_SUBDIRECTORY(www)
81
82=== removed directory 'src/com/zorba-xquery/www'
83=== removed file 'src/com/zorba-xquery/www/CMakeLists.txt'
84--- src/com/zorba-xquery/www/CMakeLists.txt 2011-10-06 08:19:19 +0000
85+++ src/com/zorba-xquery/www/CMakeLists.txt 1970-01-01 00:00:00 +0000
86@@ -1,14 +0,0 @@
87-# Copyright 2006-2008 The FLWOR Foundation.
88-#
89-# Licensed under the Apache License, Version 2.0 (the "License");
90-# you may not use this file except in compliance with the License.
91-# You may obtain a copy of the License at
92-#
93-# http://www.apache.org/licenses/LICENSE-2.0
94-#
95-# Unless required by applicable law or agreed to in writing, software
96-# distributed under the License is distributed on an "AS IS" BASIS,
97-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
98-# See the License for the specific language governing permissions and
99-# limitations under the License.
100-ADD_SUBDIRECTORY(modules)
101
102=== removed directory 'src/com/zorba-xquery/www/modules'
103=== added file 'src/process-1.xq'
104--- src/process-1.xq 1970-01-01 00:00:00 +0000
105+++ src/process-1.xq 2013-06-13 10:54:38 +0000
106@@ -0,0 +1,95 @@
107+xquery version "3.0";
108+
109+(:
110+ : Copyright 2006-2013 The FLWOR Foundation.
111+ :
112+ : Licensed under the Apache License, Version 2.0 (the "License");
113+ : you may not use this file except in compliance with the License.
114+ : You may obtain a copy of the License at
115+ :
116+ : http://www.apache.org/licenses/LICENSE-2.0
117+ :
118+ : Unless required by applicable law or agreed to in writing, software
119+ : distributed under the License is distributed on an "AS IS" BASIS,
120+ : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
121+ : See the License for the specific language governing permissions and
122+ : limitations under the License.
123+:)
124+
125+(:~
126+ : This module provides functions to create a native process and return the result
127+ : (i.e. exit code, result on standard out and error).
128+ : <p>
129+ : Example:
130+ :<pre class="ace-static" ace-mode="xquery">
131+ : import module namespace proc = "http://www.zorba-xquery.com/modules/process";
132+ : proc:exec("ls")
133+ :</pre>
134+ : </p>
135+ : <p>
136+ : Potential result:
137+ : <pre class="ace-static" ace-mode="xquery"><![CDATA[
138+ : <result xmlns="http://www.zorba-xquery.com/modules/process">
139+ : <stdout>myfile.txt</stout>
140+ : <stderr/>
141+ : <exit-code>0</exit-code>
142+ : </result>
143+ : ]]></pre>
144+ : </p>
145+ :
146+ : @author Cezar Andrei
147+ : @project Zorba/IO/Process
148+ :
149+ :)
150+module namespace process = "http://www.zorba-xquery.com/modules/process";
151+
152+declare namespace an = "http://www.zorba-xquery.com/annotations";
153+
154+declare namespace ver = "http://www.zorba-xquery.com/options/versioning";
155+declare option ver:module-version "1.0";
156+
157+(:~
158+ : Executes the specified string command in a separate process.
159+ : This function does not allow arguments to be passed to
160+ : the command.
161+ :
162+ : @param $cmd command to be executed (without arguments)
163+ :
164+ : @return the result of the execution as an element as
165+ : shown in the documentation of this module. The exit-code
166+ : element returns the exit code of the child process.
167+ : For POSIX compliant platforms: returns the process exit code. If process is
168+ : terminated or stopped: 128 + termination signal code.
169+ : For Windows platforms: returns the return value of the process or the exit
170+ : or terminate process specified value.
171+ :
172+ : @error process:PROC01 if an error occurred while communicating
173+ : with the executed process.
174+ :)
175+declare %an:sequential function process:exec(
176+ $cmd as xs:string
177+) as element(process:result) external;
178+
179+(:~
180+ : Executes the specified string command in a separate process.
181+ : Each of the strings in the sequence passed in as the second
182+ : argument is passed as an argument to the executed command.
183+ :
184+ : @param $cmd command to be executed (without arguments)
185+ : @param $args the arguments passed to the executed command (e.g. "-la")
186+ :
187+ : @return the result of the execution as an element as
188+ : shown in the documentation of this module. The exit-code
189+ : element returns the exit code of the child process.
190+ : For POSIX compliant platforms: returns the process exit code. If process is
191+ : terminated or stopped: 128 + termination signal code.
192+ : For Windows platforms: returns the return value of the process or the exit
193+ : or terminate process specified value.
194+ :
195+ : @error process:PROC01 if an error occurred while communicating
196+ : with the executed process.
197+ :)
198+declare %an:sequential function process:exec(
199+ $cmd as xs:string,
200+ $args as xs:string*
201+) as element(process:result) external;
202
203=== added directory 'src/process-1.xq.src'
204=== added file 'src/process-1.xq.src/process.cpp'
205--- src/process-1.xq.src/process.cpp 1970-01-01 00:00:00 +0000
206+++ src/process-1.xq.src/process.cpp 2013-06-13 10:54:38 +0000
207@@ -0,0 +1,577 @@
208+/*
209+ * Copyright 2006-2008 The FLWOR Foundation.
210+ *
211+ * Licensed under the Apache License, Version 2.0 (the "License");
212+ * you may not use this file except in compliance with the License.
213+ * You may obtain a copy of the License at
214+ *
215+ * http://www.apache.org/licenses/LICENSE-2.0
216+ *
217+ * Unless required by applicable law or agreed to in writing, software
218+ * distributed under the License is distributed on an "AS IS" BASIS,
219+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
220+ * See the License for the specific language governing permissions and
221+ * limitations under the License.
222+ */
223+
224+#include <sstream>
225+#include <stdlib.h>
226+#include <stdio.h>
227+#include <errno.h>
228+#include <vector>
229+#include <iostream>
230+#include <limits.h>
231+#include <algorithm>
232+
233+#ifdef WIN32
234+# include <windows.h>
235+
236+# ifndef NDEBUG
237+# define _CRTDBG_MAP_ALLOC
238+# include <stdlib.h>
239+# include <crtdbg.h>
240+# endif
241+#else
242+# include <unistd.h>
243+# ifdef __APPLE__
244+# include <sys/wait.h>
245+# else
246+# include <wait.h>
247+# endif
248+#endif
249+
250+#include <zorba/item_factory.h>
251+#include <zorba/singleton_item_sequence.h>
252+#include <zorba/diagnostic_list.h>
253+#include <zorba/user_exception.h>
254+#include <zorba/empty_sequence.h>
255+
256+#include "process.h"
257+
258+namespace zorba {
259+namespace processmodule {
260+
261+/******************************************************************************
262+ *****************************************************************************/
263+void create_result_node(
264+ zorba::Item& aResult,
265+ const std::string& aStandardOut,
266+ const std::string& aErrorOut,
267+ int aExitCode,
268+ zorba::ItemFactory* aFactory)
269+{
270+ zorba::Item lResultQName =
271+ aFactory->createQName("http://www.zorba-xquery.com/modules/process", "result");
272+ zorba::Item lExitCodeQName =
273+ aFactory->createQName("http://www.zorba-xquery.com/modules/process", "exit-code");
274+ zorba::Item lOutputQName =
275+ aFactory->createQName("http://www.zorba-xquery.com/modules/process", "stdout");
276+ zorba::Item lErrorQName =
277+ aFactory->createQName("http://www.zorba-xquery.com/modules/process", "stderr");
278+ zorba::Item lNullItem;
279+ zorba::Item lTypeName =
280+ aFactory->createQName("http://www.w3.org/2001/XMLSchema", "untyped");
281+
282+ zorba::NsBindings lNSBindings;
283+
284+ // root node called result
285+ aResult = aFactory->createElementNode(
286+ lNullItem, lResultQName, lTypeName, false, false, lNSBindings);
287+
288+ // <result><output> aStandardOut </output></result>
289+ zorba::Item lOutput;
290+ lOutput = aFactory->createElementNode(
291+ aResult, lOutputQName, lTypeName, true, false, lNSBindings);
292+ aFactory->createTextNode(lOutput, aStandardOut);
293+
294+ // <result><error> aErrorOut </error></result>
295+ zorba::Item lError;
296+ lError = aFactory->createElementNode(
297+ aResult, lErrorQName, lTypeName, true, false, lNSBindings);
298+ aFactory->createTextNode(lError, aErrorOut);
299+
300+ // <result><exit-code> aExitCode </exit-code></result>
301+ zorba::Item lExitCode;
302+ lExitCode = aFactory->createElementNode(
303+ aResult, lExitCodeQName, lTypeName, true, false, lNSBindings);
304+ std::ostringstream lExitCodeString;
305+ lExitCodeString << aExitCode;
306+ aFactory->createTextNode(lExitCode, lExitCodeString.str());
307+}
308+
309+#ifdef WIN32
310+
311+/***********************************************
312+* throw a descriptive message of the last error
313+* accessible with GetLastError() on windows
314+*/
315+void throw_last_error(const zorba::String& aFilename, unsigned int aLineNumber){
316+ LPVOID lpvMessageBuffer;
317+ TCHAR lErrorBuffer[512];
318+ FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
319+ NULL, GetLastError(),
320+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
321+ (LPTSTR)&lpvMessageBuffer, 0, NULL);
322+ wsprintf(lErrorBuffer,TEXT("Process Error Code: %d - Message= %s"),GetLastError(), (TCHAR *)lpvMessageBuffer);
323+ LocalFree(lpvMessageBuffer);
324+ Item lQName = ProcessModule::getItemFactory()->createQName(
325+ "http://www.zorba-xquery.com/modules/process",
326+ "PROC01");
327+#ifdef UNICODE
328+ char error_str[1024];
329+ WideCharToMultiByte(CP_UTF8, 0, lErrorBuffer, -1, error_str, sizeof(error_str), NULL, NULL);
330+ throw USER_EXCEPTION(lQName, error_str);
331+#else
332+ throw USER_EXCEPTION(lQName, lErrorBuffer);
333+#endif
334+}
335+
336+/******************************************
337+* read output from child process on windows
338+*/
339+void read_child_output(HANDLE aOutputPipe, std::ostringstream& aTargetStream)
340+{
341+ CHAR lBuffer[256];
342+ DWORD lBytesRead;
343+
344+ while(TRUE)
345+ {
346+ if (
347+ !ReadFile(aOutputPipe,lBuffer,sizeof(lBuffer),&lBytesRead,NULL)
348+ || !lBytesRead
349+ )
350+ {
351+ if (GetLastError() == ERROR_BROKEN_PIPE)
352+ break; // finished
353+ else{
354+
355+ // couldn't read from pipe
356+ throw_last_error(__FILE__, __LINE__);
357+ }
358+ }
359+
360+ // remove the windows specific carriage return outputs
361+ // std::stringstream lTmp;
362+ // lTmp.write(lBuffer,lBytesRead);
363+ // std::string lRawString=lTmp.str();
364+ // std::replace( lRawString.begin(), lRawString.end(), '\r', ' ' );
365+ // aTargetStream.write(lRawString.c_str(),static_cast<std::streamsize>(lRawString.length()));
366+ for(DWORD i=0;i<lBytesRead;i++)
367+ {
368+ if(lBuffer[i] != '\r')
369+ aTargetStream << lBuffer[i];
370+ }
371+ lBytesRead = 0;
372+ }
373+}
374+
375+/******************************************
376+* Create a child process on windows with
377+* redirected output
378+*/
379+BOOL create_child_process(HANDLE aStdOutputPipe,HANDLE aStdErrorPipe,const std::string& aCommand,PROCESS_INFORMATION& aProcessInformation){
380+ STARTUPINFO lChildStartupInfo;
381+ BOOL result=FALSE;
382+
383+ // set the output handles
384+ FillMemory(&lChildStartupInfo,sizeof(lChildStartupInfo),0);
385+ lChildStartupInfo.cb = sizeof(lChildStartupInfo);
386+ GetStartupInfo(&lChildStartupInfo);
387+ lChildStartupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
388+ lChildStartupInfo.wShowWindow = SW_HIDE; // don't show the command window
389+ lChildStartupInfo.hStdOutput = aStdOutputPipe;
390+ lChildStartupInfo.hStdError = aStdErrorPipe;
391+
392+ // convert from const char* to char*
393+ size_t length = strlen(aCommand.c_str());
394+#ifdef UNICODE
395+ WCHAR *tmpCommand = new WCHAR[length+1];
396+ MultiByteToWideChar(CP_UTF8, 0, aCommand.c_str(), -1, tmpCommand, length+1);
397+#else
398+ char *tmpCommand=new char[length+1];
399+ strcpy (tmpCommand,aCommand.c_str());
400+ tmpCommand[length]='\0';
401+#endif
402+
403+ try{
404+
405+ // settings for the child process
406+ LPCTSTR lApplicationName=NULL;
407+ LPTSTR lCommandLine=tmpCommand;
408+ LPSECURITY_ATTRIBUTES lProcessAttributes=NULL;
409+ LPSECURITY_ATTRIBUTES lThreadAttributes=NULL;
410+ BOOL lInheritHandles=TRUE; // that's what we want
411+ DWORD lCreationFlags=CREATE_NEW_CONSOLE;
412+ LPVOID lEnvironment=NULL;
413+ LPCTSTR lCurrentDirectory=NULL; // same as main process
414+
415+ // start child
416+ result=CreateProcess(
417+ lApplicationName,lCommandLine,lProcessAttributes,
418+ lThreadAttributes,lInheritHandles,lCreationFlags,
419+ lEnvironment,lCurrentDirectory,&lChildStartupInfo,
420+ &aProcessInformation);
421+
422+ }catch(...){
423+ delete[] tmpCommand;
424+ tmpCommand=0;
425+ throw;
426+ }
427+
428+ delete[] tmpCommand;
429+ tmpCommand=0;
430+
431+ return result;
432+}
433+
434+/******************************************
435+* run a process that executes the aCommand
436+* in a new console and reads the output
437+*/
438+int run_process(
439+ const std::string& aCommand,
440+ std::ostringstream& aTargetOutStream,
441+ std::ostringstream& aTargetErrStream)
442+{
443+ HANDLE lOutRead, lErrRead, lStdOut, lStdErr;
444+ SECURITY_ATTRIBUTES lSecurityAttributes;
445+ PROCESS_INFORMATION lChildProcessInfo;
446+ DWORD exitCode=0;
447+
448+ // prepare security attributes
449+ lSecurityAttributes.nLength= sizeof(lSecurityAttributes);
450+ lSecurityAttributes.lpSecurityDescriptor = NULL;
451+ lSecurityAttributes.bInheritHandle = TRUE;
452+
453+ // create output pipes
454+ if(
455+ !CreatePipe(&lOutRead,&lStdOut,&lSecurityAttributes,1024*1024) // std::cout >> lOutRead
456+ || !CreatePipe(&lErrRead,&lStdErr,&lSecurityAttributes,1024*1024) // std::cerr >> lErrRead
457+ ){
458+ Item lQName = ProcessModule::getItemFactory()->createQName(
459+ "http://www.zorba-xquery.com/modules/process", "PROC01");
460+ throw USER_EXCEPTION(lQName,
461+ "Couldn't create one of std::cout/std::cerr pipe for child process execution."
462+ );
463+ };
464+
465+ //start child process
466+ BOOL ok = create_child_process(lStdOut,lStdErr,aCommand,lChildProcessInfo);
467+ if(ok==TRUE)
468+ {
469+
470+ // close unneeded handle
471+ CloseHandle(lChildProcessInfo.hThread);
472+
473+ // wait for the process to finish
474+ WaitForSingleObject(lChildProcessInfo.hProcess,INFINITE);
475+ if (!GetExitCodeProcess(lChildProcessInfo.hProcess, &exitCode))
476+ {
477+ std::stringstream lErrorMsg;
478+ lErrorMsg
479+ << "Couldn't get exit code from child process. Executed command: '" << aCommand << "'.";
480+ Item lQName = ProcessModule::getItemFactory()->createQName(
481+ "http://www.zorba-xquery.com/modules/process", "PROC01");
482+ throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
483+ }
484+
485+ CloseHandle(lChildProcessInfo.hProcess);
486+ CloseHandle(lStdOut);
487+ CloseHandle(lStdErr);
488+
489+ // read child's output
490+ read_child_output(lOutRead,aTargetOutStream);
491+ read_child_output(lErrRead,aTargetErrStream);
492+
493+ // close
494+ CloseHandle(lOutRead);
495+ CloseHandle(lErrRead);
496+
497+ }else{
498+ CloseHandle(lStdOut);
499+ CloseHandle(lStdErr);
500+ CloseHandle(lOutRead);
501+ CloseHandle(lErrRead);
502+
503+ // couldn't launch process
504+ throw_last_error(__FILE__, __LINE__);
505+ };
506+
507+
508+ return exitCode;
509+}
510+
511+#else
512+
513+#define READ 0
514+#define WRITE 1
515+
516+pid_t zorba_popen(const char *command, int *infp, int *outfp, int *errfp)
517+{
518+ int p_stdin[2];
519+ int p_stdout[2];
520+ int p_stderr[2];
521+ pid_t pid;
522+
523+ if (pipe(p_stdin) != 0 || pipe(p_stdout) != 0 || pipe(p_stderr) != 0)
524+ return -1;
525+
526+ pid = fork();
527+
528+ if (pid < 0)
529+ return pid;
530+ else if (pid == 0)
531+ {
532+ close(p_stdin[WRITE]);
533+ dup2(p_stdin[READ], 0); // duplicate stdin
534+
535+ close(p_stdout[READ]);
536+ dup2(p_stdout[WRITE], 1); // duplicate stdout
537+
538+ close(p_stderr[READ]);
539+ dup2(p_stderr[WRITE], 2); // duplicate stderr
540+
541+ execl("/bin/sh", "sh", "-c", command, NULL);
542+ perror("execl"); // output the result to standard error
543+ exit(errno);
544+ }
545+
546+ if (infp == NULL)
547+ close(p_stdin[WRITE]);
548+ else
549+ *infp = p_stdin[WRITE];
550+
551+ if (outfp == NULL)
552+ close(p_stdout[READ]);
553+ else
554+ *outfp = p_stdout[READ];
555+
556+ if (errfp == NULL)
557+ close(p_stderr[READ]);
558+ else
559+ *errfp = p_stderr[READ];
560+
561+ close(p_stdin[READ]); // We only write to the forks stdin anyway
562+ close(p_stdout[WRITE]); // and we only read from its stdout
563+ close(p_stderr[WRITE]); // and we only read from its stderr
564+
565+ return pid;
566+}
567+#endif
568+
569+/******************************************************************************
570+ *****************************************************************************/
571+zorba::ItemSequence_t
572+ExecFunction::evaluate(
573+ const Arguments_t& aArgs,
574+ const zorba::StaticContext* aSctx,
575+ const zorba::DynamicContext* aDctx) const
576+{
577+ std::string lCommand;
578+ std::vector<std::string> lArgs;
579+ int exit_code = 0;
580+
581+ lCommand = getOneStringArgument(aArgs, 0).c_str();
582+
583+ if (aArgs.size() > 1)
584+ {
585+ zorba::Item lArg;
586+ Iterator_t arg1_iter = aArgs[1]->getIterator();
587+ arg1_iter->open();
588+ while (arg1_iter->next(lArg))
589+ {
590+ lArgs.push_back(lArg.getStringValue().c_str());
591+ }
592+ arg1_iter->close();
593+ }
594+
595+ std::ostringstream lTmp;
596+
597+#ifdef WIN32
598+ // execute process command in a new commandline
599+ // with quotes at the beggining and at the end
600+ lTmp << "cmd /C \"";
601+#endif
602+
603+ lTmp << "\"" << lCommand << "\""; //quoted for spaced paths/filenames
604+ size_t pos=0;
605+ for (std::vector<std::string>::const_iterator lIter = lArgs.begin();
606+ lIter != lArgs.end(); ++lIter)
607+ {
608+ pos = (*lIter).rfind('\\')+(*lIter).rfind('/');
609+ if (int(pos)>=0)
610+ lTmp << " \"" << *lIter << "\"";
611+ else
612+ lTmp << " " << *lIter;
613+ }
614+#ifdef WIN32
615+ lTmp << "\""; // with quotes at the end for commandline
616+#endif
617+
618+ std::ostringstream lStdout;
619+ std::ostringstream lStderr;
620+
621+#ifdef WIN32
622+ std::string lCommandLineString = lTmp.str();
623+ int code = run_process(lCommandLineString, lStdout, lStderr);
624+
625+ if (code != 0)
626+ {
627+ std::stringstream lErrorMsg;
628+ lErrorMsg << "Failed to execute the command (" << code << ")";
629+ Item lQName = ProcessModule::getItemFactory()->createQName(
630+ "http://www.zorba-xquery.com/modules/process", "PROC01");
631+ throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
632+ }
633+ exit_code = code;
634+
635+#else //not WIN32
636+
637+ int outfp;
638+ int errfp;
639+ int status;
640+ pid_t pid;
641+
642+ pid = zorba_popen(lTmp.str().c_str(), NULL, &outfp, &errfp);
643+ if ( pid == -1 )
644+ {
645+ std::stringstream lErrorMsg;
646+ lErrorMsg << "Failed to execute the command (" << pid << ")";
647+ Item lQName = ProcessModule::getItemFactory()->createQName(
648+ "http://www.zorba-xquery.com/modules/process", "PROC01");
649+ throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
650+ }
651+ else
652+ {
653+ char lBuf[PATH_MAX];
654+ ssize_t length = 0;
655+ while ( (length=read(outfp, lBuf, PATH_MAX)) > 0 )
656+ {
657+ lStdout.write(lBuf, length);
658+ }
659+
660+ status = close(outfp);
661+
662+ while ( (length=read(errfp, lBuf, PATH_MAX)) > 0 )
663+ {
664+ lStderr.write(lBuf, length);
665+ }
666+
667+ status = close(errfp);
668+
669+ if ( status < 0 )
670+ {
671+ std::stringstream lErrorMsg;
672+ lErrorMsg << "Failed to close the err stream (" << status << ")";
673+ Item lQName = ProcessModule::getItemFactory()->createQName(
674+ "http://www.zorba-xquery.com/modules/process", "PROC01");
675+ throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
676+ }
677+
678+ int stat = 0;
679+
680+ pid_t w = waitpid(pid, &stat, 0);
681+
682+ if (w == -1)
683+ {
684+ std::stringstream lErrorMsg;
685+ lErrorMsg << "Failed to wait for child process ";
686+ Item lQName = ProcessModule::getItemFactory()->createQName(
687+ "http://www.zorba-xquery.com/modules/process", "PROC01");
688+ throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
689+ }
690+
691+ if (WIFEXITED(stat))
692+ {
693+ //std::cout << " WEXITSTATUS : " << WEXITSTATUS(stat) << std::endl; std::cout.flush();
694+ exit_code = WEXITSTATUS(stat);
695+ }
696+ else if (WIFSIGNALED(stat))
697+ {
698+ //std::cout << " WTERMSIG : " << WTERMSIG(stat) << std::endl; std::cout.flush();
699+ exit_code = 128 + WTERMSIG(stat);
700+ }
701+ else if (WIFSTOPPED(stat))
702+ {
703+ //std::cout << " STOPSIG : " << WSTOPSIG(stat) << std::endl; std::cout.flush();
704+ exit_code = 128 + WSTOPSIG(stat);
705+ }
706+ else
707+ {
708+ //std::cout << " else : " << std::endl; std::cout.flush();
709+ exit_code = 255;
710+ }
711+
712+ //std::cout << " exit_code : " << exit_code << std::endl; std::cout.flush();
713+
714+ }
715+#endif // WIN32
716+
717+ zorba::Item lResult;
718+ create_result_node(lResult, lStdout.str(), lStderr.str(), exit_code,
719+ theModule->getItemFactory());
720+
721+ return zorba::ItemSequence_t(new zorba::SingletonItemSequence(lResult));
722+}
723+
724+String ExecFunction::getOneStringArgument (const Arguments_t& aArgs, int aPos)
725+ const
726+{
727+ Item lItem;
728+ Iterator_t args_iter = aArgs[aPos]->getIterator();
729+ args_iter->open();
730+ args_iter->next(lItem);
731+ zorba::String lTmpString = lItem.getStringValue();
732+ args_iter->close();
733+ return lTmpString;
734+}
735+
736+/******************************************************************************
737+ *****************************************************************************/
738+ProcessModule::~ProcessModule()
739+{
740+ for (FuncMap_t::const_iterator lIter = theFunctions.begin();
741+ lIter != theFunctions.end(); ++lIter) {
742+ delete lIter->second;
743+ }
744+ theFunctions.clear();
745+}
746+
747+zorba::ExternalFunction*
748+ProcessModule::getExternalFunction(const zorba::String& aLocalname)
749+{
750+ FuncMap_t::const_iterator lFind = theFunctions.find(aLocalname);
751+ zorba::ExternalFunction*& lFunc = theFunctions[aLocalname];
752+ if (lFind == theFunctions.end())
753+ {
754+ if (!aLocalname.compare("exec"))
755+ {
756+ lFunc = new ExecFunction(this);
757+ }
758+ }
759+ return lFunc;
760+}
761+
762+void ProcessModule::destroy()
763+{
764+ if (!dynamic_cast<ProcessModule*>(this)) {
765+ return;
766+ }
767+ delete this;
768+}
769+
770+ItemFactory* ProcessModule::theFactory = 0;
771+
772+} /* namespace processmodule */
773+} /* namespace zorba */
774+
775+#ifdef WIN32
776+# define DLL_EXPORT __declspec(dllexport)
777+#else
778+# define DLL_EXPORT __attribute__ ((visibility("default")))
779+#endif
780+
781+extern "C" DLL_EXPORT zorba::ExternalModule* createModule() {
782+ return new zorba::processmodule::ProcessModule();
783+}
784+/* vim:set et sw=2 ts=2: */
785
786=== added file 'src/process-1.xq.src/process.h'
787--- src/process-1.xq.src/process.h 1970-01-01 00:00:00 +0000
788+++ src/process-1.xq.src/process.h 2013-06-13 10:54:38 +0000
789@@ -0,0 +1,105 @@
790+/*
791+ * Copyright 2006-2008 The FLWOR Foundation.
792+ *
793+ * Licensed under the Apache License, Version 2.0 (the "License");
794+ * you may not use this file except in compliance with the License.
795+ * You may obtain a copy of the License at
796+ *
797+ * http://www.apache.org/licenses/LICENSE-2.0
798+ *
799+ * Unless required by applicable law or agreed to in writing, software
800+ * distributed under the License is distributed on an "AS IS" BASIS,
801+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
802+ * See the License for the specific language governing permissions and
803+ * limitations under the License.
804+ */
805+#ifndef ZORBA_PROCESSMODULE_PROCESS_H
806+#define ZORBA_PROCESSMODULE_PROCESS_H
807+
808+#include <map>
809+
810+#include <zorba/zorba.h>
811+#include <zorba/error.h>
812+#include <zorba/external_module.h>
813+#include <zorba/function.h>
814+
815+namespace zorba {
816+namespace processmodule {
817+
818+/******************************************************************************
819+ *****************************************************************************/
820+class ProcessModule : public ExternalModule
821+{
822+private:
823+ static ItemFactory* theFactory;
824+
825+protected:
826+ class ltstr
827+ {
828+ public:
829+ bool operator()(const String& s1, const String& s2) const
830+ {
831+ return s1.compare(s2) < 0;
832+ }
833+ };
834+
835+ typedef std::map<String, ExternalFunction*, ltstr> FuncMap_t;
836+
837+ FuncMap_t theFunctions;
838+
839+public:
840+ virtual ~ProcessModule();
841+
842+ virtual zorba::String getURI() const
843+ { return "http://www.zorba-xquery.com/modules/process"; }
844+
845+ virtual zorba::ExternalFunction*
846+ getExternalFunction(const zorba::String& aLocalname);
847+
848+ virtual void destroy();
849+
850+ static ItemFactory* getItemFactory()
851+ {
852+ if(!theFactory)
853+ {
854+ theFactory = Zorba::getInstance(0)->getItemFactory();
855+ }
856+
857+ return theFactory;
858+ }
859+};
860+
861+/******************************************************************************
862+ *****************************************************************************/
863+class ExecFunction : public ContextualExternalFunction
864+{
865+public:
866+ ExecFunction(const ProcessModule* aModule) : theModule(aModule) {}
867+
868+ virtual ~ExecFunction() {}
869+
870+ virtual zorba::String
871+ getLocalName() const { return "exec"; }
872+
873+ virtual zorba::ItemSequence_t
874+ evaluate(const Arguments_t&,
875+ const zorba::StaticContext*,
876+ const zorba::DynamicContext*) const;
877+
878+ virtual String getURI() const
879+ {
880+ return theModule->getURI();
881+ }
882+
883+protected:
884+ const ProcessModule* theModule;
885+
886+ String getOneStringArgument (const Arguments_t& aArgs, int index) const;
887+};
888+
889+
890+
891+} /* namespace processmodule */
892+} /* namespace zorba */
893+
894+#endif // ZORBA_PROCESSMODULE_PROCESS_H
895
896=== renamed file 'src/com/zorba-xquery/www/modules/process.xq' => 'src/process-2.xq'
897--- src/com/zorba-xquery/www/modules/process.xq 2012-12-06 02:13:28 +0000
898+++ src/process-2.xq 2013-06-13 10:54:38 +0000
899@@ -1,7 +1,7 @@
900-xquery version "3.0";
901+jsoniq version "1.0";
902
903 (:
904- : Copyright 2006-2009 The FLWOR Foundation.
905+ : Copyright 2006-2013 The FLWOR Foundation.
906 :
907 : Licensed under the Apache License, Version 2.0 (the "License");
908 : you may not use this file except in compliance with the License.
909@@ -17,77 +17,181 @@
910 :)
911
912 (:~
913+ : <p>
914 : This module provides functions to create a native process and return the result
915- : (i.e. exit code, result on standard out and error).
916+ : (i.e. exit code, result on standard out and error) of executing the given
917+ : file or command.
918+ : </p>
919 :
920+ : <p>
921 : Example:
922- :<pre class="ace-static" ace-mode="xquery">
923- : import module namespace proc = "http://www.zorba-xquery.com/modules/process";
924- : proc:exec("ls")
925- :</pre>
926+ : <pre>
927+ : import module namespace proc = "http://zorba.io/modules/process";
928+ : proc:exec("ls")
929+ : </pre>
930+ : </p>
931 :
932+ : <p>
933 : Potential result:
934- : <pre class="ace-static" ace-mode="xquery"><![CDATA[
935- : <result xmlns="http://www.zorba-xquery.com/modules/process">
936- : <stdout>myfile.txt</stout>
937- : <stderr/>
938- : <exit-code>0</exit-code>
939- : </result>
940- : ]]></pre>
941- :
942- : @author Cezar Andrei
943+ : <pre>
944+ : {
945+ : "exit-code": 0,
946+ : "stdout": "myfile.txt",
947+ : "stderr": ""
948+ : }
949+ : </pre>
950+ : </p>
951+ :
952+ : <p>
953+ : The <tt>exec-command</tt> set of functions allows execution of commands
954+ : through the command line interpreter of the operating system, such as "sh"
955+ : on Unix systems or "cmd.exe" on Windows.
956+ : </p>
957+ :
958+ : <p>
959+ : For POSIX compliant platforms the functions return 128 + termination signal
960+ : code of the process as their exit-code.
961+ : On Windows platforms, the exit-code is the return value of the process or the exit
962+ : or terminate process specified value.
963+ : </p>
964+ :
965+ : @author Cezar Andrei, Nicolae Brinza
966 : @project Zorba/IO/Process
967- :
968 :)
969-module namespace process = "http://www.zorba-xquery.com/modules/process";
970+module namespace p = "http://zorba.io/modules/process";
971
972 declare namespace an = "http://www.zorba-xquery.com/annotations";
973
974 declare namespace ver = "http://www.zorba-xquery.com/options/versioning";
975 declare option ver:module-version "1.0";
976
977+
978 (:~
979- : Executes the specified string command in a separate process.
980+ : <p>
981+ : Executes the specified program in a separate process.
982+ : </p>
983+ :
984+ : <p>
985 : This function does not allow arguments to be passed to
986- : the command.
987+ : the command. The $filename parameter can contain the full path to the
988+ : executable. On Unix systems, if the specified filename does not contain
989+ : a slash "/", the function duplicates the actions of the shell in searching
990+ : for an executable file. The file is sought in the colon-separated list of
991+ : directory pathnames specified in the PATH environment variable. If this
992+ : variable isn't defined, the path list defaults to the current directory
993+ : followed by the list of directories returned by the operating system.
994+ : </p>
995+ :
996+ : @param $filename the name of program to be executed
997+ :
998+ : @return the result of the execution as an object
999+ :
1000+ : @error p:PROC01 if an error occurred while communicating with the process.
1001+ :)
1002+declare %an:sequential function p:exec(
1003+ $filename as string
1004+) as object external;
1005+
1006+(:~
1007+ : <p>
1008+ : Executes the specified program in a separate process.
1009+ : </p>
1010+ :
1011+ : <p>
1012+ : The $filename parameter can contain the full path to the
1013+ : executable. On Unix systems, if the specified filename does not contain
1014+ : a slash "/", the function duplicates the actions of the shell in searching
1015+ : for an executable file. The file is sought in the colon-separated list of
1016+ : directory pathnames specified in the PATH environment variable. If this
1017+ : variable isn't defined, the path list defaults to the current directory
1018+ : followed by the list of directories returned by the operating system.
1019+ : The $args parameters will be passed to the executable file as arguments.
1020+ : </p>
1021+ :
1022+ : @param $filename the name of program to be executed
1023+ : @param $args arguments to be passed to the executable
1024+ :
1025+ : @return the result of the execution as an object
1026+ :
1027+ : @error p:PROC01 if an error occurred while communicating with the process.
1028+ :)
1029+declare %an:sequential function p:exec(
1030+ $filename as string,
1031+ $args as string*
1032+) as object external;
1033+
1034+(:~
1035+ : <p>
1036+ : Executes the specified program in a separate process.
1037+ : </p>
1038+ :
1039+ : <p>
1040+ : The $filename parameter can contain the full path to the
1041+ : executable. On Unix systems, if the specified filename does not contain
1042+ : a slash "/", the function duplicates the actions of the shell in searching
1043+ : for an executable file. The file is sought in the colon-separated list of
1044+ : directory pathnames specified in the PATH environment variable. If this
1045+ : variable isn't defined, the path list defaults to the current directory
1046+ : followed by the list of directories returned by the operating system.
1047+ : </p>
1048+ :
1049+ : <p>
1050+ : The $env allows defining and passing environment variables to the target
1051+ : process. They should be in the form "ENVVAR=value" where "ENVVAR" is the
1052+ : name of the environment variable and "value' is the string value to set it to.
1053+ : </p>
1054+ :
1055+ : @param $filename the name of program to be executed
1056+ : @param $args arguments to be passed to the executable
1057+ : @param $env list of environment variables for the executable
1058+ :
1059+ : @return the result of the execution as an object
1060+ :
1061+ : @error p:PROC01 if an error occurred while communicating with the process.
1062+ :)
1063+declare %an:sequential function p:exec(
1064+ $filename as string,
1065+ $args as string*,
1066+ $env as string*
1067+) as object external;
1068+
1069+(:~
1070+ : <p>
1071+ : Executes the specified string command in a separate process.
1072+ : </p>
1073+ :
1074+ : <p>
1075+ : This function does not allow arguments to be passed to the command.
1076+ : </p>
1077 :
1078 : @param $cmd command to be executed (without arguments)
1079 :
1080- : @return the result of the execution as an element as
1081- : shown in the documentation of this module. The exit-code
1082- : element returns the exit code of the child process.
1083- : For POSIX compliant platforms: returns the process exit code. If process is
1084- : terminated or stopped: 128 + termination signal code.
1085- : For Windows platforms: returns the return value of the process or the exit
1086- : or terminate process specified value.
1087+ : @return the result of the execution as an object
1088 :
1089- : @error process:PROC01 if an error occurred while communicating
1090- : with the executed process.
1091+ : @error p:PROC01 if an error occurred while communicating with the process.
1092 :)
1093-declare %an:sequential function process:exec(
1094- $cmd as xs:string
1095-) as element(process:result) external;
1096+declare %an:sequential function p:exec-command(
1097+ $cmd as string
1098+) as object external;
1099
1100 (:~
1101+ : <p>
1102 : Executes the specified string command in a separate process.
1103+ : </p>
1104+ :
1105+ : <p>
1106 : Each of the strings in the sequence passed in as the second
1107 : argument is passed as an argument to the executed command.
1108+ : </p>
1109 :
1110 : @param $cmd command to be executed (without arguments)
1111 : @param $args the arguments passed to the executed command (e.g. "-la")
1112 :
1113- : @return the result of the execution as an element as
1114- : shown in the documentation of this module. The exit-code
1115- : element returns the exit code of the child process.
1116- : For POSIX compliant platforms: returns the process exit code. If process is
1117- : terminated or stopped: 128 + termination signal code.
1118- : For Windows platforms: returns the return value of the process or the exit
1119- : or terminate process specified value.
1120+ : @return the result of the execution as an object
1121 :
1122- : @error process:PROC01 if an error occurred while communicating
1123- : with the executed process.
1124+ : @error p:PROC01 if an error occurred while communicating with the process.
1125 :)
1126-declare %an:sequential function process:exec(
1127- $cmd as xs:string,
1128- $args as xs:string*
1129-) as element(process:result) external;
1130+declare %an:sequential function p:exec-command(
1131+ $cmd as string,
1132+ $args as string*
1133+) as object external;
1134
1135=== renamed directory 'src/com/zorba-xquery/www/modules/process.xq.src' => 'src/process-2.xq.src'
1136=== modified file 'src/process-2.xq.src/process.cpp'
1137--- src/com/zorba-xquery/www/modules/process.xq.src/process.cpp 2013-06-04 02:21:47 +0000
1138+++ src/process-2.xq.src/process.cpp 2013-06-13 10:54:38 +0000
1139@@ -1,5 +1,5 @@
1140 /*
1141- * Copyright 2006-2008 The FLWOR Foundation.
1142+ * Copyright 2006-2013 The FLWOR Foundation.
1143 *
1144 * Licensed under the Apache License, Version 2.0 (the "License");
1145 * you may not use this file except in compliance with the License.
1146@@ -25,7 +25,6 @@
1147
1148 #ifdef WIN32
1149 # include <windows.h>
1150-
1151 # ifndef NDEBUG
1152 # define _CRTDBG_MAP_ALLOC
1153 # include <stdlib.h>
1154@@ -48,55 +47,45 @@
1155
1156 #include "process.h"
1157
1158+// Provde the execvpe() function since some platforms don't have it
1159+#ifndef WIN32
1160+int execvpe(const char *program, char **argv, char **envp)
1161+{
1162+ char **saved = environ;
1163+ int rc;
1164+ environ = envp;
1165+ rc = execvp(program, argv);
1166+ environ = saved;
1167+ return rc;
1168+}
1169+#endif
1170+
1171+
1172 namespace zorba {
1173 namespace processmodule {
1174
1175 /******************************************************************************
1176 *****************************************************************************/
1177-void create_result_node(
1178+void create_result_object(
1179 zorba::Item& aResult,
1180 const std::string& aStandardOut,
1181 const std::string& aErrorOut,
1182 int aExitCode,
1183 zorba::ItemFactory* aFactory)
1184+{
1185+ std::vector<std::pair<zorba::Item,zorba::Item> > pairs;
1186+
1187+ pairs.push_back(std::pair<zorba::Item,zorba::Item>(aFactory->createString("exit-code"), aFactory->createInt(aExitCode)));
1188+ pairs.push_back(std::pair<zorba::Item,zorba::Item>(aFactory->createString("stdout"), aFactory->createString(aStandardOut)));
1189+ pairs.push_back(std::pair<zorba::Item,zorba::Item>(aFactory->createString("stderr"), aFactory->createString(aErrorOut)));
1190+
1191+ aResult = aFactory->createJSONObject(pairs);
1192+}
1193+
1194+void free_char_vector(std::vector<char*> argv)
1195 {
1196- zorba::Item lResultQName =
1197- aFactory->createQName("http://www.zorba-xquery.com/modules/process", "result");
1198- zorba::Item lExitCodeQName =
1199- aFactory->createQName("http://www.zorba-xquery.com/modules/process", "exit-code");
1200- zorba::Item lOutputQName =
1201- aFactory->createQName("http://www.zorba-xquery.com/modules/process", "stdout");
1202- zorba::Item lErrorQName =
1203- aFactory->createQName("http://www.zorba-xquery.com/modules/process", "stderr");
1204- zorba::Item lNullItem;
1205- zorba::Item lTypeName =
1206- aFactory->createQName("http://www.w3.org/2001/XMLSchema", "untyped");
1207-
1208- zorba::NsBindings lNSBindings;
1209-
1210- // root node called result
1211- aResult = aFactory->createElementNode(
1212- lNullItem, lResultQName, lTypeName, false, false, lNSBindings);
1213-
1214- // <result><output> aStandardOut </output></result>
1215- zorba::Item lOutput;
1216- lOutput = aFactory->createElementNode(
1217- aResult, lOutputQName, lTypeName, true, false, lNSBindings);
1218- aFactory->createTextNode(lOutput, aStandardOut);
1219-
1220- // <result><error> aErrorOut </error></result>
1221- zorba::Item lError;
1222- lError = aFactory->createElementNode(
1223- aResult, lErrorQName, lTypeName, true, false, lNSBindings);
1224- aFactory->createTextNode(lError, aErrorOut);
1225-
1226- // <result><exit-code> aExitCode </exit-code></result>
1227- zorba::Item lExitCode;
1228- lExitCode = aFactory->createElementNode(
1229- aResult, lExitCodeQName, lTypeName, true, false, lNSBindings);
1230- std::ostringstream lExitCodeString;
1231- lExitCodeString << aExitCode;
1232- aFactory->createTextNode(lExitCode, lExitCodeString.str());
1233+ for (unsigned int i=0; i<argv.size(); i++)
1234+ free(argv[i]);
1235 }
1236
1237 #ifdef WIN32
1238@@ -306,7 +295,7 @@
1239 #define READ 0
1240 #define WRITE 1
1241
1242-pid_t zorba_popen(const char *command, int *infp, int *outfp, int *errfp)
1243+pid_t exec_helper(int *infp, int *outfp, int *errfp, const char *command, char* argv[], char* env[])
1244 {
1245 int p_stdin[2];
1246 int p_stdout[2];
1247@@ -331,9 +320,28 @@
1248 close(p_stderr[READ]);
1249 dup2(p_stderr[WRITE], 2); // duplicate stderr
1250
1251- execl("/bin/sh", "sh", "-c", command, NULL);
1252+ if (command)
1253+ execl("/bin/sh", "sh", "-c", command, NULL);
1254+ else if (env == NULL)
1255+ execvp(argv[0], argv);
1256+ else
1257+ execvpe(argv[0], argv, env);
1258+
1259 perror("execl"); // output the result to standard error
1260- exit(errno);
1261+
1262+ // TODO:
1263+ // Currently, if the child process exits with an error, the following happens:
1264+ // -- exit(errno) is called
1265+ // -- static object destruction ocurrs
1266+ // -- Zorba store is destroyed in the child process and this leaks several URIs
1267+ // and prints error messages to stderr. An exception is thrown and this overwrites
1268+ // the exit code of the invoked process.
1269+ //
1270+ // Until a proper solution is found, the child fork() process will call abort(), which
1271+ // will not trigger static object destruction.
1272+
1273+ abort();
1274+ // exit(errno);
1275 }
1276
1277 if (infp == NULL)
1278@@ -357,10 +365,23 @@
1279
1280 return pid;
1281 }
1282+
1283 #endif
1284
1285+
1286 /******************************************************************************
1287 *****************************************************************************/
1288+String ExecFunction::getOneStringArgument (const Arguments_t& aArgs, int aPos) const
1289+{
1290+ Item lItem;
1291+ Iterator_t args_iter = aArgs[aPos]->getIterator();
1292+ args_iter->open();
1293+ args_iter->next(lItem);
1294+ zorba::String lTmpString = lItem.getStringValue();
1295+ args_iter->close();
1296+ return lTmpString;
1297+}
1298+
1299 zorba::ItemSequence_t
1300 ExecFunction::evaluate(
1301 const Arguments_t& aArgs,
1302@@ -369,6 +390,7 @@
1303 {
1304 std::string lCommand;
1305 std::vector<std::string> lArgs;
1306+ std::vector<std::string> lEnv;
1307 int exit_code = 0;
1308
1309 lCommand = getOneStringArgument(aArgs, 0).c_str();
1310@@ -378,12 +400,20 @@
1311 zorba::Item lArg;
1312 Iterator_t arg1_iter = aArgs[1]->getIterator();
1313 arg1_iter->open();
1314- while (arg1_iter->next(lArg))
1315- {
1316+ while (arg1_iter->next(lArg))
1317 lArgs.push_back(lArg.getStringValue().c_str());
1318- }
1319 arg1_iter->close();
1320 }
1321+
1322+ if (aArgs.size() > 2)
1323+ {
1324+ zorba::Item lArg;
1325+ Iterator_t arg1_iter = aArgs[2]->getIterator();
1326+ arg1_iter->open();
1327+ while (arg1_iter->next(lArg))
1328+ lEnv.push_back(lArg.getStringValue().c_str());
1329+ arg1_iter->close();
1330+ }
1331
1332 std::ostringstream lTmp;
1333
1334@@ -431,18 +461,38 @@
1335 int errfp;
1336 int status;
1337 pid_t pid;
1338+
1339+ std::vector<char*> argv(lArgs.size()+2, NULL);
1340+ std::vector<char*> env(lEnv.size()+1, NULL);
1341
1342- pid = zorba_popen(lTmp.str().c_str(), NULL, &outfp, &errfp);
1343- if ( pid == -1 )
1344- {
1345- std::stringstream lErrorMsg;
1346- lErrorMsg << "Failed to execute the command (" << pid << ")";
1347- Item lQName = ProcessModule::getItemFactory()->createQName(
1348- "http://www.zorba-xquery.com/modules/process", "PROC01");
1349- throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
1350- }
1351- else
1352- {
1353+ try
1354+ {
1355+ if (theIsExecProgram)
1356+ {
1357+ argv[0] = strdup(lCommand.c_str());
1358+ for (unsigned int i=0; i<lArgs.size(); i++)
1359+ argv[i+1] = strdup(lArgs[i].c_str());
1360+
1361+ for (unsigned int i=0; i<lEnv.size(); i++)
1362+ env[i] = strdup(lEnv[i].c_str());
1363+
1364+ pid = exec_helper(NULL, &outfp, &errfp, NULL, argv.data(), lEnv.size() ? env.data() : NULL);
1365+ }
1366+ else
1367+ {
1368+ pid = exec_helper(NULL, &outfp, &errfp, lTmp.str().c_str(), argv.data(), NULL);
1369+ }
1370+
1371+ if ( pid == -1 )
1372+ {
1373+ std::stringstream lErrorMsg;
1374+ lErrorMsg << "Failed to execute the command (" << pid << ")";
1375+ Item lQName = ProcessModule::getItemFactory()->createQName(
1376+ "http://www.zorba-xquery.com/modules/process", "PROC01");
1377+ throw USER_EXCEPTION(lQName, lErrorMsg.str().c_str());
1378+ return NULL;
1379+ }
1380+
1381 char lBuf[PATH_MAX];
1382 ssize_t length = 0;
1383 while ( (length=read(outfp, lBuf, PATH_MAX)) > 0 )
1384@@ -503,28 +553,23 @@
1385 }
1386
1387 //std::cout << " exit_code : " << exit_code << std::endl; std::cout.flush();
1388-
1389+ free_char_vector(argv);
1390+ free_char_vector(env);
1391+ }
1392+ catch (...)
1393+ {
1394+ free_char_vector(argv);
1395+ free_char_vector(env);
1396+ throw;
1397 }
1398 #endif // WIN32
1399
1400 zorba::Item lResult;
1401- create_result_node(lResult, lStdout.str(), lStderr.str(), exit_code,
1402- theModule->getItemFactory());
1403-
1404+ create_result_object(lResult, lStdout.str(), lStderr.str(), exit_code,
1405+ theModule->getItemFactory());
1406 return zorba::ItemSequence_t(new zorba::SingletonItemSequence(lResult));
1407 }
1408
1409-String ExecFunction::getOneStringArgument (const Arguments_t& aArgs, int aPos)
1410- const
1411-{
1412- Item lItem;
1413- Iterator_t args_iter = aArgs[aPos]->getIterator();
1414- args_iter->open();
1415- args_iter->next(lItem);
1416- zorba::String lTmpString = lItem.getStringValue();
1417- args_iter->close();
1418- return lTmpString;
1419-}
1420
1421 /******************************************************************************
1422 *****************************************************************************/
1423@@ -544,10 +589,14 @@
1424 zorba::ExternalFunction*& lFunc = theFunctions[aLocalname];
1425 if (lFind == theFunctions.end())
1426 {
1427- if (!aLocalname.compare("exec"))
1428+ if (aLocalname.compare("exec-command") == 0)
1429 {
1430 lFunc = new ExecFunction(this);
1431 }
1432+ else if (aLocalname.compare("exec") == 0)
1433+ {
1434+ lFunc = new ExecFunction(this, true);
1435+ }
1436 }
1437 return lFunc;
1438 }
1439
1440=== modified file 'src/process-2.xq.src/process.h'
1441--- src/com/zorba-xquery/www/modules/process.xq.src/process.h 2012-07-21 01:09:37 +0000
1442+++ src/process-2.xq.src/process.h 2013-06-13 10:54:38 +0000
1443@@ -1,5 +1,5 @@
1444 /*
1445- * Copyright 2006-2008 The FLWOR Foundation.
1446+ * Copyright 2006-2013 The FLWOR Foundation.
1447 *
1448 * Licensed under the Apache License, Version 2.0 (the "License");
1449 * you may not use this file except in compliance with the License.
1450@@ -51,7 +51,7 @@
1451 virtual ~ProcessModule();
1452
1453 virtual zorba::String getURI() const
1454- { return "http://www.zorba-xquery.com/modules/process"; }
1455+ { return "http://zorba.io/modules/process"; }
1456
1457 virtual zorba::ExternalFunction*
1458 getExternalFunction(const zorba::String& aLocalname);
1459@@ -74,12 +74,13 @@
1460 class ExecFunction : public ContextualExternalFunction
1461 {
1462 public:
1463- ExecFunction(const ProcessModule* aModule) : theModule(aModule) {}
1464+ ExecFunction(const ProcessModule* aModule, bool aExecProgram = false)
1465+ : theModule(aModule), theIsExecProgram(aExecProgram) {}
1466
1467 virtual ~ExecFunction() {}
1468
1469 virtual zorba::String
1470- getLocalName() const { return "exec"; }
1471+ getLocalName() const { if (theIsExecProgram) return "exec"; else return "exec-command"; }
1472
1473 virtual zorba::ItemSequence_t
1474 evaluate(const Arguments_t&,
1475@@ -93,12 +94,14 @@
1476
1477 protected:
1478 const ProcessModule* theModule;
1479+
1480+ bool theIsExecProgram; // if set to true, will use the execvpe() version of the system function
1481+ // if set to false, will build a command string and pass it to
1482+ // either "bash" or "cmd.exe" (through execl() on Linux)
1483
1484 String getOneStringArgument (const Arguments_t& aArgs, int index) const;
1485 };
1486
1487-
1488-
1489 } /* namespace processmodule */
1490 } /* namespace zorba */
1491
1492
1493=== added file 'test/ExpQueryResults/process1-01.xml.res'
1494--- test/ExpQueryResults/process1-01.xml.res 1970-01-01 00:00:00 +0000
1495+++ test/ExpQueryResults/process1-01.xml.res 2013-06-13 10:54:38 +0000
1496@@ -0,0 +1,2 @@
1497+<?xml version="1.0" encoding="UTF-8"?>
1498+<result><out>hello world</out><err>Ooops. an error.</err></result>
1499
1500=== added file 'test/ExpQueryResults/process2-01.xml.res'
1501--- test/ExpQueryResults/process2-01.xml.res 1970-01-01 00:00:00 +0000
1502+++ test/ExpQueryResults/process2-01.xml.res 2013-06-13 10:54:38 +0000
1503@@ -0,0 +1,1 @@
1504+true
1505\ No newline at end of file
1506
1507=== added file 'test/ExpQueryResults/process2-02.xml.res'
1508--- test/ExpQueryResults/process2-02.xml.res 1970-01-01 00:00:00 +0000
1509+++ test/ExpQueryResults/process2-02.xml.res 2013-06-13 10:54:38 +0000
1510@@ -0,0 +1,1 @@
1511+true
1512\ No newline at end of file
1513
1514=== added file 'test/ExpQueryResults/process2-03.xml.res'
1515--- test/ExpQueryResults/process2-03.xml.res 1970-01-01 00:00:00 +0000
1516+++ test/ExpQueryResults/process2-03.xml.res 2013-06-13 10:54:38 +0000
1517@@ -0,0 +1,1 @@
1518+true
1519\ No newline at end of file
1520
1521=== added file 'test/ExpQueryResults/process2-04.xml.res'
1522--- test/ExpQueryResults/process2-04.xml.res 1970-01-01 00:00:00 +0000
1523+++ test/ExpQueryResults/process2-04.xml.res 2013-06-13 10:54:38 +0000
1524@@ -0,0 +1,1 @@
1525+true
1526\ No newline at end of file
1527
1528=== added file 'test/ExpQueryResults/process2-05.xml.res'
1529--- test/ExpQueryResults/process2-05.xml.res 1970-01-01 00:00:00 +0000
1530+++ test/ExpQueryResults/process2-05.xml.res 2013-06-13 10:54:38 +0000
1531@@ -0,0 +1,1 @@
1532+true
1533\ No newline at end of file
1534
1535=== added file 'test/ExpQueryResults/process2-06.xml.res'
1536--- test/ExpQueryResults/process2-06.xml.res 1970-01-01 00:00:00 +0000
1537+++ test/ExpQueryResults/process2-06.xml.res 2013-06-13 10:54:38 +0000
1538@@ -0,0 +1,1 @@
1539+true
1540\ No newline at end of file
1541
1542=== renamed file 'test/ExpQueryResults/process.xml.res' => 'test/ExpQueryResults/process2-07.xml.res'
1543=== added file 'test/Queries/process1-01.xq'
1544--- test/Queries/process1-01.xq 1970-01-01 00:00:00 +0000
1545+++ test/Queries/process1-01.xq 2013-06-13 10:54:38 +0000
1546@@ -0,0 +1,24 @@
1547+import module namespace proc = "http://www.zorba-xquery.com/modules/process#1.0";
1548+
1549+
1550+{
1551+ variable $stdOutTest := proc:exec("echo","hello world") ;
1552+ variable $stdErrTest := proc:exec("echo","Ooops. an error. 1>&amp;2");
1553+ variable $stdOutWinTest := proc:exec("cmd", ("/c", "echo","hello world")) ;
1554+ variable $stdErrWinTest := proc:exec("cmd", ("/c", "echo","Ooops. an error. 1>&amp;2"));
1555+
1556+ let $result :=
1557+ <result>
1558+ <out>{normalize-space(data($stdOutTest/proc:stdout))}</out>
1559+ <err>{normalize-space(data($stdErrTest/proc:stderr))}</err>
1560+ </result>
1561+ return
1562+ if (contains($result/err/text(),"is not recognized as an internal or external command"))
1563+ then
1564+ <result>
1565+ <out>{normalize-space(data($stdOutWinTest/proc:stdout))}</out>
1566+ <err>{normalize-space(data($stdErrWinTest/proc:stderr))}</err>
1567+ </result>
1568+ else
1569+ $result
1570+}
1571
1572=== added file 'test/Queries/process2-01.xq'
1573--- test/Queries/process2-01.xq 1970-01-01 00:00:00 +0000
1574+++ test/Queries/process2-01.xq 2013-06-13 10:54:38 +0000
1575@@ -0,0 +1,5 @@
1576+import module namespace proc = "http://zorba.io/modules/process";
1577+
1578+let $result := proc:exec("echo")
1579+return $result("stdout") eq "
1580+"
1581
1582=== added file 'test/Queries/process2-02.xq'
1583--- test/Queries/process2-02.xq 1970-01-01 00:00:00 +0000
1584+++ test/Queries/process2-02.xq 2013-06-13 10:54:38 +0000
1585@@ -0,0 +1,5 @@
1586+import module namespace proc = "http://zorba.io/modules/process";
1587+
1588+let $result := proc:exec("echo",("hello","world"))
1589+return $result("stdout") eq "hello world
1590+"
1591
1592=== added file 'test/Queries/process2-03.xq'
1593--- test/Queries/process2-03.xq 1970-01-01 00:00:00 +0000
1594+++ test/Queries/process2-03.xq 2013-06-13 10:54:38 +0000
1595@@ -0,0 +1,5 @@
1596+import module namespace proc = "http://zorba.io/modules/process";
1597+
1598+let $result := proc:exec("printenv",("TEST_ENV_VAR"),"TEST_ENV_VAR=foo")
1599+return $result("stdout") eq "foo
1600+"
1601
1602=== added file 'test/Queries/process2-04.xq'
1603--- test/Queries/process2-04.xq 1970-01-01 00:00:00 +0000
1604+++ test/Queries/process2-04.xq 2013-06-13 10:54:38 +0000
1605@@ -0,0 +1,6 @@
1606+import module namespace proc = "http://zorba.io/modules/process";
1607+
1608+let $result := proc:exec("printenv",("TEST_ENV_VAR","VAR2"),("TEST_ENV_VAR=foo","VAR2=bar"))
1609+return $result("stdout") eq "foo
1610+bar
1611+"
1612
1613=== added file 'test/Queries/process2-05.xq'
1614--- test/Queries/process2-05.xq 1970-01-01 00:00:00 +0000
1615+++ test/Queries/process2-05.xq 2013-06-13 10:54:38 +0000
1616@@ -0,0 +1,5 @@
1617+import module namespace proc = "http://zorba.io/modules/process";
1618+
1619+let $result := proc:exec("echo","{}[]()()''~!@#$%^&amp;*_-+|<>/?,.")
1620+return $result("stdout") eq "{}[]()()''~!@#$%^&amp;*_-+|<>/?,.
1621+"
1622
1623=== added file 'test/Queries/process2-06.xq'
1624--- test/Queries/process2-06.xq 1970-01-01 00:00:00 +0000
1625+++ test/Queries/process2-06.xq 2013-06-13 10:54:38 +0000
1626@@ -0,0 +1,4 @@
1627+import module namespace proc = "http://zorba.io/modules/process";
1628+
1629+let $result := proc:exec("this_executable_does_not_exist")
1630+return $result("exit-code") ne 0
1631
1632=== renamed file 'test/Queries/process.xq' => 'test/Queries/process2-07.xq'
1633--- test/Queries/process.xq 2011-08-13 00:08:53 +0000
1634+++ test/Queries/process2-07.xq 2013-06-13 10:54:38 +0000
1635@@ -1,23 +1,23 @@
1636-import module namespace proc = "http://www.zorba-xquery.com/modules/process";
1637+import module namespace proc = "http://zorba.io/modules/process";
1638
1639
1640 {
1641- variable $stdOutTest := proc:exec("echo","hello world") ;
1642- variable $stdErrTest := proc:exec("echo","Ooops. an error. 1>&amp;2");
1643- variable $stdOutWinTest := proc:exec("cmd", ("/c", "echo","hello world")) ;
1644- variable $stdErrWinTest := proc:exec("cmd", ("/c", "echo","Ooops. an error. 1>&amp;2"));
1645+ variable $stdOutTest := proc:exec-command("echo","hello world") ;
1646+ variable $stdErrTest := proc:exec-command("echo","Ooops. an error. 1>&amp;2");
1647+ variable $stdOutWinTest := proc:exec-command("cmd", ("/c", "echo","hello world")) ;
1648+ variable $stdErrWinTest := proc:exec-command("cmd", ("/c", "echo","Ooops. an error. 1>&amp;2"));
1649
1650 let $result :=
1651 <result>
1652- <out>{normalize-space(data($stdOutTest/proc:stdout))}</out>
1653- <err>{normalize-space(data($stdErrTest/proc:stderr))}</err>
1654+ <out>{normalize-space(data($stdOutTest("stdout")))}</out>
1655+ <err>{normalize-space(data($stdErrTest("stderr")))}</err>
1656 </result>
1657 return
1658 if (contains($result/err/text(),"is not recognized as an internal or external command"))
1659 then
1660 <result>
1661- <out>{normalize-space(data($stdOutWinTest/proc:stdout))}</out>
1662- <err>{normalize-space(data($stdErrWinTest/proc:stderr))}</err>
1663+ <out>{normalize-space(data($stdOutWinTest("stdout")))}</out>
1664+ <err>{normalize-space(data($stdErrWinTest("stderr")))}</err>
1665 </result>
1666 else
1667 $result

Subscribers

People subscribed via source and target branches

to all changes: