1/*
2 * This file is a part of QTerminal - http://gitorious.org/qterminal
3 *
4 * This file was un-linked from KDE and modified
5 * by Maxim Bourmistrov <maxim@unixconn.com>
6 *
7 */
8
9/*
10 This file is part of the KDE libraries
11
12 Copyright (C) 2007 Oswald Buddenhagen <ossi@kde.org>
13
14 This library is free software; you can redistribute it and/or
15 modify it under the terms of the GNU Library General Public
16 License as published by the Free Software Foundation; either
17 version 2 of the License, or (at your option) any later version.
18
19 This library is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 Library General Public License for more details.
23
24 You should have received a copy of the GNU Library General Public License
25 along with this library; see the file COPYING.LIB. If not, write to
26 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
27 Boston, MA 02110-1301, USA.
28*/
29
30#include "kprocess.h"
31
32#include <qfile.h>
33
34#ifdef Q_OS_WIN
35# include <windows.h>
36#else
37# include <unistd.h>
38# include <errno.h>
39#endif
40
41#ifndef Q_OS_WIN
42# define STD_OUTPUT_HANDLE 1
43# define STD_ERROR_HANDLE 2
44#endif
45
46#ifdef _WIN32_WCE
47#include <stdio.h>
48#endif
49
50void KProcessPrivate::writeAll(const QByteArray &buf, int fd)
51{
52#ifdef Q_OS_WIN
53#ifndef _WIN32_WCE
54 HANDLE h = GetStdHandle(fd);
55 if (h) {
56 DWORD wr;
57 WriteFile(h, buf.data(), buf.size(), &wr, 0);
58 }
59#else
60 fwrite(buf.data(), 1, buf.size(), (FILE*)fd);
61#endif
62#else
63 int off = 0;
64 do {
65 int ret = ::write(fd, buf.data() + off, buf.size() - off);
66 if (ret < 0) {
67 if (errno != EINTR)
68 return;
69 } else {
70 off += ret;
71 }
72 } while (off < buf.size());
73#endif
74}
75
76void KProcessPrivate::forwardStd(KProcess::ProcessChannel good, int fd)
77{
78 Q_Q(KProcess);
79
80 QProcess::ProcessChannel oc = q->readChannel();
81 q->setReadChannel(good);
82 writeAll(q->readAll(), fd);
83 q->setReadChannel(oc);
84}
85
86void KProcessPrivate::_k_forwardStdout()
87{
88#ifndef _WIN32_WCE
89 forwardStd(KProcess::StandardOutput, STD_OUTPUT_HANDLE);
90#else
91 forwardStd(KProcess::StandardOutput, (int)stdout);
92#endif
93}
94
95void KProcessPrivate::_k_forwardStderr()
96{
97#ifndef _WIN32_WCE
98 forwardStd(KProcess::StandardError, STD_ERROR_HANDLE);
99#else
100 forwardStd(KProcess::StandardError, (int)stderr);
101#endif
102}
103
104/////////////////////////////
105// public member functions //
106/////////////////////////////
107
108KProcess::KProcess(QObject *parent) :
109 QProcess(parent),
110 d_ptr(new KProcessPrivate)
111{
112 d_ptr->q_ptr = this;
113 setOutputChannelMode(ForwardedChannels);
114}
115
116KProcess::KProcess(KProcessPrivate *d, QObject *parent) :
117 QProcess(parent),
118 d_ptr(d)
119{
120 d_ptr->q_ptr = this;
121 setOutputChannelMode(ForwardedChannels);
122}
123
124KProcess::~KProcess()
125{
126 delete d_ptr;
127}
128
129void KProcess::setOutputChannelMode(OutputChannelMode mode)
130{
131 Q_D(KProcess);
132
133 d->outputChannelMode = mode;
134 disconnect(this, SIGNAL(readyReadStandardOutput()));
135 disconnect(this, SIGNAL(readyReadStandardError()));
136 switch (mode) {
137 case OnlyStdoutChannel:
138 connect(this, SIGNAL(readyReadStandardError()), SLOT(_k_forwardStderr()));
139 break;
140 case OnlyStderrChannel:
141 connect(this, SIGNAL(readyReadStandardOutput()), SLOT(_k_forwardStdout()));
142 break;
143 default:
144 QProcess::setProcessChannelMode((ProcessChannelMode)mode);
145 return;
146 }
147 QProcess::setProcessChannelMode(QProcess::SeparateChannels);
148}
149
150KProcess::OutputChannelMode KProcess::outputChannelMode() const
151{
152 Q_D(const KProcess);
153
154 return d->outputChannelMode;
155}
156
157void KProcess::setNextOpenMode(QIODevice::OpenMode mode)
158{
159 Q_D(KProcess);
160
161 d->openMode = mode;
162}
163
164#define DUMMYENV "_KPROCESS_DUMMY_="
165
166void KProcess::clearEnvironment()
167{
168 setEnvironment(QStringList() << QString::fromLatin1(DUMMYENV));
169}
170
171void KProcess::setEnv(const QString &name, const QString &value, bool overwrite)
172{
173 QStringList env = environment();
174 if (env.isEmpty()) {
175 env = systemEnvironment();
176 env.removeAll(QString::fromLatin1(DUMMYENV));
177 }
178 QString fname(name);
179 fname.append(QLatin1Char('='));
180 for (QStringList::Iterator it = env.begin(); it != env.end(); ++it)
181 if ((*it).startsWith(fname)) {
182 if (overwrite) {
183 *it = fname.append(value);
184 setEnvironment(env);
185 }
186 return;
187 }
188 env.append(fname.append(value));
189 setEnvironment(env);
190}
191
192void KProcess::unsetEnv(const QString &name)
193{
194 QStringList env = environment();
195 if (env.isEmpty()) {
196 env = systemEnvironment();
197 env.removeAll(QString::fromLatin1(DUMMYENV));
198 }
199 QString fname(name);
200 fname.append(QLatin1Char('='));
201 for (QStringList::Iterator it = env.begin(); it != env.end(); ++it)
202 if ((*it).startsWith(fname)) {
203 env.erase(it);
204 if (env.isEmpty())
205 env.append(QString::fromLatin1(DUMMYENV));
206 setEnvironment(env);
207 return;
208 }
209}
210
211void KProcess::setProgram(const QString &exe, const QStringList &args)
212{
213 Q_D(KProcess);
214
215 d->prog = exe;
216 d->args = args;
217#ifdef Q_OS_WIN
218 setNativeArguments(QString());
219#endif
220}
221
222void KProcess::setProgram(const QStringList &argv)
223{
224 Q_D(KProcess);
225
226 Q_ASSERT( !argv.isEmpty() );
227 d->args = argv;
228 d->prog = d->args.takeFirst();
229#ifdef Q_OS_WIN
230 setNativeArguments(QString());
231#endif
232}
233
234KProcess &KProcess::operator<<(const QString &arg)
235{
236 Q_D(KProcess);
237
238 if (d->prog.isEmpty())
239 d->prog = arg;
240 else
241 d->args << arg;
242 return *this;
243}
244
245KProcess &KProcess::operator<<(const QStringList &args)
246{
247 Q_D(KProcess);
248
249 if (d->prog.isEmpty())
250 setProgram(args);
251 else
252 d->args << args;
253 return *this;
254}
255
256void KProcess::clearProgram()
257{
258 Q_D(KProcess);
259
260 d->prog.clear();
261 d->args.clear();
262#ifdef Q_OS_WIN
263 setNativeArguments(QString());
264#endif
265}
266
267#if 0
268void KProcess::setShellCommand(const QString &cmd)
269{
270 Q_D(KProcess);
271
272 KShell::Errors err;
273 d->args = KShell::splitArgs(
274 cmd, KShell::AbortOnMeta | KShell::TildeExpand, &err);
275 if (err == KShell::NoError && !d->args.isEmpty()) {
276 d->prog = KStandardDirs::findExe(d->args[0]);
277 if (!d->prog.isEmpty()) {
278 d->args.removeFirst();
279#ifdef Q_OS_WIN
280 setNativeArguments(QString());
281#endif
282 return;
283 }
284 }
285
286 d->args.clear();
287
288#ifdef Q_OS_UNIX
289// #ifdef NON_FREE // ... as they ship non-POSIX /bin/sh
290# if !defined(__linux__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__DragonFly__) && !defined(__GNU__)
291 // If /bin/sh is a symlink, we can be pretty sure that it points to a
292 // POSIX shell - the original bourne shell is about the only non-POSIX
293 // shell still in use and it is always installed natively as /bin/sh.
294 d->prog = QFile::symLinkTarget(QString::fromLatin1("/bin/sh"));
295 if (d->prog.isEmpty()) {
296 // Try some known POSIX shells.
297 d->prog = KStandardDirs::findExe(QString::fromLatin1("ksh"));
298 if (d->prog.isEmpty()) {
299 d->prog = KStandardDirs::findExe(QString::fromLatin1("ash"));
300 if (d->prog.isEmpty()) {
301 d->prog = KStandardDirs::findExe(QString::fromLatin1("bash"));
302 if (d->prog.isEmpty()) {
303 d->prog = KStandardDirs::findExe(QString::fromLatin1("zsh"));
304 if (d->prog.isEmpty())
305 // We're pretty much screwed, to be honest ...
306 d->prog = QString::fromLatin1("/bin/sh");
307 }
308 }
309 }
310 }
311# else
312 d->prog = QString::fromLatin1("/bin/sh");
313# endif
314
315 d->args << QString::fromLatin1("-c") << cmd;
316#else // Q_OS_UNIX
317 // KMacroExpander::expandMacrosShellQuote(), KShell::quoteArg() and
318 // KShell::joinArgs() may generate these for security reasons.
319 setEnv(PERCENT_VARIABLE, QLatin1String("%"));
320
321#ifndef _WIN32_WCE
322 WCHAR sysdir[MAX_PATH + 1];
323 UINT size = GetSystemDirectoryW(sysdir, MAX_PATH + 1);
324 d->prog = QString::fromUtf16((const ushort *) sysdir, size);
325 d->prog += QLatin1String("\\cmd.exe");
326 setNativeArguments(QLatin1String("/V:OFF /S /C \"") + cmd + QLatin1Char('"'));
327#else
328 d->prog = QLatin1String("\\windows\\cmd.exe");
329 setNativeArguments(QLatin1String("/S /C \"") + cmd + QLatin1Char('"'));
330#endif
331#endif
332}
333#endif
334QStringList KProcess::program() const
335{
336 Q_D(const KProcess);
337
338 QStringList argv = d->args;
339 argv.prepend(d->prog);
340 return argv;
341}
342
343void KProcess::start()
344{
345 Q_D(KProcess);
346
347 QProcess::start(d->prog, d->args, d->openMode);
348}
349
350int KProcess::execute(int msecs)
351{
352 start();
353 if (!waitForFinished(msecs)) {
354 kill();
355 waitForFinished(-1);
356 return -2;
357 }
358 return (exitStatus() == QProcess::NormalExit) ? exitCode() : -1;
359}
360
361// static
362int KProcess::execute(const QString &exe, const QStringList &args, int msecs)
363{
364 KProcess p;
365 p.setProgram(exe, args);
366 return p.execute(msecs);
367}
368
369// static
370int KProcess::execute(const QStringList &argv, int msecs)
371{
372 KProcess p;
373 p.setProgram(argv);
374 return p.execute(msecs);
375}
376
377int KProcess::startDetached()
378{
379 Q_D(KProcess);
380
381 qint64 pid;
382 if (!QProcess::startDetached(d->prog, d->args, workingDirectory(), &pid))
383 return 0;
384 return (int) pid;
385}
386
387// static
388int KProcess::startDetached(const QString &exe, const QStringList &args)
389{
390 qint64 pid;
391 if (!QProcess::startDetached(exe, args, QString(), &pid))
392 return 0;
393 return (int) pid;
394}
395
396// static
397int KProcess::startDetached(const QStringList &argv)
398{
399 QStringList args = argv;
400 QString prog = args.takeFirst();
401 return startDetached(prog, args);
402}
403
404int KProcess::pid() const
405{
406#ifdef Q_OS_UNIX
407 return (int) QProcess::pid();
408#else
409 return QProcess::pid() ? QProcess::pid()->dwProcessId : 0;
410#endif
411}
412
413