summaryrefslogtreecommitdiffstats
path: root/kdict/dict.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kdict/dict.cpp')
-rw-r--r--kdict/dict.cpp1632
1 files changed, 1632 insertions, 0 deletions
diff --git a/kdict/dict.cpp b/kdict/dict.cpp
new file mode 100644
index 00000000..b36f1ac1
--- /dev/null
+++ b/kdict/dict.cpp
@@ -0,0 +1,1632 @@
+/* -------------------------------------------------------------
+
+ dict.cpp (part of The KDE Dictionary Client)
+
+ Copyright (C) 2000-2001 Christian Gebauer <gebauer@kde.org>
+ (C) by Matthias Hölzer 1998
+
+ This file is distributed under the Artistic License.
+ See LICENSE for details.
+
+ -------------------------------------------------------------
+
+ JobData used for data transfer between Client and Interface
+ DictAsyncClient all network related stuff happens here in asynchrous thread
+ DictInterface interface for DictAsyncClient, job management
+
+ ------------------------------------------------------------- */
+
+#include <config.h>
+
+#include "application.h"
+#include "options.h"
+#include "dict.h"
+
+#include <qregexp.h>
+#include <qtextcodec.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+#include <kmdcodec.h>
+#include <kextsock.h>
+#include <ksocks.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+
+//********* JobData ******************************************
+
+
+JobData::JobData(QueryType Ntype,bool NnewServer,QString const& Nserver,int Nport,
+ int NidleHold, int Ntimeout, int NpipeSize, QString const& Nencoding, bool NAuthEnabled,
+ QString const& Nuser, QString const& Nsecret, unsigned int NheadLayout)
+ : type(Ntype), error(ErrNoErr), canceled(false), numFetched(0), newServer(NnewServer),server(Nserver), port(Nport),
+ timeout(Ntimeout), pipeSize(NpipeSize), idleHold(NidleHold), encoding(Nencoding), authEnabled(NAuthEnabled),
+ user(Nuser), secret(Nsecret), headLayout(NheadLayout)
+{}
+
+
+//********* DictAsyncClient *************************************
+
+DictAsyncClient::DictAsyncClient(int NfdPipeIn, int NfdPipeOut)
+: job(0L), inputSize(10000), fdPipeIn(NfdPipeIn),
+ fdPipeOut(NfdPipeOut), tcpSocket(-1), idleHold(0)
+{
+ input = new char[inputSize];
+}
+
+
+DictAsyncClient::~DictAsyncClient()
+{
+ if (-1!=tcpSocket)
+ doQuit();
+ delete [] input;
+}
+
+
+void* DictAsyncClient::startThread(void* pseudoThis)
+{
+ DictAsyncClient* newthis = (DictAsyncClient*) (pseudoThis);
+
+ if (0!=pthread_setcanceltype(PTHREAD_CANCEL_ENABLE,NULL))
+ qWarning("pthread_setcanceltype failed!");
+ if (0!= pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL))
+ qWarning("pthread_setcanceltype failed!");
+
+ signal(SIGPIPE,SIG_IGN); // ignore sigpipe
+
+ newthis->waitForWork();
+ return NULL;
+}
+
+
+void DictAsyncClient::insertJob(JobData *newJob)
+{
+ if (!job) // don't overwrite existing job pointer
+ job = newJob;
+}
+
+
+void DictAsyncClient::removeJob()
+{
+ job = 0L;
+}
+
+
+void DictAsyncClient::waitForWork()
+{
+ fd_set fdsR,fdsE;
+ timeval tv;
+ int selectRet;
+ char buf;
+
+ while (true) {
+ if (tcpSocket != -1) { // we are connected, hold the connection for xx secs
+ FD_ZERO(&fdsR);
+ FD_SET(fdPipeIn, &fdsR);
+ FD_SET(tcpSocket, &fdsR);
+ FD_ZERO(&fdsE);
+ FD_SET(tcpSocket, &fdsE);
+ tv.tv_sec = idleHold;
+ tv.tv_usec = 0;
+ selectRet = KSocks::self()->select(FD_SETSIZE, &fdsR, NULL, &fdsE, &tv);
+ if (selectRet == 0) {
+ doQuit(); // nothing happend...
+ } else {
+ if (((selectRet > 0)&&(!FD_ISSET(fdPipeIn,&fdsR)))||(selectRet == -1))
+ closeSocket();
+ }
+ }
+
+ do {
+ FD_ZERO(&fdsR);
+ FD_SET(fdPipeIn, &fdsR);
+ } while (select(FD_SETSIZE, &fdsR, NULL, NULL, NULL)<0); // don't get tricked by signals
+
+ clearPipe();
+
+ if (job) {
+ if ((tcpSocket!=-1)&&(job->newServer))
+ doQuit();
+
+ codec = QTextCodec::codecForName(job->encoding.latin1());
+ input[0] = 0; //terminate string
+ thisLine = input;
+ nextLine = input;
+ inputEnd = input;
+ timeout = job->timeout;
+ idleHold = job->idleHold;
+
+ if (tcpSocket==-1)
+ openConnection();
+
+ if (tcpSocket!=-1) { // connection is ready
+ switch (job->type) {
+ case JobData::TDefine :
+ define();
+ break;
+ case JobData::TGetDefinitions :
+ getDefinitions();
+ break;
+ case JobData::TMatch :
+ match();
+ break;
+ case JobData::TShowDatabases :
+ showDatabases();
+ break;
+ case JobData::TShowDbInfo :
+ showDbInfo();
+ break;
+ case JobData::TShowStrategies :
+ showStrategies();
+ break;
+ case JobData::TShowInfo :
+ showInfo();
+ break;
+ case JobData::TUpdate :
+ update();
+ }
+ }
+ clearPipe();
+ }
+ if (write(fdPipeOut,&buf,1) == -1) // emit stopped signal
+ ::perror( "waitForJobs()" );
+ }
+}
+
+
+void DictAsyncClient::define()
+{
+ QString command;
+
+ job->defines.clear();
+ QStringList::iterator it;
+ for (it = job->databases.begin(); it != job->databases.end(); ++it) {
+ command = "define ";
+ command += *it;
+ command += " \"";
+ command += job->query;
+ command += "\"\r\n";
+ job->defines.append(command);
+ }
+
+ if (!getDefinitions())
+ return;
+
+ if (job->numFetched == 0) {
+ job->strategy = ".";
+ if (!match())
+ return;
+ job->result = QString::null;
+ if (job->numFetched == 0) {
+ resultAppend("<body>\n<p class=\"heading\">\n");
+ resultAppend(i18n("No definitions found for \'%1'.").arg(job->query));
+ resultAppend("</p>\n</html></body>");
+ } else {
+ // html header...
+ resultAppend("<body>\n<p class=\"heading\">\n");
+ resultAppend(i18n("No definitions found for \'%1\'. Perhaps you mean:").arg(job->query));
+ resultAppend("</p>\n<table width=\"100%\" cols=2>\n");
+
+ QString lastDb;
+ QStringList::iterator it;
+ for (it = job->matches.begin(); it != job->matches.end(); ++it) {
+ int pos = (*it).find(' ');
+ if (pos != -1) {
+ if (lastDb != (*it).left(pos)) {
+ if (lastDb.length() > 0)
+ resultAppend("</pre></td></tr>\n");
+ lastDb = (*it).left(pos);
+ resultAppend("<tr valign=top><td width=25%><pre><b>");
+ resultAppend(lastDb);
+ resultAppend(":</b></pre></td><td width=75%><pre>");
+ }
+ if ((*it).length() > (unsigned int)pos+2) {
+ resultAppend("<a href=\"http://define/");
+ resultAppend((*it).mid(pos+2, (*it).length()-pos-3));
+ resultAppend("\">");
+ resultAppend((*it).mid(pos+2, (*it).length()-pos-3));
+ resultAppend("</a> ");
+ }
+ }
+ }
+ resultAppend("\n</pre></td></tr></table>\n</body></html>");
+ job->numFetched = 0;
+ }
+ }
+}
+
+
+QString htmlString(const QString &raw)
+{
+ unsigned int len=raw.length();
+ QString ret;
+
+ for (unsigned int i=0; i<len; i++) {
+ switch (raw[i]) {
+ case '&' : ret += "&amp"; break;
+ case '<' : ret+="&lt;"; break;
+ case '>' : ret+="&gt;"; break;
+ default : ret+=raw[i];
+ }
+ }
+
+ return ret;
+}
+
+
+QString generateDefineLink(const QString &raw)
+{
+ QRegExp http("http://[^\\s<>()\"|\\[\\]{}]+");
+ QRegExp ftp("ftp://[^\\s<>()\"|\\[\\]{}]+");
+ int matchPos=0, matchLen=0;
+ bool httpMatch=false;
+ QString ret;
+
+ matchPos = http.search(raw);
+ matchLen = http.matchedLength();
+ if (-1 != matchPos) {
+ httpMatch = true;
+ } else {
+ matchPos = ftp.search(raw);
+ matchLen = ftp.matchedLength();
+ httpMatch = false;
+ }
+
+ if (-1 != matchPos) {
+ ret = htmlString(raw.left(matchPos));
+ ret += "<a href=\"http://";
+ if (httpMatch) {
+ ret += "realhttp/";
+ ret += raw.mid(matchPos+7, matchLen-7);
+ } else {
+ ret += "realftp/";
+ ret += raw.mid(matchPos+6, matchLen-6);
+ }
+ ret += "\">";
+ ret += htmlString(raw.mid(matchPos, matchLen));
+ ret += "</a>";
+ ret += htmlString(raw.right(raw.length()-matchLen-matchPos));
+ } else {
+ ret = "<a href=\"http://define/";
+ ret += raw;
+ ret += "\">";
+ ret += htmlString(raw);
+ ret += "</a>";
+ }
+
+ return ret;
+}
+
+
+bool DictAsyncClient::getDefinitions()
+{
+ QCString lastDb,bracketBuff;
+ QStrList hashList;
+ char *s;
+ int defCount,response;
+
+ // html header...
+ resultAppend("<body>\n");
+
+ while (job->defines.count()>0) {
+ defCount = 0;
+ cmdBuffer = "";
+ do {
+ QStringList::iterator it = job->defines.begin();
+ cmdBuffer += codec->fromUnicode(*it);
+ defCount++;
+ job->defines.remove(it);
+ } while ((job->defines.count()>0)&&((int)cmdBuffer.length()<job->pipeSize));
+
+ if (!sendBuffer())
+ return false;
+
+ for (;defCount > 0;defCount--) {
+ if (!getNextResponse(response))
+ return false;
+ switch (response) {
+ case 552: // define: 552 No match
+ break;
+ case 150: { // define: 150 n definitions retrieved - definitions follow
+ bool defineDone = false;
+ while (!defineDone) {
+ if (!getNextResponse(response))
+ return false;
+ switch (response) {
+ case 151: { // define: 151 word database name - text follows
+ char *db = strchr(thisLine, '\"');
+ if (db)
+ db = strchr(db+1, '\"');
+ char *dbdes = 0;
+ if (db) {
+ db+=2; // db points now on database name
+ dbdes = strchr(db,' ');
+ if (dbdes) {
+ dbdes[0] = 0; // terminate database name
+ dbdes+=2; // dbdes points now on database description
+ }
+ } else {
+ job->error = JobData::ErrServerError;
+ job->result = QString::null;
+ resultAppend(thisLine);
+ doQuit();
+ return false;
+ }
+
+ int oldResPos = job->result.length();
+
+ if (((job->headLayout<2)&&(lastDb!=db))||(job->headLayout==2)) {
+ lastDb = db;
+ resultAppend("<p class=\"heading\">\n");
+ if (dbdes)
+ resultAppend(codec->toUnicode(dbdes,strlen(dbdes)-1));
+ resultAppend(" [<a href=\"http://dbinfo/");
+ resultAppend(db);
+ resultAppend("\">");
+ resultAppend(db);
+ resultAppend("</a>]</p>\n");
+ } else
+ if (job->headLayout==1)
+ resultAppend("<hr>\n");
+
+ resultAppend("<pre><p class=\"definition\">\n");
+
+ KMD5 context;
+ bool bodyDone = false;
+ while (!bodyDone) {
+ if (!getNextLine())
+ return false;
+ char *line = thisLine;
+ if (line[0]=='.') {
+ if (line[1]=='.')
+ line++; // collapse double periode into one
+ else
+ if (line[1]==0)
+ bodyDone = true;
+ }
+ if (!bodyDone) {
+ context.update(QCString(line));
+ if (!bracketBuff.isEmpty()) {
+ s = strchr(line,'}');
+ if (!s)
+ resultAppend(bracketBuff.data());
+ else {
+ s[0] = 0;
+ bracketBuff.remove(0,1); // remove '{'
+ bracketBuff += line;
+ line = s+1;
+ resultAppend(generateDefineLink(codec->toUnicode(bracketBuff)));
+ }
+ bracketBuff = "";
+ }
+ s = strchr(line,'{');
+ while (s) {
+ resultAppend(htmlString(codec->toUnicode(line,s-line)));
+ line = s;
+ s = strchr(line,'}');
+ if (s) {
+ s[0] = 0;
+ line++;
+ resultAppend(generateDefineLink(codec->toUnicode(line)));
+ line = s+1;
+ s = strchr(line,'{');
+ } else {
+ bracketBuff = line;
+ bracketBuff += "\n";
+ line = 0;
+ s = 0;
+ }
+ }
+ if (line) {
+ resultAppend(htmlString(codec->toUnicode(line)));
+ resultAppend("\n");
+ }
+ }
+ }
+ resultAppend("</p></pre>\n");
+
+ if (hashList.find(context.hexDigest())>=0) // duplicate??
+ job->result.truncate(oldResPos); // delete the whole definition
+ else {
+ hashList.append(context.hexDigest());
+ job->numFetched++;
+ }
+
+ break; }
+ case 250: { // define: 250 ok (optional timing information here)
+ defineDone = true;
+ break; }
+ default: {
+ handleErrors();
+ return false; }
+ }
+ }
+ break; }
+ default:
+ handleErrors();
+ return false;
+ }
+ }
+ }
+
+ resultAppend("</body></html>\n");
+ return true;
+}
+
+
+bool DictAsyncClient::match()
+{
+ QStringList::iterator it = job->databases.begin();
+ int response;
+ cmdBuffer = "";
+
+ while (it != job->databases.end()) {
+ int send = 0;
+ do {
+ cmdBuffer += "match ";
+ cmdBuffer += codec->fromUnicode(*(it));
+ cmdBuffer += " ";
+ cmdBuffer += codec->fromUnicode(job->strategy);
+ cmdBuffer += " \"";
+ cmdBuffer += codec->fromUnicode(job->query);
+ cmdBuffer += "\"\r\n";
+ send++;
+ ++it;
+ } while ((it != job->databases.end())&&((int)cmdBuffer.length()<job->pipeSize));
+
+ if (!sendBuffer())
+ return false;
+
+ for (;send > 0;send--) {
+ if (!getNextResponse(response))
+ return false;
+ switch (response) {
+ case 552: // match: 552 No match
+ break;
+ case 152: { // match: 152 n matches found - text follows
+ bool matchDone = false;
+ while (!matchDone) {
+ if (!getNextLine())
+ return false;
+ char *line = thisLine;
+ if (line[0]=='.') {
+ if (line[1]=='.')
+ line++; // collapse double period into one
+ else
+ if (line[1]==0)
+ matchDone = true;
+ }
+ if (!matchDone) {
+ job->numFetched++;
+ job->matches.append(codec->toUnicode(line));
+ }
+ }
+ if (!nextResponseOk(250)) // match: "250 ok ..."
+ return false;
+ break; }
+ default:
+ handleErrors();
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+void DictAsyncClient::showDatabases()
+{
+ cmdBuffer = "show db\r\n";
+
+ if (!sendBuffer())
+ return;
+
+ if (!nextResponseOk(110)) // show db: "110 n databases present - text follows "
+ return;
+
+ // html header...
+ resultAppend("<body>\n<p class=\"heading\">\n");
+ resultAppend(i18n("Available Databases:"));
+ resultAppend("\n</p>\n<table width=\"100%\" cols=2>\n");
+
+ bool done(false);
+ char *line;
+ while (!done) {
+ if (!getNextLine())
+ return;
+ line = thisLine;
+ if (line[0]=='.') {
+ if (line[1]=='.')
+ line++; // collapse double periode into one
+ else
+ if (line[1]==0)
+ done = true;
+ }
+ if (!done) {
+ resultAppend("<tr valign=top><td width=25%><pre><a href=\"http://dbinfo/");
+ char *space = strchr(line,' ');
+ if (space) {
+ resultAppend(codec->toUnicode(line,space-line));
+ resultAppend("\">");
+ resultAppend(codec->toUnicode(line,space-line));
+ resultAppend("</a></pre></td><td width=75%><pre>");
+ line = space+1;
+ if (line[0]=='"') {
+ line++; // remove double quote
+ char *quote = strchr(line, '\"');
+ if (quote)
+ quote[0]=0;
+ }
+ } else { // hmmm, malformated line...
+ resultAppend("\"></a></pre></td><td width=75%>");
+ }
+ resultAppend(line);
+ resultAppend("</pre></td></tr>\n");
+ }
+ }
+ resultAppend("</table>\n</body></html>");
+
+ if (!nextResponseOk(250)) // end of transmission: "250 ok ..."
+ return;
+}
+
+
+void DictAsyncClient::showDbInfo()
+{
+ cmdBuffer = "show info ";
+ cmdBuffer += codec->fromUnicode(job->query);
+ cmdBuffer += "\r\n";
+
+ if (!sendBuffer())
+ return;
+
+ if (!nextResponseOk(112)) // show info db: "112 database information follows"
+ return;
+
+ // html header...
+ resultAppend("<body>\n<p class=\"heading\">\n");
+ resultAppend(i18n("Database Information [%1]:").arg(job->query));
+ resultAppend("</p>\n<pre><p class=\"definition\">\n");
+
+ bool done(false);
+ char *line;
+ while (!done) {
+ if (!getNextLine())
+ return;
+ line = thisLine;
+ if (line[0]=='.') {
+ if (line[1]=='.')
+ line++; // collapse double periode into one
+ else
+ if (line[1]==0)
+ done = true;
+ }
+ if (!done) {
+ resultAppend(line);
+ resultAppend("\n");
+ }
+ }
+
+ resultAppend("</p></pre>\n</body></html>");
+
+ if (!nextResponseOk(250)) // end of transmission: "250 ok ..."
+ return;
+}
+
+
+void DictAsyncClient::showStrategies()
+{
+ cmdBuffer = "show strat\r\n";
+
+ if (!sendBuffer())
+ return;
+
+ if (!nextResponseOk(111)) // show strat: "111 n strategies present - text follows "
+ return;
+
+ // html header...
+ resultAppend("<body>\n<p class=\"heading\">\n");
+ resultAppend(i18n("Available Strategies:"));
+ resultAppend("\n</p>\n<table width=\"100%\" cols=2>\n");
+
+ bool done(false);
+ char *line;
+ while (!done) {
+ if (!getNextLine())
+ return;
+ line = thisLine;
+ if (line[0]=='.') {
+ if (line[1]=='.')
+ line++; // collapse double periode into one
+ else
+ if (line[1]==0)
+ done = true;
+ }
+ if (!done) {
+ resultAppend("<tr valign=top><td width=25%><pre>");
+ char *space = strchr(line,' ');
+ if (space) {
+ resultAppend(codec->toUnicode(line,space-line));
+ resultAppend("</pre></td><td width=75%><pre>");
+ line = space+1;
+ if (line[0]=='"') {
+ line++; // remove double quote
+ char *quote = strchr(line, '\"');
+ if (quote)
+ quote[0]=0;
+ }
+ } else { // hmmm, malformated line...
+ resultAppend("</pre></td><td width=75%><pre>");
+ }
+ resultAppend(line);
+ resultAppend("</pre></td></tr>\n");
+ }
+ }
+ resultAppend("</table>\n</body></html>");
+
+ if (!nextResponseOk(250)) // end of transmission: "250 ok ..."
+ return;
+}
+
+
+void DictAsyncClient::showInfo()
+{
+ cmdBuffer = "show server\r\n";
+
+ if (!sendBuffer())
+ return;
+
+ if (!nextResponseOk(114)) // show server: "114 server information follows"
+ return;
+
+ // html header...
+ resultAppend("<body>\n<p class=\"heading\">\n");
+ resultAppend(i18n("Server Information:"));
+ resultAppend("\n</p>\n<pre><p class=\"definition\">\n");
+
+ bool done(false);
+ char *line;
+ while (!done) {
+ if (!getNextLine())
+ return;
+ line = thisLine;
+ if (line[0]=='.') {
+ if (line[1]=='.')
+ line++; // collapse double periode into one
+ else
+ if (line[1]==0)
+ done = true;
+ }
+ if (!done) {
+ resultAppend(line);
+ resultAppend("\n");
+ }
+ }
+
+ resultAppend("</p></pre>\n</body></html>");
+
+ if (!nextResponseOk(250)) // end of transmission: "250 ok ..."
+ return;
+}
+
+
+void DictAsyncClient::update()
+{
+ cmdBuffer = "show strat\r\nshow db\r\n";
+
+ if (!sendBuffer())
+ return;
+
+ if (!nextResponseOk(111)) // show strat: "111 n strategies present - text follows "
+ return;
+
+ bool done(false);
+ char *line;
+ while (!done) {
+ if (!getNextLine())
+ return;
+ line = thisLine;
+ if (line[0]=='.') {
+ if (line[1]=='.')
+ line++; // collapse double periode into one
+ else
+ if (line[1]==0)
+ done = true;
+ }
+ if (!done) {
+ char *space = strchr(line,' ');
+ if (space) space[0] = 0; // terminate string, hack ;-)
+ job->strategies.append(codec->toUnicode(line));
+ }
+ }
+
+ if (!nextResponseOk(250)) // end of transmission: "250 ok ..."
+ return;
+
+ if (!nextResponseOk(110)) // show db: "110 n databases present - text follows "
+ return;
+
+ done = false;
+ while (!done) {
+ if (!getNextLine())
+ return;
+ line = thisLine;
+ if (line[0]=='.') {
+ if (line[1]=='.')
+ line++; // collapse double periode into one
+ else
+ if (line[1]==0)
+ done = true;
+ }
+ if (!done) {
+ char *space = strchr(line,' ');
+ if (space) space[0] = 0; // terminate string, hack ;-)
+ job->databases.append(codec->toUnicode(line));
+ }
+ }
+
+ if (!nextResponseOk(250)) // end of transmission: "250 ok ..."
+ return;
+}
+
+
+// connect, handshake and authorization
+void DictAsyncClient::openConnection()
+{
+ if (job->server.isEmpty()) {
+ job->error = JobData::ErrBadHost;
+ return;
+ }
+
+ KExtendedSocket ks;
+
+ ks.setAddress(job->server, job->port);
+ ks.setTimeout(job->timeout);
+ if (ks.connect() < 0) {
+ if (ks.status() == IO_LookupError)
+ job->error = JobData::ErrBadHost;
+ else if (ks.status() == IO_ConnectError) {
+ job->result = QString::null;
+ resultAppend(KExtendedSocket::strError(ks.status(), errno));
+ job->error = JobData::ErrConnect;
+ } else if (ks.status() == IO_TimeOutError)
+ job->error = JobData::ErrTimeout;
+ else {
+ job->result = QString::null;
+ resultAppend(KExtendedSocket::strError(ks.status(), errno));
+ job->error = JobData::ErrCommunication;
+ }
+
+ closeSocket();
+ return;
+ }
+ tcpSocket = ks.fd();
+ ks.release();
+
+ if (!nextResponseOk(220)) // connect: "220 text capabilities msg-id"
+ return;
+
+ cmdBuffer = "client \"Kdict ";
+ cmdBuffer += KDICT_VERSION;
+ cmdBuffer += "\"\r\n";
+
+ if (job->authEnabled)
+ if (strstr(thisLine,"auth")) { // skip auth if not supported
+ char *msgId = strrchr(thisLine,'<');
+
+ if ((!msgId)||(!job->user.length())) {
+ job->error = JobData::ErrAuthFailed;
+ closeSocket();
+ return;
+ }
+
+ KMD5 context;
+ context.update(QCString(msgId));
+ context.update(job->secret.local8Bit());
+
+ cmdBuffer += "auth " + job->user.local8Bit() + " ";
+ cmdBuffer += context.hexDigest();
+ cmdBuffer += "\r\n";
+ }
+
+ if (!sendBuffer())
+ return;
+
+ if (!nextResponseOk(250)) // client: "250 ok ..."
+ return;
+
+ if (job->authEnabled)
+ if (!nextResponseOk(230)) // auth: "230 Authentication successful"
+ return;
+}
+
+
+void DictAsyncClient::closeSocket()
+{
+ if (-1 != tcpSocket) {
+ ::close(tcpSocket);
+ tcpSocket = -1;
+ }
+}
+
+
+// send "quit" without timeout, without checks, close connection
+void DictAsyncClient::doQuit()
+{
+ fd_set fdsW;
+ timeval tv;
+
+ FD_ZERO(&fdsW);
+ FD_SET(tcpSocket, &fdsW);
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ int ret = KSocks::self()->select(FD_SETSIZE, NULL, &fdsW, NULL, &tv);
+
+ if (ret > 0) { // we can write...
+ cmdBuffer = "quit\r\n";
+ int todo = cmdBuffer.length();
+ KSocks::self()->write(tcpSocket,&cmdBuffer.data()[0],todo);
+ }
+ closeSocket();
+}
+
+
+// used by getNextLine()
+bool DictAsyncClient::waitForRead()
+{
+ fd_set fdsR,fdsE;
+ timeval tv;
+
+ int ret;
+ do {
+ FD_ZERO(&fdsR);
+ FD_SET(fdPipeIn, &fdsR);
+ FD_SET(tcpSocket, &fdsR);
+ FD_ZERO(&fdsE);
+ FD_SET(tcpSocket, &fdsE);
+ FD_SET(fdPipeIn, &fdsE);
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ ret = KSocks::self()->select(FD_SETSIZE, &fdsR, NULL, &fdsE, &tv);
+ } while ((ret<0)&&(errno==EINTR)); // don't get tricked by signals
+
+ if (ret == -1) { // select failed
+ if (job) {
+ job->result = QString::null;
+ resultAppend(strerror(errno));
+ job->error = JobData::ErrCommunication;
+ }
+ closeSocket();
+ return false;
+ }
+ if (ret == 0) { // Nothing happend, timeout
+ if (job)
+ job->error = JobData::ErrTimeout;
+ doQuit();
+ return false;
+ }
+ if (ret > 0) {
+ if (FD_ISSET(fdPipeIn,&fdsR)) { // stop signal
+ doQuit();
+ return false;
+ }
+ if (FD_ISSET(tcpSocket,&fdsE)||FD_ISSET(fdPipeIn,&fdsE)) { // broken pipe, etc
+ if (job) {
+ job->result = QString::null;
+ resultAppend(i18n("The connection is broken."));
+ job->error = JobData::ErrCommunication;
+ }
+ closeSocket();
+ return false;
+ }
+ if (FD_ISSET(tcpSocket,&fdsR)) // all ok
+ return true;
+ }
+
+ if (job) {
+ job->result = QString::null;
+ job->error = JobData::ErrCommunication;
+ }
+ closeSocket();
+ return false;
+}
+
+
+// used by sendBuffer() & connect()
+bool DictAsyncClient::waitForWrite()
+{
+ fd_set fdsR,fdsW,fdsE;
+ timeval tv;
+
+ int ret;
+ do {
+ FD_ZERO(&fdsR);
+ FD_SET(fdPipeIn, &fdsR);
+ FD_SET(tcpSocket, &fdsR);
+ FD_ZERO(&fdsW);
+ FD_SET(tcpSocket, &fdsW);
+ FD_ZERO(&fdsE);
+ FD_SET(tcpSocket, &fdsE);
+ FD_SET(fdPipeIn, &fdsE);
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+ ret = KSocks::self()->select(FD_SETSIZE, &fdsR, &fdsW, &fdsE, &tv);
+ } while ((ret<0)&&(errno==EINTR)); // don't get tricked by signals
+
+ if (ret == -1) { // select failed
+ if (job) {
+ job->result = QString::null;
+ resultAppend(strerror(errno));
+ job->error = JobData::ErrCommunication;
+ }
+ closeSocket();
+ return false;
+ }
+ if (ret == 0) { // nothing happend, timeout
+ if (job)
+ job->error = JobData::ErrTimeout;
+ closeSocket();
+ return false;
+ }
+ if (ret > 0) {
+ if (FD_ISSET(fdPipeIn,&fdsR)) { // stop signal
+ doQuit();
+ return false;
+ }
+ if (FD_ISSET(tcpSocket,&fdsR)||FD_ISSET(tcpSocket,&fdsE)||FD_ISSET(fdPipeIn,&fdsE)) { // broken pipe, etc
+ if (job) {
+ job->result = QString::null;
+ resultAppend(i18n("The connection is broken."));
+ job->error = JobData::ErrCommunication;
+ }
+ closeSocket();
+ return false;
+ }
+ if (FD_ISSET(tcpSocket,&fdsW)) // all ok
+ return true;
+ }
+ if (job) {
+ job->result = QString::null;
+ job->error = JobData::ErrCommunication;
+ }
+ closeSocket();
+ return false;
+}
+
+
+// remove start/stop signal
+void DictAsyncClient::clearPipe()
+{
+ fd_set fdsR;
+ timeval tv;
+ int selectRet;
+ char buf;
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ do {
+ FD_ZERO(&fdsR);
+ FD_SET(fdPipeIn,&fdsR);
+ if (1==(selectRet=select(FD_SETSIZE,&fdsR,NULL,NULL,&tv)))
+ if ( ::read(fdPipeIn, &buf, 1 ) == -1 )
+ ::perror( "clearPipe()" );
+ } while (selectRet == 1);
+}
+
+
+bool DictAsyncClient::sendBuffer()
+{
+ int ret;
+ int todo = cmdBuffer.length();
+ int done = 0;
+
+ while (todo > 0) {
+ if (!waitForWrite())
+ return false;
+ ret = KSocks::self()->write(tcpSocket,&cmdBuffer.data()[done],todo);
+ if (ret <= 0) {
+ if (job) {
+ job->result = QString::null;
+ resultAppend(strerror(errno));
+ job->error = JobData::ErrCommunication;
+ }
+ closeSocket();
+ return false;
+ } else {
+ done += ret;
+ todo -= ret;
+ }
+ }
+ return true;
+}
+
+
+// set thisLine to next complete line of input
+bool DictAsyncClient::getNextLine()
+{
+ thisLine = nextLine;
+ nextLine = strstr(thisLine,"\r\n");
+ if (nextLine) { // there is another full line in the inputbuffer
+ nextLine[0] = 0; // terminate string
+ nextLine[1] = 0;
+ nextLine+=2;
+ return true;
+ }
+ unsigned int div = inputEnd-thisLine+1; // hmmm, I need to fetch more input from the server...
+ memmove(input,thisLine,div); // save last, incomplete line
+ thisLine = input;
+ inputEnd = input+div-1;
+ do {
+ if ((inputEnd-input) > 9000) {
+ job->error = JobData::ErrMsgTooLong;
+ closeSocket();
+ return false;
+ }
+ if (!waitForRead())
+ return false;
+
+ int received;
+ do {
+ received = KSocks::self()->read(tcpSocket, inputEnd, inputSize-(inputEnd-input)-1);
+ } while ((received<0)&&(errno==EINTR)); // don't get tricked by signals
+
+ if (received <= 0) {
+ job->result = QString::null;
+ resultAppend(i18n("The connection is broken."));
+ job->error = JobData::ErrCommunication;
+ closeSocket();
+ return false;
+ }
+ inputEnd += received;
+ inputEnd[0] = 0; // terminate *char
+ } while (!(nextLine = strstr(thisLine,"\r\n")));
+ nextLine[0] = 0; // terminate string
+ nextLine[1] = 0;
+ nextLine+=2;
+ return true;
+}
+
+
+// reads next line and checks the response code
+bool DictAsyncClient::nextResponseOk(int code)
+{
+ if (!getNextLine())
+ return false;
+ if (strtol(thisLine,0L,0)!=code) {
+ handleErrors();
+ return false;
+ }
+ return true;
+}
+
+
+// reads next line and returns the response code
+bool DictAsyncClient::getNextResponse(int &code)
+{
+ if (!getNextLine())
+ return false;
+ code = strtol(thisLine,0L,0);
+ return true;
+}
+
+
+void DictAsyncClient::handleErrors()
+{
+ int len = strlen(thisLine);
+ if (len>80)
+ len = 80;
+ job->result = QString::null;
+ resultAppend(codec->toUnicode(thisLine,len));
+
+ switch (strtol(thisLine,0L,0)) {
+ case 420:
+ case 421:
+ job->error = JobData::ErrNotAvailable; // server unavailable
+ break;
+ case 500:
+ case 501:
+ job->error = JobData::ErrSyntax; // syntax error
+ break;
+ case 502:
+ case 503:
+ job->error = JobData::ErrCommandNotImplemented; // command not implemented
+ break;
+ case 530:
+ job->error = JobData::ErrAccessDenied; // access denied
+ break;
+ case 531:
+ job->error = JobData::ErrAuthFailed; // authentication failed
+ break;
+ case 550:
+ case 551:
+ job->error = JobData::ErrInvalidDbStrat; // invalid strategy/database
+ break;
+ case 554:
+ job->error = JobData::ErrNoDatabases; // no databases
+ break;
+ case 555:
+ job->error = JobData::ErrNoStrategies; // no strategies
+ break;
+ default:
+ job->error = JobData::ErrServerError;
+ }
+ doQuit();
+}
+
+
+void DictAsyncClient::resultAppend(const char* str)
+{
+ if (job)
+ job->result += codec->toUnicode(str);
+}
+
+
+void DictAsyncClient::resultAppend(QString str)
+{
+ if (job)
+ job->result += str;
+}
+
+
+
+//********* DictInterface ******************************************
+
+DictInterface::DictInterface()
+: newServer(false), clientDoneInProgress(false)
+{
+ if (::pipe(fdPipeIn ) == -1 ) {
+ perror( "Creating in pipe" );
+ KMessageBox::error(global->topLevel, i18n("Internal error:\nFailed to open pipes for internal communication."));
+ kapp->exit(1);
+ }
+ if (::pipe(fdPipeOut ) == -1 ) {
+ perror( "Creating out pipe" );
+ KMessageBox::error(global->topLevel, i18n("Internal error:\nFailed to open pipes for internal communication."));
+ kapp->exit(1);
+ }
+
+ if (-1 == fcntl(fdPipeIn[0],F_SETFL,O_NONBLOCK)) { // make socket non-blocking
+ perror("fcntl()");
+ KMessageBox::error(global->topLevel, i18n("Internal error:\nFailed to open pipes for internal communication."));
+ kapp->exit(1);
+ }
+
+ if (-1 == fcntl(fdPipeOut[0],F_SETFL,O_NONBLOCK)) { // make socket non-blocking
+ perror("fcntl()");
+ KMessageBox::error(global->topLevel, i18n("Internal error:\nFailed to open pipes for internal communication."));
+ kapp->exit(1);
+ }
+
+ notifier = new QSocketNotifier(fdPipeIn[0],QSocketNotifier::Read,this);
+ connect(notifier,SIGNAL(activated(int)),this,SLOT(clientDone()));
+
+ // initialize the KSocks stuff in the main thread, otherwise we get
+ // strange effects on FreeBSD
+ (void) KSocks::self();
+
+ client = new DictAsyncClient(fdPipeOut[0],fdPipeIn[1]);
+ if (0!=pthread_create(&threadID,0,&(client->startThread),client)) {
+ KMessageBox::error(global->topLevel, i18n("Internal error:\nUnable to create thread."));
+ kapp->exit(1);
+ }
+
+ jobList.setAutoDelete(true);
+}
+
+
+DictInterface::~DictInterface()
+{
+ disconnect(notifier,SIGNAL(activated(int)),this,SLOT(clientDone()));
+
+ if (0!=pthread_cancel(threadID))
+ kdWarning() << "pthread_cancel failed!" << endl;
+ if (0!=pthread_join(threadID,NULL))
+ kdWarning() << "pthread_join failed!" << endl;
+ delete client;
+
+ if ( ::close( fdPipeIn[0] ) == -1 ) {
+ perror( "Closing fdPipeIn[0]" );
+ }
+ if ( ::close( fdPipeIn[1] ) == -1 ) {
+ perror( "Closing fdPipeIn[1]" );
+ }
+ if ( ::close( fdPipeOut[0] ) == -1 ) {
+ perror( "Closing fdPipeOut[0]" );
+ }
+ if ( ::close( fdPipeOut[1] ) == -1 ) {
+ perror( "Closing fdPipeOut[1]" );
+ }
+}
+
+
+// inform the client when server settings get changed
+void DictInterface::serverChanged()
+{
+ newServer = true;
+}
+
+
+// cancel all pending jobs
+void DictInterface::stop()
+{
+ if (jobList.isEmpty()) {
+ return;
+ } else {
+ while (jobList.count()>1) // not yet started jobs can be deleted directly
+ jobList.removeLast();
+
+ if (!clientDoneInProgress) {
+ jobList.getFirst()->canceled = true; // clientDone() now ignores the results of this job
+ char buf; // write one char in the pipe to the async thread
+ if (::write(fdPipeOut[1],&buf,1) == -1)
+ ::perror( "stop()" );
+ }
+ }
+}
+
+
+void DictInterface::define(const QString &query)
+{
+ JobData *newJob = generateQuery(JobData::TDefine,query);
+ if (newJob)
+ insertJob(newJob);
+}
+
+
+void DictInterface::getDefinitions(QStringList query)
+{
+ JobData *newjob = new JobData(JobData::TGetDefinitions,newServer,global->server,global->port,
+ global->idleHold,global->timeout,global->pipeSize, global->encoding,global->authEnabled,
+ global->user,global->secret,global->headLayout);
+ newjob->defines = query;
+ newServer = false;
+ insertJob(newjob);
+}
+
+
+void DictInterface::match(const QString &query)
+{
+ JobData *newJob = generateQuery(JobData::TMatch,query);
+
+ if (newJob) {
+ if (global->currentStrategy == 0)
+ newJob->strategy = "."; // spell check strategy
+ else
+ newJob->strategy = global->strategies[global->currentStrategy].utf8();
+
+ insertJob(newJob);
+ }
+}
+
+
+// fetch detailed db info
+void DictInterface::showDbInfo(const QString &db)
+{
+ QString ndb = db.simplifyWhiteSpace(); // cleanup query string
+ if (ndb.isEmpty())
+ return;
+ if (ndb.length()>100) // shorten if necessary
+ ndb.truncate(100);
+ JobData *newjob = new JobData(JobData::TShowDbInfo,newServer,global->server,global->port,
+ global->idleHold,global->timeout,global->pipeSize, global->encoding,global->authEnabled,
+ global->user,global->secret,global->headLayout);
+ newServer = false;
+ newjob->query = ndb; // construct job...
+ insertJob(newjob);
+}
+
+
+void DictInterface::showDatabases()
+{
+ insertJob( new JobData(JobData::TShowDatabases,newServer,global->server,global->port,
+ global->idleHold,global->timeout,global->pipeSize, global->encoding,global->authEnabled,
+ global->user,global->secret,global->headLayout));
+ newServer = false;
+}
+
+
+void DictInterface::showStrategies()
+{
+ insertJob( new JobData(JobData::TShowStrategies,newServer,global->server,global->port,
+ global->idleHold,global->timeout,global->pipeSize, global->encoding,global->authEnabled,
+ global->user,global->secret,global->headLayout));
+ newServer = false;
+}
+
+
+void DictInterface::showInfo()
+{
+ insertJob( new JobData(JobData::TShowInfo,newServer,global->server,global->port,
+ global->idleHold,global->timeout,global->pipeSize, global->encoding,global->authEnabled,
+ global->user,global->secret,global->headLayout));
+ newServer = false;
+}
+
+
+// get info about databases & stratgies the server knows
+void DictInterface::updateServer()
+{
+ insertJob( new JobData(JobData::TUpdate,newServer,global->server,global->port,
+ global->idleHold,global->timeout,global->pipeSize, global->encoding,global->authEnabled,
+ global->user,global->secret,global->headLayout));
+ newServer = false;
+}
+
+
+// client-thread ended
+void DictInterface::clientDone()
+{
+ QString message;
+
+ cleanPipes(); // read from pipe so that notifier doesn´t fire again
+
+ if (jobList.isEmpty()) {
+ kdDebug(5004) << "This shouldn´t happen, the client-thread signaled termination, but the job list is empty" << endl;
+ return; // strange..
+ }
+
+ clientDoneInProgress = true;
+ QStringList::iterator it;
+ JobData* job = jobList.getFirst();
+ if (!job->canceled) { // non-interupted job?
+ if (JobData::ErrNoErr == job->error) {
+ switch (job->type) {
+ case JobData::TUpdate :
+ global->serverDatabases.clear();
+ for (it = job->databases.begin(); it != job->databases.end(); ++it)
+ global->serverDatabases.append(*it);
+ global->databases = global->serverDatabases;
+ for (int i = global->databaseSets.count()-1;i>=0;i--)
+ global->databases.prepend(global->databaseSets.at(i)->first());
+ global->databases.prepend(i18n("All Databases"));
+ global->currentDatabase = 0;
+
+ global->strategies.clear();
+ for (it = job->strategies.begin(); it != job->strategies.end(); ++it)
+ global->strategies.append(*it);
+ global->strategies.prepend(i18n("Spell Check"));
+ global->currentStrategy = 0;
+ message = i18n(" Received database/strategy list ");
+ emit stopped(message);
+ emit infoReady();
+ break;
+ case JobData::TDefine:
+ case JobData::TGetDefinitions:
+ if (job->type == JobData::TDefine) {
+ switch (job->numFetched) {
+ case 0:
+ message = i18n("No definitions found");
+ break;
+ case 1:
+ message = i18n("One definition found");
+ break;
+ default:
+ message = i18n("%1 definitions found").arg(job->numFetched);
+ }
+ } else {
+ switch (job->numFetched) {
+ case 0:
+ message = i18n(" No definitions fetched ");
+ break;
+ case 1:
+ message = i18n(" One definition fetched ");
+ break;
+ default:
+ message = i18n(" %1 definitions fetched ").arg(job->numFetched);
+ }
+ }
+ emit stopped(message);
+ emit resultReady(job->result, job->query);
+ break;
+ case JobData::TMatch:
+ switch (job->numFetched) {
+ case 0:
+ message = i18n(" No matching definitions found ");
+ break;
+ case 1:
+ message = i18n(" One matching definition found ");
+ break;
+ default:
+ message = i18n(" %1 matching definitions found ").arg(job->numFetched);
+ }
+ emit stopped(message);
+ emit matchReady(job->matches);
+ break;
+ default :
+ message = i18n(" Received information ");
+ emit stopped(message);
+ emit resultReady(job->result, job->query);
+ }
+ } else {
+ QString errMsg;
+ switch (job->error) {
+ case JobData::ErrCommunication:
+ errMsg = i18n("Communication error:\n\n");
+ errMsg += job->result;
+ break;
+ case JobData::ErrTimeout:
+ errMsg = i18n("A delay occurred which exceeded the\ncurrent timeout limit of %1 seconds.\nYou can modify this limit in the Preferences Dialog.").arg(global->timeout);
+ break;
+ case JobData::ErrBadHost:
+ errMsg = i18n("Unable to connect to:\n%1:%2\n\nCannot resolve hostname.").arg(job->server).arg(job->port);
+ break;
+ case JobData::ErrConnect:
+ errMsg = i18n("Unable to connect to:\n%1:%2\n\n").arg(job->server).arg(job->port);
+ errMsg += job->result;
+ break;
+ case JobData::ErrRefused:
+ errMsg = i18n("Unable to connect to:\n%1:%2\n\nThe server refused the connection.").arg(job->server).arg(job->port);
+ break;
+ case JobData::ErrNotAvailable:
+ errMsg = i18n("The server is temporarily unavailable.");
+ break;
+ case JobData::ErrSyntax:
+ errMsg = i18n("The server reported a syntax error.\nThis shouldn't happen -- please consider\nwriting a bug report.");
+ break;
+ case JobData::ErrCommandNotImplemented:
+ errMsg = i18n("A command that Kdict needs isn't\nimplemented on the server.");
+ break;
+ case JobData::ErrAccessDenied:
+ errMsg = i18n("Access denied.\nThis host is not allowed to connect.");
+ break;
+ case JobData::ErrAuthFailed:
+ errMsg = i18n("Authentication failed.\nPlease enter a valid username and password.");
+ break;
+ case JobData::ErrInvalidDbStrat:
+ errMsg = i18n("Invalid database/strategy.\nYou probably need to use Server->Get Capabilities.");
+ break;
+ case JobData::ErrNoDatabases:
+ errMsg = i18n("No databases available.\nIt is possible that you need to authenticate\nwith a valid username/password combination to\ngain access to any databases.");
+ break;
+ case JobData::ErrNoStrategies:
+ errMsg = i18n("No strategies available.");
+ break;
+ case JobData::ErrServerError:
+ errMsg = i18n("The server sent an unexpected reply:\n\"%1\"\nThis shouldn't happen, please consider\nwriting a bug report").arg(job->result);
+ break;
+ case JobData::ErrMsgTooLong:
+ errMsg = i18n("The server sent a response with a text line\nthat was too long.\n(RFC 2229: max. 1024 characters/6144 octets)");
+ break;
+ case JobData::ErrNoErr: // make compiler happy
+ errMsg = i18n("No Errors");
+ }
+ message = i18n(" Error ");
+ emit stopped(message);
+ KMessageBox::error(global->topLevel, errMsg);
+ }
+ } else {
+ message = i18n(" Stopped ");
+ emit stopped(message);
+ }
+
+ clientDoneInProgress = false;
+
+ client->removeJob();
+ jobList.removeFirst(); // this job is now history
+ if (!jobList.isEmpty()) // work to be done?
+ startClient(); // => restart client
+}
+
+
+JobData* DictInterface::generateQuery(JobData::QueryType type, QString query)
+{
+ query = query.simplifyWhiteSpace(); // cleanup query string
+ if (query.isEmpty())
+ return 0L;
+ if (query.length()>300) // shorten if necessary
+ query.truncate(300);
+ query = query.replace(QRegExp("[\"\\]"), ""); // remove remaining illegal chars...
+ if (query.isEmpty())
+ return 0L;
+
+ JobData *newjob = new JobData(type,newServer,global->server,global->port,
+ global->idleHold,global->timeout,global->pipeSize, global->encoding, global->authEnabled,
+ global->user,global->secret,global->headLayout);
+ newServer = false;
+ newjob->query = query; // construct job...
+
+ if (global->currentDatabase == 0) // all databases
+ newjob->databases.append("*");
+ else {
+ if ((global->currentDatabase > 0)&& // database set
+ (global->currentDatabase < global->databaseSets.count()+1)) {
+ for (int i = 0;i<(int)global->serverDatabases.count();i++)
+ if ((global->databaseSets.at(global->currentDatabase-1))->findIndex(global->serverDatabases[i])>0)
+ newjob->databases.append(global->serverDatabases[i].utf8().data());
+ if (newjob->databases.count()==0) {
+ KMessageBox::sorry(global->topLevel, i18n("Please select at least one database."));
+ delete newjob;
+ return 0L;
+ }
+ } else { // one database
+ newjob->databases.append(global->databases[global->currentDatabase].utf8().data());
+ }
+ }
+
+ return newjob;
+}
+
+
+void DictInterface::insertJob(JobData* job)
+{
+ if (jobList.isEmpty()) { // Client has nothing to do, start directly
+ jobList.append(job);
+ startClient();
+ } else { // there are other pending jobs...
+ stop();
+ jobList.append(job);
+ }
+}
+
+
+// start client-thread
+void DictInterface::startClient()
+{
+ cleanPipes();
+ if (jobList.isEmpty()) {
+ kdDebug(5004) << "This shouldn´t happen, startClient called, but clientList is empty" << endl;
+ return;
+ }
+
+ client->insertJob(jobList.getFirst());
+ char buf; // write one char in the pipe to the async thread
+ if (::write(fdPipeOut[1],&buf,1) == -1)
+ ::perror( "startClient()" );
+
+ QString message;
+ switch (jobList.getFirst()->type) {
+ case JobData::TDefine:
+ case JobData::TGetDefinitions:
+ case JobData::TMatch:
+ message = i18n(" Querying server... ");
+ break;
+ case JobData::TShowDatabases:
+ case JobData::TShowStrategies:
+ case JobData::TShowInfo:
+ case JobData::TShowDbInfo:
+ message = i18n(" Fetching information... ");
+ break;
+ case JobData::TUpdate:
+ message = i18n(" Updating server information... ");
+ break;
+ }
+ emit started(message);
+}
+
+
+// empty the pipes, so that notifier stops firing
+void DictInterface::cleanPipes()
+{
+ fd_set rfds;
+ struct timeval tv;
+ int ret;
+ char buf;
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+
+ do {
+ FD_ZERO(&rfds);
+ FD_SET(fdPipeIn[0],&rfds);
+ if (1==(ret=select(FD_SETSIZE,&rfds,NULL,NULL,&tv)))
+ if ( ::read(fdPipeIn[0], &buf, 1 ) == -1 )
+ ::perror( "cleanPipes" );
+ } while (ret == 1);
+
+ do {
+ FD_ZERO(&rfds);
+ FD_SET(fdPipeOut[0],&rfds);
+ if (1==(ret=select(FD_SETSIZE,&rfds,NULL,NULL,&tv)))
+ if ( ::read(fdPipeOut[0], &buf, 1 ) == -1 )
+ ::perror( "cleanPipes" );
+ } while (ret == 1);
+}
+
+//--------------------------------
+
+#include "dict.moc"