2 * Copyright 2004-2010, Jérôme Duval. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 * Original code from ZipOMatic by jonas.sundstrom@kirilla.com
6 #include "ExpanderThread.h"
15 #include <Messenger.h>
19 const char* ExpanderThreadName
= "ExpanderThread";
22 ExpanderThread::ExpanderThread(BMessage
* refs_message
, BMessenger
* messenger
)
24 GenericThread(ExpanderThreadName
, B_NORMAL_PRIORITY
, refs_message
),
25 fWindowMessenger(messenger
),
30 fExpanderOutput(NULL
),
33 SetDataStore(new BMessage(*refs_message
));
35 // prevents bug with B_SIMPLE_DATA
36 // (drag&drop messages)
40 ExpanderThread::~ExpanderThread()
42 delete fWindowMessenger
;
47 ExpanderThread::ThreadStartup()
49 status_t status
= B_OK
;
54 if ((status
= GetDataStore()->FindRef("srcRef", &srcRef
)) != B_OK
)
57 if ((status
= GetDataStore()->FindRef("destRef", &destRef
)) == B_OK
) {
62 if ((status
= GetDataStore()->FindString("cmd", &cmd
)) != B_OK
)
66 BString
pathString(path
.Path());
67 pathString
.CharacterEscape("\\\"$`", '\\');
68 pathString
.Prepend("\"");
69 pathString
.Append("\"");
70 cmd
.ReplaceAll("%s", pathString
.String());
73 const char** argv
= new const char * [argc
+ 1];
75 argv
[0] = strdup("/bin/sh");
76 argv
[1] = strdup("-c");
77 argv
[2] = strdup(cmd
.String());
80 fThreadId
= PipeCommand(argc
, argv
, fStdIn
, fStdOut
, fStdErr
);
87 // lower the command priority since it is a background task.
88 set_thread_priority(fThreadId
, B_LOW_PRIORITY
);
90 resume_thread(fThreadId
);
92 int flags
= fcntl(fStdOut
, F_GETFL
, 0);
94 fcntl(fStdOut
, F_SETFL
, flags
);
95 flags
= fcntl(fStdErr
, F_GETFL
, 0);
97 fcntl(fStdErr
, F_SETFL
, flags
);
99 fExpanderOutput
= fdopen(fStdOut
, "r");
100 fExpanderError
= fdopen(fStdErr
, "r");
107 ExpanderThread::ExecuteUnit(void)
109 // read output and error from command
112 BMessage
message('outp');
113 bool outputAdded
= false;
114 for (int32 i
= 0; i
< 50; i
++) {
115 char* output_string
= fgets(fExpanderOutputBuffer
, LINE_MAX
,
117 if (output_string
== NULL
)
120 message
.AddString("output", output_string
);
124 fWindowMessenger
->SendMessage(&message
);
126 if (feof(fExpanderOutput
))
129 char* error_string
= fgets(fExpanderOutputBuffer
, LINE_MAX
,
131 if (error_string
!= NULL
&& strcmp(error_string
, "\n")) {
132 BMessage
message('errp');
133 message
.AddString("error", error_string
);
134 fWindowMessenger
->SendMessage(&message
);
137 // streams are non blocking, sleep every 100ms
145 ExpanderThread::PushInput(BString text
)
148 write(fStdIn
, text
.String(), text
.Length());
153 ExpanderThread::ThreadShutdown(void)
164 ExpanderThread::ThreadStartupFailed(status_t status
)
166 fprintf(stderr
, "ExpanderThread::ThreadStartupFailed() : %s\n",
174 ExpanderThread::ExecuteUnitFailed(status_t status
)
177 // thread has finished, been quit or killed, we don't know
178 fWindowMessenger
->SendMessage(new BMessage('exit'));
180 // explicit error - communicate error to Window
181 fWindowMessenger
->SendMessage(new BMessage('exrr'));
189 ExpanderThread::ThreadShutdownFailed(status_t status
)
191 fprintf(stderr
, "ExpanderThread::ThreadShutdownFailed() %s\n",
197 ExpanderThread::ProcessRefs(BMessage
*msg
)
204 ExpanderThread::PipeCommand(int argc
, const char** argv
, int& in
, int& out
,
205 int& err
, const char** envp
)
207 // This function written by Peter Folk <pfolk@uni.uiuc.edu>
208 // and published in the BeDevTalk FAQ
209 // http://www.abisoft.com/faq/BeDevTalk_FAQ.html#FAQ-209
212 int old_out
= dup(1);
213 int old_err
= dup(2);
217 // create new pipe FDs as stdout, stderr
218 pipe(filedes
); dup2(filedes
[1], 1); close(filedes
[1]);
219 out
= filedes
[0]; // Read from out, taken from cmd's stdout
220 pipe(filedes
); dup2(filedes
[1], 2); close(filedes
[1]);
221 err
= filedes
[0]; // Read from err, taken from cmd's stderr
223 // taken from pty.cpp
224 // create a tty for stdin, as utilities don't generally use stdin
225 int master
= posix_openpt(O_RDWR
);
231 if (grantpt(master
) != 0 || unlockpt(master
) != 0
232 || (ttyName
= ptsname(master
)) == NULL
233 || (slave
= open(ttyName
, O_RDWR
| O_NOCTTY
)) < 0) {
250 if (ioctl(slave
, TIOCSCTTY
, NULL
) != 0)
257 execv(argv
[0], (char *const *)argv
);
268 close(1); dup(old_out
); close(old_out
);
269 close(2); dup(old_err
); close(old_err
);
271 // Theoretically I should do loads of error checking, but
272 // the calls aren't very likely to fail, and that would
273 // muddy up the example quite a bit. YMMV.
280 ExpanderThread::SuspendExternalExpander()
283 status_t status
= get_thread_info(fThreadId
, &info
);
286 return send_signal(-fThreadId
, SIGSTOP
);
293 ExpanderThread::ResumeExternalExpander()
296 status_t status
= get_thread_info(fThreadId
, &info
);
299 return send_signal(-fThreadId
, SIGCONT
);
306 ExpanderThread::InterruptExternalExpander()
309 status_t status
= get_thread_info(fThreadId
, &info
);
311 if (status
== B_OK
) {
312 status
= send_signal(-fThreadId
, SIGINT
);
313 WaitOnExternalExpander();
321 ExpanderThread::WaitOnExternalExpander()
324 status_t status
= get_thread_info(fThreadId
, &info
);
327 return wait_for_thread(fThreadId
, &status
);