1 /*  smplayer, GUI front-end for mplayer.
2     Copyright (C) 2006-2021 Ricardo Villalba <ricardo@smplayer.info>
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18 
19 #include "myprocess.h"
20 #include <QDir>
21 #include <QDebug>
22 
23 #ifdef Q_OS_WIN
24 
25 #if QT_VERSION < 0x040300
26 #define USE_TEMP_FILE 1
27 #else
28 #define USE_TEMP_FILE 0
29 #endif
30 
31 #else
32 #define USE_TEMP_FILE 0
33 #endif
34 
35 
MyProcess(QObject * parent)36 MyProcess::MyProcess(QObject * parent) : QProcess(parent)
37 {
38 	clearArguments();
39 	setProcessChannelMode( QProcess::MergedChannels );
40 
41 	QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
42 #ifndef Q_OS_WIN
43 	QString bin_path = QDir::homePath() + "/bin";
44 	qDebug() << "MyProcess::MyProcess: bin_path:" << bin_path;
45 	qDebug() << "MyProcess::MyProcess: PATH:" << env.value("PATH");
46 	QStringList paths = env.value("PATH").split(":");
47 	qDebug() << "MyProcess::MyProcess: paths:" << paths;
48 	if (!paths.contains(bin_path)) {
49 		QString new_path = bin_path +":" + env.value("PATH");
50 		env.insert("PATH", new_path);
51 		qDebug() << "MyProcess::MyProcess: PATH:" << env.value("PATH");
52 	}
53 #endif
54 	setProcessEnvironment(env);
55 
56 #if USE_TEMP_FILE
57 	temp_file.open(); // Create temporary file
58 	QString filename = temp_file.fileName();
59 	setStandardOutputFile( filename );
60 	qDebug("MyProcess::MyProcess: temporary file: %s", filename.toUtf8().data());
61 	temp_file.close();
62 
63 	//connect(&temp_file, SIGNAL(readyRead()), this, SLOT(readTmpFile()) );
64 	connect(&timer, SIGNAL(timeout()), this, SLOT(readTmpFile()) );
65 #else
66 	connect(this, SIGNAL(readyReadStandardOutput()), this, SLOT(readStdOut()) );
67 #endif
68 
69 	connect(this, SIGNAL(finished(int, QProcess::ExitStatus)),
70             this, SLOT(procFinished()) );
71 
72 	// Test splitArguments
73 	//QStringList l = MyProcess::splitArguments("-opt 1 hello \"56 67\" wssx -ios");
74 }
75 
clearArguments()76 void MyProcess::clearArguments() {
77 	program = "";
78 	arg.clear();
79 }
80 
isRunning()81 bool MyProcess::isRunning() {
82 	return (state() == QProcess::Running);
83 }
84 
addArgument(const QString & a)85 void MyProcess::addArgument(const QString & a) {
86 	if (program.isEmpty()) {
87 		program = a;
88 	} else {
89 		arg.append(a);
90 	}
91 }
92 
arguments()93 QStringList MyProcess::arguments() {
94 	QStringList l = arg;
95 	l.prepend(program);
96 	return l;
97 }
98 
start()99 void MyProcess::start() {
100 	qDebug() << "MyProcess::start: environment path:" << processEnvironment().value("PATH");
101 	qDebug() << "MyProcess::start: current directory:" << QDir::currentPath();
102 
103 	remaining_output.clear();
104 
105 	QString bin = program;
106 	/*
107 	QFileInfo fi(program);
108 	if (fi.exists() && fi.isExecutable() && !fi.isDir()) {
109 		bin = fi.absoluteFilePath();
110 	}
111 	qDebug() << "MyProcess::start: executable:" << bin;
112 	*/
113 
114 	QProcess::start(bin, arg);
115 
116 #if USE_TEMP_FILE
117 	//bool r = temp_file.open(QIODevice::ReadOnly);
118 	bool r = temp_file.open();
119 	timer.start(50);
120 	qDebug("MyProcess::start: r: %d", r);
121 #endif
122 }
123 
readStdOut()124 void MyProcess::readStdOut() {
125 	genericRead( readAllStandardOutput() );
126 }
127 
128 
readTmpFile()129 void MyProcess::readTmpFile() {
130 	genericRead( temp_file.readAll() );
131 }
132 
genericRead(QByteArray buffer)133 void MyProcess::genericRead(QByteArray buffer) {
134 	QByteArray ba = remaining_output + buffer;
135 	int start = 0;
136 	int from_pos = 0;
137 	int pos = canReadLine(ba, from_pos);
138 
139 	//qDebug("MyProcess::read: pos: %d", pos);
140 	while ( pos > -1 ) {
141 		// Readline
142 		//QByteArray line = ba.left(pos);
143 		QByteArray line = ba.mid(start, pos-start);
144 		//ba = ba.mid(pos+1);
145 		from_pos = pos + 1;
146 #if defined(Q_OS_WIN) || defined(Q_OS_OS2)
147 		if ((from_pos < ba.size()) && (ba.at(from_pos)=='\n')) from_pos++;
148 #endif
149 		start = from_pos;
150 
151 		emit lineAvailable(line);
152 
153 		pos = canReadLine(ba, from_pos);
154 	}
155 
156 	remaining_output = ba.mid(from_pos);
157 }
158 
canReadLine(const QByteArray & ba,int from)159 int MyProcess::canReadLine(const QByteArray & ba, int from) {
160 	int pos1 = ba.indexOf('\n', from);
161 	int pos2 = ba.indexOf('\r', from);
162 
163 	//qDebug("MyProcess::canReadLine: pos2: %d", pos2);
164 
165 	if ( (pos1 == -1) && (pos2 == -1) ) return -1;
166 
167 	int pos = pos1;
168 	if ( (pos1 != -1) && (pos2 != -1) ) {
169 		/*
170 		if (pos2 == (pos1+1)) pos = pos2; // \r\n
171 		else
172 		*/
173 		if (pos1 < pos2) pos = pos1; else pos = pos2;
174 	} else {
175 		if (pos1 == -1) pos = pos2;
176 		else
177 		if (pos2 == -1) pos = pos1;
178 	}
179 
180 	return pos;
181 }
182 
183 /*!
184 Do some clean up, and be sure that all output has been read.
185 */
procFinished()186 void MyProcess::procFinished() {
187 	qDebug("MyProcess::procFinished");
188 
189 #if !USE_TEMP_FILE
190 	qDebug() << "MyProcess::procFinished: Bytes available: " << bytesAvailable();
191 	if ( bytesAvailable() > 0 ) readStdOut();
192 #else
193 	timer.stop();
194 
195 	qDebug() << "MyProcess::procFinished: Bytes available: " << temp_file.bytesAvailable();
196 	if ( temp_file.bytesAvailable() > 0 ) readTmpFile();
197 	qDebug() << "MyProcess::procFinished: Bytes available:" << temp_file.bytesAvailable();
198 
199 	temp_file.close();
200 #endif
201 }
202 
splitArguments(const QString & args)203 QStringList MyProcess::splitArguments(const QString & args) {
204 	qDebug("MyProcess::splitArguments: '%s'", args.toUtf8().constData());
205 
206 	QStringList l;
207 
208 	bool opened_quote = false;
209 	int init_pos = 0;
210 	for (int n = 0; n < args.length(); n++) {
211 		if ((args[n] == QChar(' ')) && (!opened_quote)) {
212 			l.append(args.mid(init_pos, n - init_pos));
213 			init_pos = n+1;
214 		}
215 		else
216 		if (args[n] == QChar('\"')) opened_quote = !opened_quote;
217 
218 		if (n == args.length()-1) {
219 			l.append(args.mid(init_pos, (n - init_pos)+1));
220 		}
221 	}
222 
223 	for (int n = 0; n < l.count(); n++) {
224 		qDebug("MyProcess::splitArguments: arg: %d '%s'", n, l[n].toUtf8().constData());
225 	}
226 
227 	return l;
228 }
229 
230 #include "moc_myprocess.cpp"
231