Windows command.temp EACCES error.
[scilab.git] / scilab / modules / windows_tools / src / c / scilab_windows / spawncommand.c
1 /*
2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) INRIA - Allan CORNET
4 * Copyright (C) DIGITEO - 2010 - Allan CORNET
5 *
6 * This file must be used under the terms of the CeCILL.
7 * This source file is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution.  The terms
9 * are also available at
10 * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
11 *
12 */
13
14 /*--------------------------------------------------------------------------*/
15 #include <stdio.h>
16 #include "PATH_MAX.h"
17 #include "sci_malloc.h"
18 #include "FileExist.h"
19 #include "configvariable_interface.h"
20 #include "sci_tmpdir.h"
21 #include "spawncommand.h"
22 #include "os_string.h"
23 #include "charEncoding.h"
24 #include "getshortpathname.h"
25 #include "os_string.h"
26 /*--------------------------------------------------------------------------*/
27 #define BUFSIZE 4096
28 #define LF_STR "\n"
29 #define CR '\r'
30 #define LF '\n'
31 #define BLANK L' '
32 #define NOTPRINTABLE -96
33 #define EMPTY_CHAR L'\0'
34 #define CMDLINE_FORMAT_DETACHED L"%ls /A /C \"%ls\""
35 #define CMDLINE_FORMAT_NOTDETACHED L"%ls /A /C \"%ls && echo DOS > %ls\""
36 #define OUTPUT_CHECK_FILENAME_FORMAT L"%ls\\DOS.OK"
37 /*--------------------------------------------------------------------------*/
38 pipeinfo SCILAB_WINDOWS_IMPEXP pipeSpawnOut = {INVALID_HANDLE_VALUE, NULL, 0};
39 pipeinfo SCILAB_WINDOWS_IMPEXP pipeSpawnErr = {INVALID_HANDLE_VALUE, NULL, 0};
40 /*--------------------------------------------------------------------------*/
41 static int GetNumberOfLines(char *lines);
42 static BOOL removeEOL(char *_string);
43 static BOOL removeNotPrintableCharacters(char *_string);
44 static char *convertLine(char *_string, BOOL DetachProcess);
45 /*--------------------------------------------------------------------------*/
46 int spawncommand(wchar_t *command, BOOL DetachProcess)
47 {
48     wchar_t shellCmd[PATH_MAX];
49     wchar_t *CmdLine = NULL;
50
51     STARTUPINFOW si;
52     PROCESS_INFORMATION pi;
53     SECURITY_ATTRIBUTES sa;
54     DWORD threadID;
55     DWORD dwCreationFlags;
56     BOOL ok = FALSE;
57     HANDLE hProcess = NULL, h = NULL, pipeThreads[2];
58     DWORD ExitCode = 0;
59
60     if (wcscmp(command, L"") == 0)
61     {
62         // do nothing
63         pipeSpawnOut.NumberOfLines = 0;
64         pipeSpawnOut.OutputBuffer = NULL;
65
66         pipeSpawnErr.NumberOfLines = 0;
67         pipeSpawnErr.OutputBuffer = NULL;
68
69         return 1;
70     }
71
72     hProcess = GetCurrentProcess();
73
74     ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
75     ZeroMemory(&si, sizeof(STARTUPINFOW));
76     si.cb = sizeof(STARTUPINFO);
77     si.dwFlags   = STARTF_USESTDHANDLES;
78     si.hStdInput = INVALID_HANDLE_VALUE;
79
80     ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
81     sa.nLength = sizeof(SECURITY_ATTRIBUTES);
82     sa.lpSecurityDescriptor = NULL;
83     sa.bInheritHandle = TRUE;
84
85     /* create a non-inheritible pipe. */
86     CreatePipe(&pipeSpawnOut.pipe, &h, &sa, 0);
87
88     /* dupe the write side, make it inheritible, and close the original. */
89     DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput,
90                     0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
91
92     /* Same as above, but for the error side. */
93     CreatePipe(&pipeSpawnErr.pipe, &h, &sa, 0);
94     DuplicateHandle(hProcess, h, hProcess, &si.hStdError,
95                     0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
96
97     /* base command line */
98     GetEnvironmentVariableW(L"ComSpec", shellCmd, PATH_MAX);
99
100     if (DetachProcess)
101     {
102         int lenCmdLine = (int)(wcslen(shellCmd) + wcslen(command) + wcslen(CMDLINE_FORMAT_DETACHED));
103         CmdLine = (wchar_t*) MALLOC((lenCmdLine + 1) * sizeof(wchar_t));
104         os_swprintf(CmdLine, lenCmdLine, CMDLINE_FORMAT_DETACHED, shellCmd, command);
105
106         dwCreationFlags = DETACHED_PROCESS;
107     }
108     else
109     {
110         int lenCmdLine = 0;
111         wchar_t FileTMPDir[PATH_MAX + 16];
112         BOOL bConvert = FALSE;
113
114         wchar_t *TMPDirLong = getTMPDIRW();
115
116         os_swprintf(FileTMPDir, PATH_MAX + 16, OUTPUT_CHECK_FILENAME_FORMAT, TMPDirLong);
117         FREE(TMPDirLong);
118
119         if (FileExistW(FileTMPDir))
120         {
121             DeleteFileW(FileTMPDir);
122         }
123
124         lenCmdLine = (int)(wcslen(shellCmd) + wcslen(command) + wcslen(CMDLINE_FORMAT_NOTDETACHED) +
125                            wcslen(FileTMPDir));
126         CmdLine = (wchar_t*)MALLOC((lenCmdLine + 1) * sizeof(wchar_t));
127         os_swprintf(CmdLine, lenCmdLine, CMDLINE_FORMAT_NOTDETACHED, shellCmd, command, FileTMPDir);
128
129         dwCreationFlags = 0;
130     }
131
132     ok = CreateProcessW(
133              NULL,          /* Module name. */
134              CmdLine,       /* Command line. */
135              NULL,          /* Process handle not inheritable. */
136              NULL,          /* Thread handle not inheritable. */
137              TRUE,          /* yes, inherit handles. */
138              dwCreationFlags, /* No console for you. */
139              NULL,          /* Use parent's environment block. */
140              NULL,          /* Use parent's starting directory. */
141              &si,           /* Pointer to STARTUPINFO structure. */
142              &pi);          /* Pointer to PROCESS_INFORMATION structure. */
143
144     if (!ok)
145     {
146         return 2;
147     }
148
149     if (CmdLine)
150     {
151         FREE(CmdLine);
152         CmdLine = NULL;
153     }
154
155     /* close our references to the write handles that have now been inherited. */
156     CloseHandle(si.hStdOutput);
157     CloseHandle(si.hStdError);
158
159     WaitForInputIdle(pi.hProcess, 5000);
160     CloseHandle(pi.hThread);
161
162     /* start the pipe reader threads. */
163     pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &pipeSpawnOut, 0, &threadID);
164     pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &pipeSpawnErr, 0, &threadID);
165
166     /* block waiting for the process to end. */
167     WaitForSingleObject(pi.hProcess, INFINITE);
168
169     if ( GetExitCodeProcess(pi.hProcess, &ExitCode) == STILL_ACTIVE )
170     {
171         TerminateProcess(pi.hProcess, 0);
172     }
173
174     CloseHandle(pi.hProcess);
175
176     /* wait for our pipe to get done reading */
177     WaitForMultipleObjects(2, pipeThreads, TRUE, 500);
178     CloseHandle(pipeThreads[0]);
179     CloseHandle(pipeThreads[1]);
180
181     return ExitCode;
182 }
183 /*--------------------------------------------------------------------------*/
184 int ClosePipeInfo (pipeinfo pipe)
185 {
186     CloseHandle(pipe.pipe);
187     if (pipe.OutputBuffer)
188     {
189         FREE(pipe.OutputBuffer);
190         pipe.OutputBuffer = NULL;
191         pipe.NumberOfLines = 0;
192     }
193     return 0;
194 }
195 /*--------------------------------------------------------------------------*/
196 DWORD WINAPI ReadFromPipe (LPVOID args)
197 {
198     pipeinfo *pi = (pipeinfo *) args;
199     int readSoFar = 0;
200     DWORD dwRead;
201     BOOL moreOutput = TRUE;
202     unsigned char *op = NULL;
203
204     pi->OutputBuffer = (unsigned char*) MALLOC(BUFSIZE);
205     op = pi->OutputBuffer;
206
207     while (moreOutput)
208     {
209         BOOL bres = ReadFile( pi->pipe, op, BUFSIZE - 1, &dwRead, NULL);
210
211         moreOutput = bres || (dwRead != 0);
212
213         if (moreOutput)
214         {
215             readSoFar += dwRead;
216             pi->OutputBuffer  = (unsigned char*) REALLOC(pi->OutputBuffer , readSoFar + BUFSIZE);
217             op = pi->OutputBuffer + readSoFar;
218         }
219     }
220     *op = '\0';
221     return 0;
222 }
223 /*--------------------------------------------------------------------------*/
224 int GetNumberOfLines(char *lines)
225 {
226     int NumberOfLines = 0;
227     if (lines)
228     {
229         char *buffer = os_strdup(lines);
230         if (buffer)
231         {
232             int i = 0;
233             char *line = strtok(buffer, LF_STR);
234
235             while (line)
236             {
237                 line = strtok(NULL, LF_STR);
238                 i++;
239             }
240
241             NumberOfLines = i;
242
243             FREE(buffer);
244             buffer = NULL;
245         }
246         if (NumberOfLines == 0)
247         {
248             NumberOfLines = 1;
249         }
250     }
251     return NumberOfLines;
252 }
253 /*--------------------------------------------------------------------------*/
254 char **CreateOuput(pipeinfo *pipe, BOOL DetachProcess)
255 {
256     char **OuputStrings = NULL;
257     if (pipe)
258     {
259         if (pipe->OutputBuffer)
260         {
261             char *buffer = os_strdup(pipe->OutputBuffer);
262             if (buffer)
263             {
264                 pipe->NumberOfLines = GetNumberOfLines(buffer);
265                 if (pipe->NumberOfLines)
266                 {
267                     OuputStrings = (char**)MALLOC((pipe->NumberOfLines) * sizeof(char*));
268                     memset(OuputStrings, 0x00, sizeof(char*) * pipe->NumberOfLines);
269                     if (OuputStrings)
270                     {
271                         char *line = strtok(buffer, LF_STR);
272                         int i = 0;
273
274                         while (line)
275                         {
276                             OuputStrings[i] = convertLine(line, DetachProcess);
277                             line = strtok(NULL, LF_STR);
278                             i++;
279                             if (i > pipe->NumberOfLines)
280                             {
281                                 break;
282                             }
283                         }
284                     }
285                 }
286                 FREE(buffer);
287                 buffer = NULL;
288             }
289         }
290     }
291     return OuputStrings;
292 }
293 /*--------------------------------------------------------------------------*/
294 BOOL DetectDetachProcessInCommandLine(wchar_t *command)
295 {
296     BOOL bOK = FALSE;
297     if (command)
298     {
299         int i = (int)wcslen(command);
300         for (i = (int)wcslen(command) - 1; i >= 0; i--)
301         {
302             if (command[i] == BLANK)
303             {
304                 command[i] = EMPTY_CHAR;
305             }
306             else
307             {
308                 break;
309             }
310         }
311         i = (int)wcslen(command);
312         if ( (i > 0) && (command[i - 1] == L'&') )
313         {
314             bOK = TRUE;
315         }
316     }
317     return bOK;
318 }
319 /*--------------------------------------------------------------------------*/
320 BOOL removeEOL(char *_string)
321 {
322     if (_string)
323     {
324         int len = (int)strlen(_string);
325         if ( (_string[len - 1] == CR) || (_string[len - 1] == LF) )
326         {
327             _string[len - 1] = EMPTY_CHAR;
328             return TRUE;
329         }
330     }
331     return FALSE;
332 }
333 /*--------------------------------------------------------------------------*/
334 BOOL removeNotPrintableCharacters(char *_string)
335 {
336     if (_string)
337     {
338         int j = 0;
339         int len = (int)strlen(_string);
340         BOOL bRemove = FALSE;
341         for (j = 0; j < len; j++)
342         {
343             /* remove some no printable characters */
344             if (_string[j] == NOTPRINTABLE)
345             {
346                 _string[j] = BLANK;
347                 bRemove = TRUE;
348             }
349         }
350         return bRemove;
351     }
352     return FALSE;
353 }
354 /*--------------------------------------------------------------------------*/
355 char *convertLine(char *_string, BOOL DetachProcess)
356 {
357     char *convertedString = NULL;
358     if (_string)
359     {
360         convertedString = os_strdup(_string);
361
362         if (getScilabMode() == SCILAB_STD)
363         {
364             if ( (!DetachProcess) && (!IsValidUTF8(_string)) )
365             {
366                 // We need to add detection of ANSI characters
367                 // in this case we do not convert from Oem to char
368                 OemToChar(_string, convertedString);
369             }
370         }
371         else
372         {
373             // in -nw mode
374             // chcp 65001 (to switch cmd to UNICODE)
375             // and change font to Lucida (TrueType)
376             if ( (DetachProcess) && (!IsValidUTF8(_string)) )
377             {
378                 CharToOem(_string, convertedString);
379             }
380         }
381
382         removeEOL(convertedString);
383         removeNotPrintableCharacters(convertedString);
384
385     }
386     return convertedString;
387 }
388 /*--------------------------------------------------------------------------*/
389 int CallWindowsShell(char *command)
390 {
391     int returnedExitCode = -1;
392
393     wchar_t shellCmd[PATH_MAX];
394     wchar_t *CmdLine = NULL;
395     wchar_t * wcommand = NULL;
396     size_t iCmdSize = 0;
397
398     PROCESS_INFORMATION piProcInfo;
399     STARTUPINFOW siStartInfo;
400     SECURITY_ATTRIBUTES saAttr;
401
402     DWORD ExitCode = 0;
403
404     wchar_t *TMPDir = NULL;
405     wchar_t FileTMPDir[PATH_MAX];
406
407     if (strcmp(command, "") == 0)
408     {
409         // do nothing
410         return 1;
411     }
412
413     saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
414     saAttr.bInheritHandle = TRUE;
415     saAttr.lpSecurityDescriptor = NULL;
416
417     ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
418
419     ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
420     siStartInfo.cb = sizeof(STARTUPINFO);
421     siStartInfo.dwFlags      = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
422     siStartInfo.wShowWindow  = SW_HIDE;
423     siStartInfo.hStdInput = NULL;
424
425     siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
426     siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
427
428     GetEnvironmentVariableW(L"ComSpec", shellCmd, PATH_MAX);
429     TMPDir = getTMPDIRW();
430     os_swprintf(FileTMPDir, PATH_MAX, L"%ls\\DOS.OK", TMPDir);
431     if (TMPDir)
432     {
433         FREE(TMPDir);
434         TMPDir = NULL;
435     }
436
437     wcommand = to_wide_string(command);
438     iCmdSize = (wcslen(shellCmd) + wcslen(wcommand) + wcslen(FileTMPDir) + wcslen(L"%ls /a /c \"%ls\" && echo DOS>%ls") + 1);
439     CmdLine = (wchar_t*)MALLOC(iCmdSize * sizeof(wchar_t));
440     os_swprintf(CmdLine, iCmdSize, L"%ls /a /c \"%ls\" && echo DOS>%ls", shellCmd, wcommand, FileTMPDir);
441
442     if (CreateProcessW(NULL, CmdLine, NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo))
443     {
444         WaitForSingleObject(piProcInfo.hProcess, INFINITE);
445
446         if ( GetExitCodeProcess(piProcInfo.hProcess, &ExitCode) == STILL_ACTIVE )
447         {
448             TerminateProcess(piProcInfo.hProcess, 0);
449         }
450
451         CloseHandle(piProcInfo.hProcess);
452
453         if (CmdLine)
454         {
455             FREE(CmdLine);
456             CmdLine = NULL;
457         }
458
459         if (FileExistW(FileTMPDir))
460         {
461             DeleteFileW(FileTMPDir);
462         }
463
464         returnedExitCode = (int)ExitCode;
465     }
466     else
467     {
468         CloseHandle(piProcInfo.hProcess);
469         if (CmdLine)
470         {
471             FREE(CmdLine);
472             CmdLine = NULL;
473         }
474     }
475     return returnedExitCode;
476 }
477 /*--------------------------------------------------------------------------*/
478 int CallWindowsShellW(wchar_t* _pstCommand)
479 {
480     int returnedExitCode = -1;
481
482     wchar_t shellCmd[PATH_MAX];
483     wchar_t *CmdLine = NULL;
484     size_t iCmdSize = 0;
485
486     PROCESS_INFORMATION piProcInfo;
487     STARTUPINFOW siStartInfo;
488     SECURITY_ATTRIBUTES saAttr;
489
490     DWORD ExitCode = 0;
491
492     wchar_t *TMPDir = NULL;
493     wchar_t FileTMPDir[PATH_MAX];
494
495     if (wcscmp(_pstCommand, L"") == 0)
496     {
497         // do nothing
498         return 1;
499     }
500
501     ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
502
503     ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
504     siStartInfo.cb              = sizeof(STARTUPINFO);
505     siStartInfo.dwFlags         = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
506     siStartInfo.wShowWindow     = SW_HIDE;
507     siStartInfo.hStdInput       = NULL;
508
509     siStartInfo.hStdOutput      = GetStdHandle(STD_OUTPUT_HANDLE);
510     siStartInfo.hStdError       = GetStdHandle(STD_ERROR_HANDLE);
511
512     GetEnvironmentVariableW(L"ComSpec", shellCmd, PATH_MAX);
513     TMPDir = getTMPDIRW();
514     os_swprintf(FileTMPDir, PATH_MAX, L"%ls\\DOS.OK", TMPDir);
515     if (TMPDir)
516     {
517         FREE(TMPDir);
518         TMPDir = NULL;
519     }
520
521     iCmdSize    = (wcslen(shellCmd) + wcslen(_pstCommand) + wcslen(FileTMPDir) + wcslen(L"%ls /a /c \"%ls\" && echo DOS>%ls") + 1);
522     CmdLine     = (wchar_t*)MALLOC(iCmdSize * sizeof(wchar_t));
523     os_swprintf(CmdLine, iCmdSize, L"%ls /a /c \"%ls\" && echo DOS>%ls", shellCmd, _pstCommand, FileTMPDir);
524
525     if (CreateProcessW(NULL, CmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &siStartInfo, &piProcInfo))
526     {
527         WaitForSingleObject(piProcInfo.hProcess, INFINITE);
528
529         if (GetExitCodeProcess(piProcInfo.hProcess, &ExitCode) == STILL_ACTIVE)
530         {
531             TerminateProcess(piProcInfo.hProcess, 0);
532         }
533
534         CloseHandle(piProcInfo.hProcess);
535
536         if (CmdLine)
537         {
538             FREE(CmdLine);
539             CmdLine = NULL;
540         }
541
542         if (FileExistW(FileTMPDir))
543         {
544             DeleteFileW(FileTMPDir);
545         }
546
547         returnedExitCode = (int)ExitCode;
548     }
549     else
550     {
551         CloseHandle(piProcInfo.hProcess);
552         if (CmdLine)
553         {
554             FREE(CmdLine);
555             CmdLine = NULL;
556         }
557     }
558     return returnedExitCode;
559 }
560 /*--------------------------------------------------------------------------*/