filedialog 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.github/workflows/gempush.yml +31 -0
- data/.gitignore +13 -0
- data/.gitmodules +3 -0
- data/.rubocop.yml +19 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +30 -0
- data/LICENSE +674 -0
- data/README.md +22 -0
- data/Rakefile +35 -0
- data/bin/console +25 -0
- data/deps/filedialogbuilddeps.rb +49 -0
- data/deps/nativefiledialog/.github/ISSUE_TEMPLATE/bug_report.md +32 -0
- data/deps/nativefiledialog/.gitignore +181 -0
- data/deps/nativefiledialog/LICENSE +16 -0
- data/deps/nativefiledialog/README.md +180 -0
- data/deps/nativefiledialog/build/dont_run_premake.txt +1 -0
- data/deps/nativefiledialog/build/gmake_linux/Makefile +101 -0
- data/deps/nativefiledialog/build/gmake_linux/nfd.make +192 -0
- data/deps/nativefiledialog/build/gmake_linux/test_opendialog.make +188 -0
- data/deps/nativefiledialog/build/gmake_linux/test_opendialogmultiple.make +188 -0
- data/deps/nativefiledialog/build/gmake_linux/test_pickfolder.make +188 -0
- data/deps/nativefiledialog/build/gmake_linux/test_savedialog.make +188 -0
- data/deps/nativefiledialog/build/gmake_linux_zenity/Makefile +101 -0
- data/deps/nativefiledialog/build/gmake_linux_zenity/nfd.make +192 -0
- data/deps/nativefiledialog/build/gmake_linux_zenity/test_opendialog.make +188 -0
- data/deps/nativefiledialog/build/gmake_linux_zenity/test_opendialogmultiple.make +188 -0
- data/deps/nativefiledialog/build/gmake_linux_zenity/test_pickfolder.make +188 -0
- data/deps/nativefiledialog/build/gmake_linux_zenity/test_savedialog.make +188 -0
- data/deps/nativefiledialog/build/gmake_macosx/Makefile +85 -0
- data/deps/nativefiledialog/build/gmake_macosx/nfd.make +154 -0
- data/deps/nativefiledialog/build/gmake_macosx/test_opendialog.make +150 -0
- data/deps/nativefiledialog/build/gmake_macosx/test_opendialogmultiple.make +150 -0
- data/deps/nativefiledialog/build/gmake_macosx/test_pickfolder.make +150 -0
- data/deps/nativefiledialog/build/gmake_macosx/test_savedialog.make +150 -0
- data/deps/nativefiledialog/build/gmake_windows/Makefile +101 -0
- data/deps/nativefiledialog/build/gmake_windows/nfd.make +192 -0
- data/deps/nativefiledialog/build/gmake_windows/test_opendialog.make +188 -0
- data/deps/nativefiledialog/build/gmake_windows/test_opendialogmultiple.make +188 -0
- data/deps/nativefiledialog/build/gmake_windows/test_pickfolder.make +188 -0
- data/deps/nativefiledialog/build/gmake_windows/test_savedialog.make +188 -0
- data/deps/nativefiledialog/build/premake5.lua +265 -0
- data/deps/nativefiledialog/build/vs2010/NativeFileDialog.sln +78 -0
- data/deps/nativefiledialog/build/vs2010/nfd.vcxproj +168 -0
- data/deps/nativefiledialog/build/vs2010/nfd.vcxproj.filters +20 -0
- data/deps/nativefiledialog/build/vs2010/test_opendialog.vcxproj +182 -0
- data/deps/nativefiledialog/build/vs2010/test_opendialogmultiple.vcxproj +182 -0
- data/deps/nativefiledialog/build/vs2010/test_pickfolder.vcxproj +182 -0
- data/deps/nativefiledialog/build/vs2010/test_savedialog.vcxproj +182 -0
- data/deps/nativefiledialog/build/xcode4/NativeFileDialog.xcworkspace/contents.xcworkspacedata +19 -0
- data/deps/nativefiledialog/build/xcode4/nfd.xcodeproj/project.pbxproj +228 -0
- data/deps/nativefiledialog/build/xcode4/test_opendialog.xcodeproj/project.pbxproj +294 -0
- data/deps/nativefiledialog/build/xcode4/test_opendialogmultiple.xcodeproj/project.pbxproj +294 -0
- data/deps/nativefiledialog/build/xcode4/test_pickfolder.xcodeproj/project.pbxproj +294 -0
- data/deps/nativefiledialog/build/xcode4/test_savedialog.xcodeproj/project.pbxproj +294 -0
- data/deps/nativefiledialog/docs/build.md +39 -0
- data/deps/nativefiledialog/docs/contributing.md +25 -0
- data/deps/nativefiledialog/screens/open_cocoa.png +0 -0
- data/deps/nativefiledialog/screens/open_gtk3.png +0 -0
- data/deps/nativefiledialog/screens/open_win.png +0 -0
- data/deps/nativefiledialog/src/common.h +21 -0
- data/deps/nativefiledialog/src/include/nfd.h +74 -0
- data/deps/nativefiledialog/src/nfd_cocoa.m +286 -0
- data/deps/nativefiledialog/src/nfd_common.c +142 -0
- data/deps/nativefiledialog/src/nfd_common.h +39 -0
- data/deps/nativefiledialog/src/nfd_gtk.c +379 -0
- data/deps/nativefiledialog/src/nfd_win.cpp +762 -0
- data/deps/nativefiledialog/src/nfd_zenity.c +307 -0
- data/deps/nativefiledialog/src/simple_exec.h +218 -0
- data/deps/nativefiledialog/test/test_opendialog.c +29 -0
- data/deps/nativefiledialog/test/test_opendialogmultiple.c +32 -0
- data/deps/nativefiledialog/test/test_pickfolder.c +29 -0
- data/deps/nativefiledialog/test/test_savedialog.c +28 -0
- data/ext/filedialog/extconf.rb +58 -0
- data/ext/filedialog/filedialog.c +118 -0
- data/filedialog.gemspec +48 -0
- data/lib/filedialog.rb +50 -0
- data/lib/filedialog/version.rb +5 -0
- metadata +137 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Native File Dialog
|
|
3
|
+
|
|
4
|
+
http://www.frogtoss.com/labs
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
#include <stdio.h>
|
|
8
|
+
#include <assert.h>
|
|
9
|
+
#include <string.h>
|
|
10
|
+
#include "nfd.h"
|
|
11
|
+
#include "nfd_common.h"
|
|
12
|
+
|
|
13
|
+
#define SIMPLE_EXEC_IMPLEMENTATION
|
|
14
|
+
#include "simple_exec.h"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
const char NO_ZENITY_MSG[] = "zenity not installed";
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
static void AddTypeToFilterName( const char *typebuf, char *filterName, size_t bufsize )
|
|
21
|
+
{
|
|
22
|
+
size_t len = strlen(filterName);
|
|
23
|
+
if( len > 0 )
|
|
24
|
+
strncat( filterName, " *.", bufsize - len - 1 );
|
|
25
|
+
else
|
|
26
|
+
strncat( filterName, "--file-filter=*.", bufsize - len - 1 );
|
|
27
|
+
|
|
28
|
+
len = strlen(filterName);
|
|
29
|
+
strncat( filterName, typebuf, bufsize - len - 1 );
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
static void AddFiltersToCommandArgs(char** commandArgs, int commandArgsLen, const char *filterList )
|
|
33
|
+
{
|
|
34
|
+
char typebuf[NFD_MAX_STRLEN] = {0};
|
|
35
|
+
const char *p_filterList = filterList;
|
|
36
|
+
char *p_typebuf = typebuf;
|
|
37
|
+
char filterName[NFD_MAX_STRLEN] = {0};
|
|
38
|
+
int i;
|
|
39
|
+
|
|
40
|
+
if ( !filterList || strlen(filterList) == 0 )
|
|
41
|
+
return;
|
|
42
|
+
|
|
43
|
+
while ( 1 )
|
|
44
|
+
{
|
|
45
|
+
|
|
46
|
+
if ( NFDi_IsFilterSegmentChar(*p_filterList) )
|
|
47
|
+
{
|
|
48
|
+
char typebufWildcard[NFD_MAX_STRLEN];
|
|
49
|
+
/* add another type to the filter */
|
|
50
|
+
assert( strlen(typebuf) > 0 );
|
|
51
|
+
assert( strlen(typebuf) < NFD_MAX_STRLEN-1 );
|
|
52
|
+
|
|
53
|
+
snprintf( typebufWildcard, NFD_MAX_STRLEN, "*.%s", typebuf );
|
|
54
|
+
|
|
55
|
+
AddTypeToFilterName( typebuf, filterName, NFD_MAX_STRLEN );
|
|
56
|
+
|
|
57
|
+
p_typebuf = typebuf;
|
|
58
|
+
memset( typebuf, 0, sizeof(char) * NFD_MAX_STRLEN );
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if ( *p_filterList == ';' || *p_filterList == '\0' )
|
|
62
|
+
{
|
|
63
|
+
/* end of filter -- add it to the dialog */
|
|
64
|
+
|
|
65
|
+
for(i = 0; commandArgs[i] != NULL && i < commandArgsLen; i++);
|
|
66
|
+
|
|
67
|
+
commandArgs[i] = strdup(filterName);
|
|
68
|
+
|
|
69
|
+
filterName[0] = '\0';
|
|
70
|
+
|
|
71
|
+
if ( *p_filterList == '\0' )
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if ( !NFDi_IsFilterSegmentChar( *p_filterList ) )
|
|
76
|
+
{
|
|
77
|
+
*p_typebuf = *p_filterList;
|
|
78
|
+
p_typebuf++;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
p_filterList++;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* always append a wildcard option to the end*/
|
|
85
|
+
|
|
86
|
+
for(i = 0; commandArgs[i] != NULL && i < commandArgsLen; i++);
|
|
87
|
+
|
|
88
|
+
commandArgs[i] = strdup("--file-filter=*.*");
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
static nfdresult_t ZenityCommon(char** command, int commandLen, const char* defaultPath, const char* filterList, char** stdOut)
|
|
92
|
+
{
|
|
93
|
+
if(defaultPath != NULL)
|
|
94
|
+
{
|
|
95
|
+
char* prefix = "--filename=";
|
|
96
|
+
int len = strlen(prefix) + strlen(defaultPath) + 1;
|
|
97
|
+
|
|
98
|
+
char* tmp = (char*) calloc(len, 1);
|
|
99
|
+
strcat(tmp, prefix);
|
|
100
|
+
strcat(tmp, defaultPath);
|
|
101
|
+
|
|
102
|
+
int i;
|
|
103
|
+
for(i = 0; command[i] != NULL && i < commandLen; i++);
|
|
104
|
+
|
|
105
|
+
command[i] = tmp;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
AddFiltersToCommandArgs(command, commandLen, filterList);
|
|
109
|
+
|
|
110
|
+
int byteCount = 0;
|
|
111
|
+
int exitCode = 0;
|
|
112
|
+
int processInvokeError = runCommandArray(stdOut, &byteCount, &exitCode, 0, command);
|
|
113
|
+
|
|
114
|
+
for(int i = 0; command[i] != NULL && i < commandLen; i++)
|
|
115
|
+
free(command[i]);
|
|
116
|
+
|
|
117
|
+
nfdresult_t result = NFD_OKAY;
|
|
118
|
+
|
|
119
|
+
if(processInvokeError == COMMAND_NOT_FOUND)
|
|
120
|
+
{
|
|
121
|
+
NFDi_SetError(NO_ZENITY_MSG);
|
|
122
|
+
result = NFD_ERROR;
|
|
123
|
+
}
|
|
124
|
+
else
|
|
125
|
+
{
|
|
126
|
+
if(exitCode == 1)
|
|
127
|
+
result = NFD_CANCEL;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
static nfdresult_t AllocPathSet(char* zenityList, nfdpathset_t *pathSet )
|
|
135
|
+
{
|
|
136
|
+
assert(zenityList);
|
|
137
|
+
assert(pathSet);
|
|
138
|
+
|
|
139
|
+
size_t len = strlen(zenityList) + 1;
|
|
140
|
+
pathSet->buf = NFDi_Malloc(len);
|
|
141
|
+
|
|
142
|
+
int numEntries = 1;
|
|
143
|
+
|
|
144
|
+
for(size_t i = 0; i < len; i++)
|
|
145
|
+
{
|
|
146
|
+
char ch = zenityList[i];
|
|
147
|
+
|
|
148
|
+
if(ch == '|')
|
|
149
|
+
{
|
|
150
|
+
numEntries++;
|
|
151
|
+
ch = '\0';
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
pathSet->buf[i] = ch;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
pathSet->count = numEntries;
|
|
158
|
+
assert( pathSet->count > 0 );
|
|
159
|
+
|
|
160
|
+
pathSet->indices = NFDi_Malloc( sizeof(size_t)*pathSet->count );
|
|
161
|
+
|
|
162
|
+
int entry = 0;
|
|
163
|
+
pathSet->indices[0] = 0;
|
|
164
|
+
for(size_t i = 0; i < len; i++)
|
|
165
|
+
{
|
|
166
|
+
char ch = zenityList[i];
|
|
167
|
+
|
|
168
|
+
if(ch == '|')
|
|
169
|
+
{
|
|
170
|
+
entry++;
|
|
171
|
+
pathSet->indices[entry] = i + 1;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return NFD_OKAY;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* public */
|
|
179
|
+
|
|
180
|
+
nfdresult_t NFD_OpenDialog( const char *filterList,
|
|
181
|
+
const nfdchar_t *defaultPath,
|
|
182
|
+
nfdchar_t **outPath )
|
|
183
|
+
{
|
|
184
|
+
int commandLen = 100;
|
|
185
|
+
char* command[commandLen];
|
|
186
|
+
memset(command, 0, commandLen * sizeof(char*));
|
|
187
|
+
|
|
188
|
+
command[0] = strdup("zenity");
|
|
189
|
+
command[1] = strdup("--file-selection");
|
|
190
|
+
command[2] = strdup("--title=Open File");
|
|
191
|
+
|
|
192
|
+
char* stdOut = NULL;
|
|
193
|
+
nfdresult_t result = ZenityCommon(command, commandLen, defaultPath, filterList, &stdOut);
|
|
194
|
+
|
|
195
|
+
if(stdOut != NULL)
|
|
196
|
+
{
|
|
197
|
+
size_t len = strlen(stdOut);
|
|
198
|
+
*outPath = NFDi_Malloc(len);
|
|
199
|
+
memcpy(*outPath, stdOut, len);
|
|
200
|
+
(*outPath)[len-1] = '\0'; // trim out the final \n with a null terminator
|
|
201
|
+
free(stdOut);
|
|
202
|
+
}
|
|
203
|
+
else
|
|
204
|
+
{
|
|
205
|
+
*outPath = NULL;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return result;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
nfdresult_t NFD_OpenDialogMultiple( const nfdchar_t *filterList,
|
|
213
|
+
const nfdchar_t *defaultPath,
|
|
214
|
+
nfdpathset_t *outPaths )
|
|
215
|
+
{
|
|
216
|
+
int commandLen = 100;
|
|
217
|
+
char* command[commandLen];
|
|
218
|
+
memset(command, 0, commandLen * sizeof(char*));
|
|
219
|
+
|
|
220
|
+
command[0] = strdup("zenity");
|
|
221
|
+
command[1] = strdup("--file-selection");
|
|
222
|
+
command[2] = strdup("--title=Open Files");
|
|
223
|
+
command[3] = strdup("--multiple");
|
|
224
|
+
|
|
225
|
+
char* stdOut = NULL;
|
|
226
|
+
nfdresult_t result = ZenityCommon(command, commandLen, defaultPath, filterList, &stdOut);
|
|
227
|
+
|
|
228
|
+
if(stdOut != NULL)
|
|
229
|
+
{
|
|
230
|
+
size_t len = strlen(stdOut);
|
|
231
|
+
stdOut[len-1] = '\0'; // remove trailing newline
|
|
232
|
+
|
|
233
|
+
if ( AllocPathSet( stdOut, outPaths ) == NFD_ERROR )
|
|
234
|
+
result = NFD_ERROR;
|
|
235
|
+
|
|
236
|
+
free(stdOut);
|
|
237
|
+
}
|
|
238
|
+
else
|
|
239
|
+
{
|
|
240
|
+
result = NFD_ERROR;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return result;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
nfdresult_t NFD_SaveDialog( const nfdchar_t *filterList,
|
|
247
|
+
const nfdchar_t *defaultPath,
|
|
248
|
+
nfdchar_t **outPath )
|
|
249
|
+
{
|
|
250
|
+
int commandLen = 100;
|
|
251
|
+
char* command[commandLen];
|
|
252
|
+
memset(command, 0, commandLen * sizeof(char*));
|
|
253
|
+
|
|
254
|
+
command[0] = strdup("zenity");
|
|
255
|
+
command[1] = strdup("--file-selection");
|
|
256
|
+
command[2] = strdup("--title=Save File");
|
|
257
|
+
command[3] = strdup("--save");
|
|
258
|
+
|
|
259
|
+
char* stdOut = NULL;
|
|
260
|
+
nfdresult_t result = ZenityCommon(command, commandLen, defaultPath, filterList, &stdOut);
|
|
261
|
+
|
|
262
|
+
if(stdOut != NULL)
|
|
263
|
+
{
|
|
264
|
+
size_t len = strlen(stdOut);
|
|
265
|
+
*outPath = NFDi_Malloc(len);
|
|
266
|
+
memcpy(*outPath, stdOut, len);
|
|
267
|
+
(*outPath)[len-1] = '\0'; // trim out the final \n with a null terminator
|
|
268
|
+
free(stdOut);
|
|
269
|
+
}
|
|
270
|
+
else
|
|
271
|
+
{
|
|
272
|
+
*outPath = NULL;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return result;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
nfdresult_t NFD_PickFolder(const nfdchar_t *defaultPath,
|
|
279
|
+
nfdchar_t **outPath)
|
|
280
|
+
{
|
|
281
|
+
int commandLen = 100;
|
|
282
|
+
char* command[commandLen];
|
|
283
|
+
memset(command, 0, commandLen * sizeof(char*));
|
|
284
|
+
|
|
285
|
+
command[0] = strdup("zenity");
|
|
286
|
+
command[1] = strdup("--file-selection");
|
|
287
|
+
command[2] = strdup("--directory");
|
|
288
|
+
command[3] = strdup("--title=Select folder");
|
|
289
|
+
|
|
290
|
+
char* stdOut = NULL;
|
|
291
|
+
nfdresult_t result = ZenityCommon(command, commandLen, defaultPath, "", &stdOut);
|
|
292
|
+
|
|
293
|
+
if(stdOut != NULL)
|
|
294
|
+
{
|
|
295
|
+
size_t len = strlen(stdOut);
|
|
296
|
+
*outPath = NFDi_Malloc(len);
|
|
297
|
+
memcpy(*outPath, stdOut, len);
|
|
298
|
+
(*outPath)[len-1] = '\0'; // trim out the final \n with a null terminator
|
|
299
|
+
free(stdOut);
|
|
300
|
+
}
|
|
301
|
+
else
|
|
302
|
+
{
|
|
303
|
+
*outPath = NULL;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return result;
|
|
307
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
// copied from: https://github.com/wheybags/simple_exec/blob/5a74c507c4ce1b2bb166177ead4cca7cfa23cb35/simple_exec.h
|
|
2
|
+
|
|
3
|
+
// simple_exec.h, single header library to run external programs + retrieve their status code and output (unix only for now)
|
|
4
|
+
//
|
|
5
|
+
// do this:
|
|
6
|
+
// #define SIMPLE_EXEC_IMPLEMENTATION
|
|
7
|
+
// before you include this file in *one* C or C++ file to create the implementation.
|
|
8
|
+
// i.e. it should look like this:
|
|
9
|
+
// #define SIMPLE_EXEC_IMPLEMENTATION
|
|
10
|
+
// #include "simple_exec.h"
|
|
11
|
+
|
|
12
|
+
#ifndef SIMPLE_EXEC_H
|
|
13
|
+
#define SIMPLE_EXEC_H
|
|
14
|
+
|
|
15
|
+
int runCommand(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* command, ...);
|
|
16
|
+
int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* const* allArgs);
|
|
17
|
+
|
|
18
|
+
#endif // SIMPLE_EXEC_H
|
|
19
|
+
|
|
20
|
+
#ifdef SIMPLE_EXEC_IMPLEMENTATION
|
|
21
|
+
|
|
22
|
+
#include <stdio.h>
|
|
23
|
+
#include <stdlib.h>
|
|
24
|
+
#include <string.h>
|
|
25
|
+
#include <unistd.h>
|
|
26
|
+
#include <assert.h>
|
|
27
|
+
#include <sys/wait.h>
|
|
28
|
+
#include <stdarg.h>
|
|
29
|
+
#include <fcntl.h>
|
|
30
|
+
|
|
31
|
+
#define release_assert(exp) { if (!(exp)) { abort(); } }
|
|
32
|
+
|
|
33
|
+
enum PIPE_FILE_DESCRIPTORS
|
|
34
|
+
{
|
|
35
|
+
READ_FD = 0,
|
|
36
|
+
WRITE_FD = 1
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
enum RUN_COMMAND_ERROR
|
|
40
|
+
{
|
|
41
|
+
COMMAND_RAN_OK = 0,
|
|
42
|
+
COMMAND_NOT_FOUND = 1
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
int runCommandArray(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* const* allArgs)
|
|
46
|
+
{
|
|
47
|
+
// adapted from: https://stackoverflow.com/a/479103
|
|
48
|
+
|
|
49
|
+
int bufferSize = 256;
|
|
50
|
+
char buffer[bufferSize + 1];
|
|
51
|
+
|
|
52
|
+
int dataReadFromChildDefaultSize = bufferSize * 5;
|
|
53
|
+
int dataReadFromChildSize = dataReadFromChildDefaultSize;
|
|
54
|
+
int dataReadFromChildUsed = 0;
|
|
55
|
+
char* dataReadFromChild = (char*)malloc(dataReadFromChildSize);
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
int parentToChild[2];
|
|
59
|
+
release_assert(pipe(parentToChild) == 0);
|
|
60
|
+
|
|
61
|
+
int childToParent[2];
|
|
62
|
+
release_assert(pipe(childToParent) == 0);
|
|
63
|
+
|
|
64
|
+
int errPipe[2];
|
|
65
|
+
release_assert(pipe(errPipe) == 0);
|
|
66
|
+
|
|
67
|
+
pid_t pid;
|
|
68
|
+
switch( pid = fork() )
|
|
69
|
+
{
|
|
70
|
+
case -1:
|
|
71
|
+
{
|
|
72
|
+
release_assert(0 && "Fork failed");
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
case 0: // child
|
|
77
|
+
{
|
|
78
|
+
release_assert(dup2(parentToChild[READ_FD ], STDIN_FILENO ) != -1);
|
|
79
|
+
release_assert(dup2(childToParent[WRITE_FD], STDOUT_FILENO) != -1);
|
|
80
|
+
|
|
81
|
+
if(includeStdErr)
|
|
82
|
+
{
|
|
83
|
+
release_assert(dup2(childToParent[WRITE_FD], STDERR_FILENO) != -1);
|
|
84
|
+
}
|
|
85
|
+
else
|
|
86
|
+
{
|
|
87
|
+
int devNull = open("/dev/null", O_WRONLY);
|
|
88
|
+
release_assert(dup2(devNull, STDERR_FILENO) != -1);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// unused
|
|
92
|
+
release_assert(close(parentToChild[WRITE_FD]) == 0);
|
|
93
|
+
release_assert(close(childToParent[READ_FD ]) == 0);
|
|
94
|
+
release_assert(close(errPipe[READ_FD]) == 0);
|
|
95
|
+
|
|
96
|
+
const char* command = allArgs[0];
|
|
97
|
+
execvp(command, allArgs);
|
|
98
|
+
|
|
99
|
+
char err = 1;
|
|
100
|
+
ssize_t result = write(errPipe[WRITE_FD], &err, 1);
|
|
101
|
+
release_assert(result != -1);
|
|
102
|
+
|
|
103
|
+
close(errPipe[WRITE_FD]);
|
|
104
|
+
close(parentToChild[READ_FD]);
|
|
105
|
+
close(childToParent[WRITE_FD]);
|
|
106
|
+
|
|
107
|
+
exit(0);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
default: // parent
|
|
112
|
+
{
|
|
113
|
+
// unused
|
|
114
|
+
release_assert(close(parentToChild[READ_FD]) == 0);
|
|
115
|
+
release_assert(close(childToParent[WRITE_FD]) == 0);
|
|
116
|
+
release_assert(close(errPipe[WRITE_FD]) == 0);
|
|
117
|
+
|
|
118
|
+
while(1)
|
|
119
|
+
{
|
|
120
|
+
ssize_t bytesRead = 0;
|
|
121
|
+
switch(bytesRead = read(childToParent[READ_FD], buffer, bufferSize))
|
|
122
|
+
{
|
|
123
|
+
case 0: // End-of-File, or non-blocking read.
|
|
124
|
+
{
|
|
125
|
+
int status = 0;
|
|
126
|
+
release_assert(waitpid(pid, &status, 0) == pid);
|
|
127
|
+
|
|
128
|
+
// done with these now
|
|
129
|
+
release_assert(close(parentToChild[WRITE_FD]) == 0);
|
|
130
|
+
release_assert(close(childToParent[READ_FD]) == 0);
|
|
131
|
+
|
|
132
|
+
char errChar = 0;
|
|
133
|
+
ssize_t result = read(errPipe[READ_FD], &errChar, 1);
|
|
134
|
+
release_assert(result != -1);
|
|
135
|
+
close(errPipe[READ_FD]);
|
|
136
|
+
|
|
137
|
+
if(errChar)
|
|
138
|
+
{
|
|
139
|
+
free(dataReadFromChild);
|
|
140
|
+
return COMMAND_NOT_FOUND;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// free any un-needed memory with realloc + add a null terminator for convenience
|
|
144
|
+
dataReadFromChild = (char*)realloc(dataReadFromChild, dataReadFromChildUsed + 1);
|
|
145
|
+
dataReadFromChild[dataReadFromChildUsed] = '\0';
|
|
146
|
+
|
|
147
|
+
if(stdOut != NULL)
|
|
148
|
+
*stdOut = dataReadFromChild;
|
|
149
|
+
else
|
|
150
|
+
free(dataReadFromChild);
|
|
151
|
+
|
|
152
|
+
if(stdOutByteCount != NULL)
|
|
153
|
+
*stdOutByteCount = dataReadFromChildUsed;
|
|
154
|
+
if(returnCode != NULL)
|
|
155
|
+
*returnCode = WEXITSTATUS(status);
|
|
156
|
+
|
|
157
|
+
return COMMAND_RAN_OK;
|
|
158
|
+
}
|
|
159
|
+
case -1:
|
|
160
|
+
{
|
|
161
|
+
release_assert(0 && "read() failed");
|
|
162
|
+
break;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
default:
|
|
166
|
+
{
|
|
167
|
+
if(dataReadFromChildUsed + bytesRead + 1 >= dataReadFromChildSize)
|
|
168
|
+
{
|
|
169
|
+
dataReadFromChildSize += dataReadFromChildDefaultSize;
|
|
170
|
+
dataReadFromChild = (char*)realloc(dataReadFromChild, dataReadFromChildSize);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
memcpy(dataReadFromChild + dataReadFromChildUsed, buffer, bytesRead);
|
|
174
|
+
dataReadFromChildUsed += bytesRead;
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
int runCommand(char** stdOut, int* stdOutByteCount, int* returnCode, int includeStdErr, char* command, ...)
|
|
184
|
+
{
|
|
185
|
+
va_list vl;
|
|
186
|
+
va_start(vl, command);
|
|
187
|
+
|
|
188
|
+
char* currArg = NULL;
|
|
189
|
+
|
|
190
|
+
int allArgsInitialSize = 16;
|
|
191
|
+
int allArgsSize = allArgsInitialSize;
|
|
192
|
+
char** allArgs = (char**)malloc(sizeof(char*) * allArgsSize);
|
|
193
|
+
allArgs[0] = command;
|
|
194
|
+
|
|
195
|
+
int i = 1;
|
|
196
|
+
do
|
|
197
|
+
{
|
|
198
|
+
currArg = va_arg(vl, char*);
|
|
199
|
+
allArgs[i] = currArg;
|
|
200
|
+
|
|
201
|
+
i++;
|
|
202
|
+
|
|
203
|
+
if(i >= allArgsSize)
|
|
204
|
+
{
|
|
205
|
+
allArgsSize += allArgsInitialSize;
|
|
206
|
+
allArgs = (char**)realloc(allArgs, sizeof(char*) * allArgsSize);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
} while(currArg != NULL);
|
|
210
|
+
|
|
211
|
+
va_end(vl);
|
|
212
|
+
|
|
213
|
+
int retval = runCommandArray(stdOut, stdOutByteCount, returnCode, includeStdErr, allArgs);
|
|
214
|
+
free(allArgs);
|
|
215
|
+
return retval;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
#endif //SIMPLE_EXEC_IMPLEMENTATION
|