/****************************************************************************
**
** Copyright (C) 2001-2006 Frank Hemer <frank@hemer.org>,
** Tilo Riemer <riemer@crossvc.com>
**
**
**----------------------------------------------------------------------------
**
**----------------------------------------------------------------------------
**
** CrossVC is available under two different licenses:
**
** If CrossVC is linked against the GPLed version of Qt
** CrossVC is released under the terms of GPL also.
**
** If CrossVC is linked against a nonGPLed version of Qt
** CrossVC is released under the terms of the
** CrossVC License for non-Unix platforms (CLNU)
**
**
** CrossVC License for non-Unix platforms (CLNU):
**
** Redistribution and use in binary form, without modification,
** are permitted provided that the following conditions are met:
**
** 1. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 2. It is not permitted to distribute the binary package under a name
** different than CrossVC.
** 3. The name of the authors may not be used to endorse or promote
** products derived from this software without specific prior written
** permission.
** 4. The source code is the creative property of the authors.
** Extensions and development under the terms of the Gnu Public License
** are limited to the Unix platform. Any distribution or compilation of
** the source code against libraries licensed other than gpl requires
** the written permission of the authors.
**
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
** WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
** DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
** GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
**
**
** CrossVC License for Unix platforms:
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, version 2 of the License.
** This program is distributed in
** the hope that it will be useful, but WITHOUT ANY WARRANTY; without
** even the implied warranty of MERCHANTABILITY or FITNESS FOR A
** PARTICULAR PURPOSE.
**
** See the GNU General Public License version 2 for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software Foundation,
** Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
*****************************************************************************/
#include "config.h"
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <qapplication.h>
#include <qregexp.h>
#include <qtimer.h>
#include "globals.h"
#include "InteractiveCmdThread.h"
#include "TextDecoder.h"
//------------------------------------------------------------------------------------
InteractiveCmdThread::InteractiveCmdThread (QWidget *qw, CvsBuffer *pCvsBuffer, CCvsOutput *pMessages,
QDir &workDir, QString cmdstr, int cmd,
QString module, CommandInterface *inst)
: QObject(),
m_qw(qw),
m_pCvsBuffer(pCvsBuffer),
m_pMessages(pMessages),
m_workDir(workDir),
m_cmdStr(cmdstr),
m_cmd(cmd),
m_module(module),
instance(inst)
{
m_pEnvironment = NULL;
m_pTmpFile = NULL;
// m_pMessages->insert("cmd: "+m_cmdStr+" "+m_module+"\n\n");
m_pCommandList = new QStringList();
*m_pCommandList = QStringList::split(" && ",m_cmdStr);
}
//------------------------------------------------------------------------------------
InteractiveCmdThread::~InteractiveCmdThread () {
disconnect(this,0,0,0);
delete m_pCommandList;
delete m_pEnvironment;
delete m_pTmpFile;
// delete m_proc; m_proc is a child of this object and should be deleted automatically
}
//------------------------------------------------------------------------------------
bool InteractiveCmdThread::start() {
//read environment settings
//make sure this is only called once and kept till end of thread,
//in case more than one call is executed (concatenated with && )
//and the following commands might need these env too!
delete m_pEnvironment;
m_pEnvironment = new QStringList();
m_pEnvironment->append("CVS_PASSFILE="+CVSPASSPATH);
#ifdef Q_WS_MAC
QString askpass = APPDIR + "/" + LC_TOOLS_DIR + "/ssh-askpass.app/Contents/MacOS/ssh-askpass";
#else
QString askpass = APPDIR + "/" + LC_TOOLS_DIR + "/ssh-askpass.bin";
#endif
if (QFileInfo(askpass).exists()) {
m_pEnvironment->append("SSH_ASKPASS="+askpass);
} else {
addToEnv("SSH_ASKPASS");
}
#ifdef Q_WS_MAC
if (!addToEnv("DISPLAY")) m_pEnvironment->append("DISPLAY=:0.0");
#else
addToEnv("DISPLAY");
#endif
// Have the process inherit the PATH setting so that ssh and other
// commands can be found
addToEnv("PATH");
addToEnv("HOME");
return run();
}
//-------------------------------------------------------------------------------------
bool InteractiveCmdThread::run () {
delete m_pTmpFile;
m_pTmpFile = NULL;
//reset ouput line offset
m_bIsOutputLineOffsetChecked = false;
outputLineOffset = 0;
//parse for exports and append to environment
bool found;
do {
found = false;
m_command = m_pCommandList->first().stripWhiteSpace();
m_pCommandList->pop_front();
if (m_command.find("export ")==0) {
#ifdef Q_WS_WIN
m_pEnvironment->append(adaptPath(m_command.mid(7).stripWhiteSpace()));
#else
m_pEnvironment->append(m_command.mid(7).stripWhiteSpace());
#endif
found = true;
}
} while (found);
if (m_pEnvironment->empty()) {
delete(m_pEnvironment);
m_pEnvironment = NULL;
}
//check for writing into tmp files
int pos;
if ((pos = m_command.find(" > "))>-1) {
QString fileName = m_command.mid(pos+3).stripWhiteSpace();
m_command = m_command.left(pos).stripWhiteSpace();
m_pTmpFile = new QFile(fileName);
if(!m_pTmpFile->open(IO_WriteOnly | IO_Append)) {
delete(m_pTmpFile);
m_pTmpFile = NULL;
}
} else {
m_pTmpFile = NULL;
}
//complete the command
m_command += " " + m_module;
//write cmd to message window
QString commandWithoutPlaceholder = m_command;
commandWithoutPlaceholder.replace(PLACEHOLDER_FOR_QUOTATION_MARKS, "\"");
m_pCvsBuffer->append("cmd: "+commandWithoutPlaceholder+getSystemLF());
m_pMessages->insert("cmd: "+commandWithoutPlaceholder+getSystemLF());
//split command options and prevent doublequoted strings from being split
//the doublequotes are removed though
if (m_command.startsWith("\"")) {
pos = 0;
} else {
pos = 1;
}
QStringList commandMasqued = QStringList::split("\"",m_command);
QStringList commandArgs;
QStringList::Iterator outer = commandMasqued.begin();
while (outer != commandMasqued.end()) {
if (pos % 2) {
QStringList tmpList = QStringList::split(" ",(*outer));
for ( QStringList::Iterator inner = tmpList.begin(); inner != tmpList.end(); ++inner ) {
commandArgs.append((*inner).replace(PLACEHOLDER_FOR_QUOTATION_MARKS, "\""));
}
} else {
commandArgs.append((*outer).replace(PLACEHOLDER_FOR_QUOTATION_MARKS, "\""));
}
++pos;
++outer;
}
// m_pMessages->insert("*******start********\n");
// for ( QStringList::Iterator it = commandArgs.begin(); it != commandArgs.end(); ++it ) {
// m_pMessages->insert(*it+"\n");
// }
// m_pMessages->insert("*******end********\n");
//setup QProcess
m_proc = new QProcess(this);
m_proc->setCommunication(QProcess::Stdin|QProcess::Stdout|QProcess::Stderr|QProcess::DupStderr);
m_proc->setWorkingDirectory(m_workDir);
m_proc->setArguments(commandArgs);
connect( m_proc, SIGNAL(readyReadStdout()), this, SLOT(readFromStdout()) );
connect( m_proc, SIGNAL(processExited()), this, SLOT(ready()) );
//and run it
if ( !m_proc->start(m_pEnvironment) ) {
// error handling
std::cout << "can't start qprocess\n";
m_pMessages->insert("\nLinCVS ERROR: can't start qprocess\n\nMaybe cvs is not in $PATH?\n");
m_pCommandList->clear();
ready();
return FALSE;
} else {
return TRUE;
}
}
bool InteractiveCmdThread::addToEnv(QString var) {
if (m_pEnvironment) {
QString tmpEnv = getenv(var);
if (tmpEnv.length() != 0) {
m_pEnvironment->append(var+"="+tmpEnv);
return TRUE;
}
}
return FALSE;
}
void InteractiveCmdThread::ready() {
if (m_pTmpFile) {
m_pTmpFile->flush();
m_pTmpFile->close();
}
if (m_pCommandList->empty()) {
m_pMessages->insert(getSystemLF());
emit cvsCommandFinished();
} else {
run();
}
}
void InteractiveCmdThread::readFromStdout() {
while (true) {
QString line;
QByteArray nextLine;
do {
nextLine = m_proc->readStdout();
if (nextLine.size() == 0) {
if (line.isEmpty()) {
return;
} else {
break;
}
}
//test for proxy startup message, if found, ignore it!
if (!m_bIsOutputLineOffsetChecked) {
QByteArray tmp = nextLine.copy();
int size = tmp.size();
tmp.resize(size+1);
tmp[size] = '\0';
QString firstLine(tmp);
int start;
if ( (start = firstLine.find("(HTTP tunneling through ")) >= 0) {
start = firstLine.find("\n",start);
line = firstLine.left(start+1);
QByteArray hlp(nextLine.size()-start-1);
unsigned int i, j;
for (i=0,j=start+1;j<nextLine.size();i++,j++) {
hlp.at(i)=nextLine.at(j);
}
nextLine = hlp;
outputLineOffset = 1;
}
m_bIsOutputLineOffsetChecked = true;
}
if (m_pTmpFile) {//write to tmp file
int size = nextLine.size();
do {
int written = m_pTmpFile->writeBlock(nextLine);
size -= written;
if (size>0) {
int pos,fill;
for (pos = 0,fill = written; written < size; pos++,fill++) {
nextLine[pos]=nextLine[fill];
}
nextLine.truncate(pos+1);
}
} while (size>0);
} else {//write to cvs output buffer
line.append(I18n::g_pTextDecoder->decode(nextLine.data(), nextLine.size()));
//old version
/*
int size = nextLine.size();
nextLine.resize(size+1);
nextLine[size] = '\0';
line.append(nextLine);
*/
}
} while (true);
if (!checkForInputRequest(&line)) {
if (!line.isEmpty()) {
m_pCvsBuffer->append(line);
m_pMessages->insert(line);//use insert because append adds additional '\n'
}
}
}
}
//-------------------------------------------------------------------------------------------------
bool InteractiveCmdThread::checkForInputRequest(QString *line) {
switch( m_cmd) {
case CVS_RELEASE_CMD: {
return release(line);
break;
}
case CVS_RELEASE_D_CMD: {
return release(line);
break;
}
case CVS_UNEDIT_CMD: {
return unedit(line);
break;
}
case CVS_NOT_INTERACTIVE_CMD: {
// nothing done
}
default: { // additional not listed non-interactive commands, that need no implementation here but in afterCall
}
}
return false;
}
//-------------------------------------------------------------------------------------------------
bool InteractiveCmdThread::release(QString *line) {
int a = 0;
int b = 0;
QString msg;
a = line->find( ':',-4);
b = line->findRev( "revert changes?");
if (a>=0 || b>=0) {
if (a>=0) { // last two lines
int i = line->findRev('\n',a-line->length()-1);
i = line->findRev('\n',i-line->length()-1) + 1;
msg = line->mid(i);
m_pCvsBuffer->append(line->left(i));
m_pMessages->insert(line->left(i));
}
else msg = *line;// only one line
MESSAGE message;
message.type = CVS_Y_N;
message.request = msg;
message.reply = "";
emit requestReceived(message);
return true;
}
return false;
}
//-------------------------------------------------------------------------------------------------
bool InteractiveCmdThread::unedit(QString *line) {
int llen;
if ( (llen = (int)line->length()-19) >= 0) {
if ( line->findRev("revert changes?") >= llen) {
MESSAGE message;
message.type = CVS_Y_N;
message.request = *line;
message.reply = "";
emit requestReceived(message);
return true;
}
}
return false;
}
//-------------------------------------------------------------------------------------------------
void InteractiveCmdThread::writeToShell( QString *line, QString s) {
m_proc->writeToStdin(s);
if (!line->isEmpty()) {
m_pCvsBuffer->append( *line+s);
m_pMessages->insert( *line+s);
} else {
m_pCvsBuffer->append( s);
m_pMessages->insert( s);
}
}
//-------------------------------------------------------------------------------------------------
bool InteractiveCmdThread::sendReply(MESSAGE msg) {
writeToShell(&msg.request,msg.reply);
return true;
}
//-------------------------------------------------------------------------------------------------
CommandInterface* InteractiveCmdThread::getInstance() {
return instance;
}
//-------------------------------------------------------------------------------------------------
int InteractiveCmdThread::getCommand() {
return m_cmd;
}
//-------------------------------------------------------------------------------------------------
void InteractiveCmdThread::exit() {
m_proc->tryTerminate();
m_pMessages->insert("Please stand by, process will be terminated\n");
QTimer::singleShot( 5000, m_proc, SLOT( kill()) );
}
syntax highlighted by Code2HTML, v. 0.9.1