/*************************************************************************** * fqterm, a terminal emulator for both BBS and *nix. * * Copyright (C) 2008 fqterm development group. * * * * 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; either version 2 of the License, or * * (at your option) any later version. * * * * 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 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., * * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * ***************************************************************************/ #include #include #include #include #include #include #include #include #include "fqterm.h" #ifdef HAVE_PYTHON #include #endif class SleeperThread : public QThread { public: static void msleep(unsigned long msecs) { QThread::msleep(msecs); } }; #ifdef HAVE_CONFIG_H #include #endif #ifdef HAVE_PYTHON #include "fqterm_window.h" #include "fqterm_buffer.h" #include "fqterm_text_line.h" #include "fqterm_param.h" #include "fqterm_session.h" #include "fqterm_python.h" #include "fqterm_path.h" #include "common.h" namespace FQTerm { /* ************************************************************************** * * Pythons Embedding * * ***************************************************************************/ QString getException() { PyObject *pType = NULL, *pValue = NULL, *pTb = NULL, *pName, *pTraceback; PyErr_Fetch(&pType, &pValue, &pTb); pName = PyString_FromString("traceback"); pTraceback = PyImport_Import(pName); Py_DECREF(pName); if (pTraceback == NULL) { return "General Error in Python Callback"; } pName = PyString_FromString("format_exception"); PyObject *pRes = PyObject_CallMethodObjArgs(pTraceback, pName, pType, pValue, pTb, NULL); Py_DECREF(pName); Py_DECREF(pTraceback); Py_XDECREF(pType); Py_XDECREF(pValue); Py_XDECREF(pTb); if (pRes == NULL) { return "General Error in Python Callback"; } pName = PyString_FromString("string"); PyObject *pString = PyImport_Import(pName); Py_DECREF(pName); if (pString == NULL) { return "General Error in Python Callback"; } pName = PyString_FromString("join"); PyObject *pErr = PyObject_CallMethodObjArgs(pString, pName, pRes, NULL); Py_DECREF(pName); Py_DECREF(pString); Py_DECREF(pRes); if (pErr == NULL) { return "General Error in Python Callback"; } QString str(PyString_AsString(pErr)); Py_DECREF(pErr); return str; } QString getErrOutputFile(FQTermWindow *lp) { // file name QString str2; str2.setNum(long(lp)); str2 += ".err"; // path return getPath(USER_CONFIG) + str2; } // copy current article, for backward-compatible use only. // for new code, please use getArticle static PyObject *fqterm_copyArticle(PyObject *, PyObject *args) { long lp; if (!PyArg_ParseTuple(args, "l", &lp)) { return NULL; } FQTermWindow *termWindow_ = (FQTermWindow*)lp; QStringList strList; QString strArticle; QReadWriteLock& bufferLock = termWindow_->getSession()->getBufferLock(); QReadLocker locker(&bufferLock); while (1) { // check if there is duplicated string // it starts from the end in the range of one screen height // so this is a non-greedy match QString strTemp; termWindow_->getSession()->getBuffer()->getTextLineInTerm(0)->getAllPlainText(strTemp); strTemp = strTemp.trimmed(); int i = 0; int start = 0; for (QStringList::Iterator it = strList.end(); it != strList.begin() && i < termWindow_->getSession()->getBuffer()->getNumRows() - 1; // not exceeeding the last screen --it, i++) { if (*it != strTemp) { continue; } QStringList::Iterator it2 = it; bool dup = true; // match more to see if it's duplicated for (int j = 0; j <= i; j++, it2++) { QString str1; termWindow_->getSession()->getBuffer()->getTextLineInTerm(j)->getAllPlainText(str1); if (*it2 != str1.trimmed()) { dup = false; break; } } if (dup) { // set the start point start = i + 1; break; } } // add new lines for (i = start; i < termWindow_->getSession()->getBuffer()->getNumRows() - 1; i++) { QString tmp; termWindow_->getSession()->getBuffer()->getTextLineInTerm(i)->getAllPlainText(tmp); strList += tmp.trimmed(); } // the end of article QString testEnd; termWindow_->getSession()->getBuffer()->getTextLineInTerm(termWindow_->getSession()->getBuffer()->getNumRows() - 1)->getAllPlainText(testEnd); if (testEnd.indexOf("%") == -1) { break; } // continue termWindow_->writeString_ts(" "); // TODO: fixme if (!termWindow_->getSession()->getWaitCondition().wait(&bufferLock, 10000)) { // // timeout break; } } #if defined(_OS_WIN32_) || defined(Q_OS_WIN32) strArticle = strList.join("\r\n"); #else strArticle = strList.join("\n"); #endif PyObject *py_text = PyString_FromString(strArticle.toUtf8()); Py_INCREF(py_text); return py_text; } static PyObject *fqterm_getArticle(PyObject *, PyObject *args) { long lp; int timeout; int succeed = 1; if (!PyArg_ParseTuple(args, "li", &lp, &timeout)) { return NULL; } FQTermWindow *termWindow_ = (FQTermWindow*)lp; QStringList strList; QString strArticle; QReadWriteLock& bufferLock = termWindow_->getSession()->getBufferLock(); while (!bufferLock.tryLockForRead()) {} while (1) { // check it there is duplicated string // it starts from the end in the range of one screen height // so this is a non-greedy match QString strTemp; termWindow_->getSession()->getBuffer()->getTextLineInTerm(0)->getAllPlainText(strTemp); strTemp = strTemp.trimmed(); int i = 0; int start = 0; for (QStringList::Iterator it = strList.end(); it != strList.begin() && i < termWindow_->getSession()->getBuffer()->getNumRows() - 1; // not exceeeding the last screen --it, i++) { if (it == strList.end() || *it != strTemp) { continue; } QStringList::Iterator it2 = it; bool dup = true; // match more to see if it's duplicated for (int j = 0; j <= i && it2 != strList.end(); j++, it2++) { QString str1; termWindow_->getSession()->getBuffer()->getTextLineInTerm(j)->getAllPlainText(str1); if (*it2 != str1.trimmed()) { dup = false; break; } } if (dup) { // set the start point start = i + 1; break; } } // add new lines for (i = start; i < termWindow_->getSession()->getBuffer()->getNumRows() - 1; i++) { QString tmp; termWindow_->getSession()->getBuffer()->getTextLineInTerm(i)->getAllPlainText(tmp); strList += tmp.trimmed(); } // the end of article QString testEnd; termWindow_->getSession()->getBuffer()->getTextLineInTerm(termWindow_->getSession()->getBuffer()->getNumRows() - 1)->getAllPlainText(testEnd); if (testEnd.indexOf("%") == -1) { break; } // continue termWindow_->writeString_ts(" "); // TODO: fixme if (!termWindow_->getSession()->getWaitCondition().wait(&bufferLock, timeout)) { // timeout succeed = 0; break; } } bufferLock.unlock(); if (succeed) strArticle = strList.join(OS_NEW_LINE); PyObject *py_res = Py_BuildValue("si", (const char*)strArticle.toUtf8().data(), succeed); Py_INCREF(py_res); return py_res; } static PyObject *fqterm_formatError(PyObject *, PyObject *args) { long lp; if (!PyArg_ParseTuple(args, "l", &lp)) { return NULL; } QString strErr; QString filename = getErrOutputFile((FQTermWindow*)lp); QDir d; if (d.exists(filename)) { QFile file(filename); file.open(QIODevice::ReadOnly); QTextStream is(&file); while (!is.atEnd()) { strErr += is.readLine(); // line of text excluding '\n' strErr += '\n'; } file.close(); d.remove(filename); } if (!strErr.isEmpty()) { ((FQTermWindow*)lp)->getPythonErrorMessage() = strErr; // TODO: fixme //qApp->postEvent((FQTermWindow*)lp, new QCustomEvent(PYE_ERROR)); } else { // TODO: fixme //qApp->postEvent((FQTermWindow*)lp, new QCustomEvent(PYE_FINISH)); } Py_INCREF(Py_None); return Py_None; } // caret x static PyObject *fqterm_caretX(PyObject *, PyObject *args) { long lp; if (!PyArg_ParseTuple(args, "l", &lp)) { return NULL; } int x = ((FQTermWindow*)lp)->getSession()->getBuffer()->getCaretColumn(); PyObject *py_x = Py_BuildValue("i", x); Py_INCREF(py_x); return py_x; } // caret y static PyObject *fqterm_caretY(PyObject *, PyObject *args) { long lp; if (!PyArg_ParseTuple(args, "l", &lp)) { return NULL; } int y = ((FQTermWindow*)lp)->getSession()->getBuffer()->getCaretRow(); PyObject *py_y = Py_BuildValue("i", y); Py_INCREF(py_y); return py_y; } // columns static PyObject *fqterm_columns(PyObject *, PyObject *args) { long lp; if (!PyArg_ParseTuple(args, "l", &lp)) { return NULL; } int numColumns = ((FQTermWindow*)lp)->getSession()->getBuffer()->getNumColumns(); PyObject *py_columns = Py_BuildValue("i", numColumns); Py_INCREF(py_columns); return py_columns; } // rows static PyObject *fqterm_rows(PyObject *, PyObject *args) { long lp; if (!PyArg_ParseTuple(args, "l", &lp)) { return NULL; } int rows = ((FQTermWindow*)lp)->getSession()->getBuffer()->getNumRows(); PyObject *py_rows = Py_BuildValue("i", rows); Py_INCREF(py_rows); return py_rows; } // sned string to server static PyObject *fqterm_sendString(PyObject *, PyObject *args) { char *pstr; long lp; if (!PyArg_ParseTuple(args, "ls", &lp, &pstr)) { return NULL; } ((FQTermWindow*)lp)->writeRawString_ts(U82U(pstr)); Py_INCREF(Py_None); return Py_None; } // input should be utf8. // same as above except parsing string first "\n" "^p" etc static PyObject *fqterm_sendParsedString(PyObject *, PyObject *args) { char *pstr; long lp; int len; if (!PyArg_ParseTuple(args, "ls", &lp, &pstr)) { return NULL; } len = strlen(pstr); ((FQTermWindow*)lp)->writeString_ts(U82U(pstr)); Py_INCREF(Py_None); return Py_None; } // get text at line static PyObject *fqterm_getText(PyObject *, PyObject *args) { long lp; int numRows; if (!PyArg_ParseTuple(args, "li", &lp, &numRows)) { return NULL; } QString str; if (numRows < ((FQTermWindow*)lp)->getSession()->getBuffer()->getNumRows()) ((FQTermWindow*)lp)->getSession()->getBuffer()->getTextLineInTerm(numRows)->getAllPlainText(str); PyObject *py_text = PyString_FromString(U2U8(str)); Py_INCREF(py_text); return py_text; } // get text with attributes static PyObject *fqterm_getAttrText(PyObject *, PyObject *args) { long lp; int numRows; if (!PyArg_ParseTuple(args, "li", &lp, &numRows)) { return NULL; } QString str; if (numRows < ((FQTermWindow*)lp)->getSession()->getBuffer()->getNumRows()) ((FQTermWindow*)lp)->getSession()->getBuffer()->getTextLineInTerm(numRows)->getAllAnsiText(str); PyObject *py_text = PyString_FromString(U2U8(str)); Py_INCREF(py_text); return py_text; } // is host connected static PyObject *fqterm_isConnected(PyObject *, PyObject *args) { long lp; if (!PyArg_ParseTuple(args, "l", &lp)) { return NULL; } bool connected = ((FQTermWindow*)lp)->isConnected(); PyObject *py_connected = Py_BuildValue("i", connected ? 1 : 0); Py_INCREF(py_connected); return py_connected; } // disconnect from host static PyObject *fqterm_disconnect(PyObject *, PyObject *args) { long lp; if (!PyArg_ParseTuple(args, "l", &lp)) { return NULL; } ((FQTermWindow*)lp)->disconnect(); Py_INCREF(Py_None); return Py_None; } // reconnect to host static PyObject *fqterm_reconnect(PyObject *, PyObject *args) { long lp; if (!PyArg_ParseTuple(args, "l", &lp)) { return NULL; } ((FQTermWindow*)lp)->getSession()->reconnect(); Py_INCREF(Py_None); return Py_None; } // bbs encoding 0-GBK 1-BIG5 //FIXME: UTF8 and HKSCS static PyObject *fqterm_getBBSCodec(PyObject *, PyObject *args) { long lp; if (!PyArg_ParseTuple(args, "l", &lp)) { return NULL; } PyObject *py_codec = PyString_FromString(((FQTermWindow*)lp) ->getSession()->param().serverEncodingID_ == 0 ? "GBK" : "Big5"); Py_INCREF(py_codec); return py_codec; } // host address static PyObject *fqterm_getAddress(PyObject *, PyObject *args) { long lp; if (!PyArg_ParseTuple(args, "l", &lp)) { return NULL; } PyObject *py_addr = PyString_FromString(((FQTermWindow*)lp) ->getSession()->param().hostAddress_.toLocal8Bit()); Py_INCREF(py_addr); return py_addr; } // host port number static PyObject *fqterm_getPort(PyObject *, PyObject *args) { long lp; if (!PyArg_ParseTuple(args, "l", &lp)) { return NULL; } PyObject *py_port = Py_BuildValue("i", ((FQTermWindow*)lp)->getSession()->param().port_); Py_INCREF(py_port); return py_port; } // connection protocol 0-telnet 1-SSH1 2-SSH2 static PyObject *fqterm_getProtocol(PyObject *, PyObject *args) { long lp; if (!PyArg_ParseTuple(args, "l", &lp)) { return NULL; } PyObject *py_port = Py_BuildValue("i", ((FQTermWindow*)lp) ->getSession()->param().protocolType_); Py_INCREF(py_port); return py_port; } // key to reply msg static PyObject *fqterm_getReplyKey(PyObject *, PyObject *args) { long lp; if (!PyArg_ParseTuple(args, "l", &lp)) { return NULL; } PyObject *py_key = PyString_FromString(((FQTermWindow*)lp) ->getSession()->param().replyKeyCombination_.toLocal8Bit()); Py_INCREF(py_key); return py_key; } // url under mouse static PyObject *fqterm_getURL(PyObject *, PyObject *args) { long lp; if (!PyArg_ParseTuple(args, "l", &lp)) { return NULL; } PyObject *py_url = PyString_FromString(((FQTermWindow*)lp)->getSession()->getUrl().toUtf8().constData()); Py_INCREF(py_url); return py_url; } // preview image link static PyObject *fqterm_previewImage(PyObject *, PyObject *args) { long lp; char *url; if (!PyArg_ParseTuple(args, "ls", &lp, &url)) { return NULL; } ((FQTermWindow*)lp)->getHttpHelper(url, true); Py_INCREF(Py_None); return Py_None; } // convert string from UTF8 to specified encoding static PyObject *fqterm_fromUTF8(PyObject *, PyObject *args) { char *str, *enc; if (!PyArg_ParseTuple(args, "ss", &str, &enc)) { return NULL; } QTextCodec *encodec = QTextCodec::codecForName(enc); QTextCodec *utf8 = QTextCodec::codecForName("utf8"); PyObject *py_str = PyString_FromString(encodec->fromUnicode(utf8->toUnicode (str))); Py_INCREF(py_str); return py_str; } // convert string from specified encoding to UTF8 static PyObject *fqterm_toUTF8(PyObject *, PyObject *args) { char *str, *enc; if (!PyArg_ParseTuple(args, "ss", &str, &enc)) { return NULL; } QTextCodec *encodec = QTextCodec::codecForName(enc); QTextCodec *utf8 = QTextCodec::codecForName("utf8"); PyObject *py_str = PyString_FromString(utf8->fromUnicode(encodec->toUnicode (str))); Py_INCREF(py_str); return py_str; } static PyObject *fqterm_wait(PyObject *, PyObject *args) { long t; if (!PyArg_ParseTuple(args, "l", &t)) { return NULL; } SleeperThread::msleep(t); Py_INCREF(Py_None); return Py_None; } PyMethodDef fqterm_methods[] = { { "formatError", (PyCFunction)fqterm_formatError, METH_VARARGS, "get the traceback info" } , { "getArticle", (PyCFunction)fqterm_getArticle, METH_VARARGS, "copy current article" } , { "copyArticle", (PyCFunction)fqterm_copyArticle, METH_VARARGS, "copy current article (obsolete)" } , { "getText", (PyCFunction)fqterm_getText, METH_VARARGS, "get text at line#" } , { "getAttrText", (PyCFunction)fqterm_getAttrText, METH_VARARGS, "get attr text at line#" } , { "sendString", (PyCFunction)fqterm_sendString, METH_VARARGS, "send string to server" } , { "sendParsedString", (PyCFunction)fqterm_sendParsedString, METH_VARARGS, "send string with escape" } , { "caretX", (PyCFunction)fqterm_caretX, METH_VARARGS, "caret x" } , { "caretY", (PyCFunction)fqterm_caretY, METH_VARARGS, "caret y" } , { "columns", (PyCFunction)fqterm_columns, METH_VARARGS, "screen width" } , { "rows", (PyCFunction)fqterm_rows, METH_VARARGS, "screen height" } , { "isConnected", (PyCFunction)fqterm_isConnected, METH_VARARGS, "connected to server or not" } , { "disconnect", (PyCFunction)fqterm_disconnect, METH_VARARGS, "disconnect from server" } , { "reconnect", (PyCFunction)fqterm_reconnect, METH_VARARGS, "reconnect" } , { "getBBSCodec", (PyCFunction)fqterm_getBBSCodec, METH_VARARGS, "get the bbs encoding, GBK or Big5" } , { "getAddress", (PyCFunction)fqterm_getAddress, METH_VARARGS, "get the bbs address" } , { "getPort", (PyCFunction)fqterm_getPort, METH_VARARGS, "get the bbs port number" } , { "getProtocol", (PyCFunction)fqterm_getProtocol, METH_VARARGS, "get the bbs protocol, 0/1/2 TELNET/SSH1/SSH2" } , { "getReplyKey", (PyCFunction)fqterm_getReplyKey, METH_VARARGS, "get the key to reply messages" } , { "getURL", (PyCFunction)fqterm_getURL, METH_VARARGS, "get the url string under mouse" } , { "previewImage", (PyCFunction)fqterm_previewImage, METH_VARARGS, "preview the image link" } , { "fromUTF8", (PyCFunction)fqterm_fromUTF8, METH_VARARGS, "decode from utf8 to string in specified codec" } , { "toUTF8", (PyCFunction)fqterm_toUTF8, METH_VARARGS, "decode from string in specified codec to utf8" } , { "wait", (PyCFunction)fqterm_wait, METH_VARARGS, "wait for x ms" } , { NULL, (PyCFunction)NULL, 0, NULL } }; FQTermPythonHelper::FQTermPythonHelper() : mainThreadState_(NULL) { // initialize Python Py_Initialize(); // initialize thread support PyEval_InitThreads(); // save a pointer to the main PyThreadState object mainThreadState_ = PyThreadState_Get(); // add path PyRun_SimpleString("import sys\n"); QString pathCmd; pathCmd = "sys.path.insert(0,'"; pathCmd += getPath(RESOURCE)+"script')"; PyRun_SimpleString(fq_strdup(pathCmd.toUtf8().data())); Py_InitModule4("fqterm", fqterm_methods, NULL,(PyObject*)NULL,PYTHON_API_VERSION); // release the lock PyEval_ReleaseLock(); } FQTermPythonHelper::~FQTermPythonHelper() { // shut down the interpreter PyInterpreterState * mainInterpreterState = mainThreadState_->interp; // create a thread state object for this thread PyThreadState * myThreadState = PyThreadState_New(mainInterpreterState); PyThreadState_Swap(myThreadState); PyEval_AcquireLock(); Py_Finalize(); } } // namespace FQTerm #endif //HAVE_PYTHON