X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Flyxserver.C;h=a6213f9deefe2fce95e8559d0ff8c1d7d8daef65;hb=d13d6da879462707cd947cdf00c0cf7c3dfac924;hp=3e16f061389964d1d8574028a98af5e0d31460f3;hpb=5843696fe99457523f27606f41e0bac584dd4791;p=lyx.git diff --git a/src/lyxserver.C b/src/lyxserver.C index 3e16f06138..a6213f9dee 100644 --- a/src/lyxserver.C +++ b/src/lyxserver.C @@ -1,59 +1,58 @@ -/* This file is part of - * ====================================================== - * - * LyX, The Document Processor - * - * Copyright 1995 Matthias Ettrich - * Copyright 1995-2001 The LyX Team. +/** + * \file lyxserver.C + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Lars Gullik Bjønnes + * \author Jean-Marc Lasgouttes + * \author Angus Leeming + * \author John Levon * - * ====================================================== */ + * Full author contact details are available in file CREDITS. + */ /** Docu : To use the lyxserver define the name of the pipe in your - lyxrc: - \serverpipe "/home/myhome/.lyxpipe" - Then use .lyxpipe.in and .lyxpipe.out to communicate to LyX. - Each message consists of a single line in ASCII. Input lines - (client -> LyX) have the following format: - "LYXCMD:::" - Answers from LyX look like this: - "INFO:::" + lyxrc: + \serverpipe "/home/myhome/.lyxpipe" + Then use .lyxpipe.in and .lyxpipe.out to communicate to LyX. + Each message consists of a single line in ASCII. Input lines + (client -> LyX) have the following format: + "LYXCMD:::" + Answers from LyX look like this: + "INFO:::" [asierra970531] Or like this in case of error: - "ERROR:::" - where and are just echoed. - If LyX notifies about a user defined extension key-sequence, - the line looks like this: - "NOTIFY:" + "ERROR:::" + where and are just echoed. + If LyX notifies about a user defined extension key-sequence, + the line looks like this: + "NOTIFY:" [asierra970531] New server-only messages to implement a simple protocol - "LYXSRV::" - where can be "hello" or "bye". If hello is - received LyX will inform the client that it's listening its - messages, and 'bye' will inform that lyx is closing. + "LYXSRV::" + where can be "hello" or "bye". If hello is + received LyX will inform the client that it's listening its + messages, and 'bye' will inform that lyx is closing. - See development/server_monitor.c for an example client. + See development/server_monitor.c for an example client. Purpose: implement a client/server lib for LyX */ #include -#include -#include -#include -#include -#include -#include FORMS_H_LOCATION - -#ifdef __GNUG__ -#pragma implementation -#endif - #include "lyxserver.h" -#include "lyx_main.h" #include "debug.h" +#include "funcrequest.h" #include "LyXAction.h" #include "lyxfunc.h" #include "support/lstrings.h" #include "support/lyxlib.h" +#include "frontends/lyx_gui.h" + +#include + +#include +#include +#include #ifdef __EMX__ #include @@ -62,44 +61,31 @@ #define INCL_DOSNMPIPES #define INCL_DOSERRORS #include -#include "os2_errortable.h" +#include "support/os2_errortable.h" #endif +using lyx::support::compare; +using lyx::support::rtrim; +using lyx::support::split; +using lyx::support::unlink; + using std::endl; +using std::string; + // provide an empty mkfifo() if we do not have one. This disables the -// lyxserver. +// lyxserver. #ifndef HAVE_MKFIFO int mkfifo(char const * __path, mode_t __mode) { - return 0; + return 0; } #endif -/* === variables ========================================================= */ - -extern LyXAction lyxaction; - - -extern "C" { - - // C wrapper - static - void C_LyXComm_callback(int fd, void *v) - { - LyXComm::callback(fd, v); - } - -} - - -// LyXComm class - -// Open pipes void LyXComm::openConnection() { lyxerr[Debug::LYXSERVER] << "LyXComm: Opening connection" << endl; - + // If we are up, that's an error if (ready) { lyxerr << "LyXComm: Already connected" << endl; @@ -107,53 +93,60 @@ void LyXComm::openConnection() } // We assume that we don't make it ready = false; - - if (pipename.empty()) return; + + if (pipename.empty()) { + lyxerr[Debug::LYXSERVER] + << "LyXComm: server is disabled, nothing to do" + << endl; + return; + } if ((infd = startPipe(inPipeName(), false)) == -1) return; if ((outfd = startPipe(outPipeName(), true)) == -1) { - endPipe(infd, inPipeName()); + endPipe(infd, inPipeName(), false); return; } - + if (fcntl(outfd, F_SETFL, O_NONBLOCK) < 0) { lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName() << '\n' << strerror(errno) << endl; return; } - + // We made it! ready = true; lyxerr[Debug::LYXSERVER] << "LyXComm: Connection established" << endl; } - + /// Close pipes void LyXComm::closeConnection() { - lyxerr[Debug::LYXSERVER] << "LyXComm: Closing connection" << endl; + lyxerr[Debug::LYXSERVER] << "LyXComm: Closing connection" << endl; if (pipename.empty()) { - return; + lyxerr[Debug::LYXSERVER] + << "LyXComm: server is disabled, nothing to do" + << endl; + return; } if (!ready) { lyxerr << "LyXComm: Already disconnected" << endl; return; } - - endPipe(infd, inPipeName()); - endPipe(outfd, outPipeName()); - + + endPipe(infd, inPipeName(), false); + endPipe(outfd, outPipeName(), true); + ready = false; } + int LyXComm::startPipe(string const & filename, bool write) { - int fd; - #ifdef __EMX__ HPIPE os2fd; APIRET rc; @@ -169,7 +162,7 @@ int LyXComm::startPipe(string const & filename, bool write) pipename.erase(); return -1; } - + if (rc != NO_ERROR) { errnum = TranslateOS2Error(rc); lyxerr <<"LyXComm: Could not create pipe " << filename @@ -185,8 +178,8 @@ int LyXComm::startPipe(string const & filename, bool write) return -1; }; // Imported handles can be used both with OS/2 APIs and emx - // library functions. - fd = _imphandle(os2fd); + // library functions. + int const fd = _imphandle(os2fd); #else if (::access(filename.c_str(), F_OK) == 0) { lyxerr << "LyXComm: Pipe " << filename << " already exists.\n" @@ -195,38 +188,44 @@ int LyXComm::startPipe(string const & filename, bool write) pipename.erase(); return -1; } - + if (::mkfifo(filename.c_str(), 0600) < 0) { lyxerr << "LyXComm: Could not create pipe " << filename << '\n' << strerror(errno) << endl; return -1; }; - fd = ::open(filename.c_str(), write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK)); + int const fd = ::open(filename.c_str(), + write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK)); #endif - + if (fd < 0) { lyxerr << "LyXComm: Could not open pipe " << filename << '\n' << strerror(errno) << endl; - lyx::unlink(filename); + unlink(filename); return -1; } - - if (!write) - fl_add_io_callback(fd, FL_READ, C_LyXComm_callback, this); - + + if (!write) { + lyx_gui::register_socket_callback(fd, boost::bind(&LyXComm::read_ready, this)); + } + return fd; } -void LyXComm::endPipe(int & fd, string const & filename) +void LyXComm::endPipe(int & fd, string const & filename, bool write) { if (fd < 0) return; + if (!write) { + lyx_gui::unregister_socket_callback(fd); + } + #ifdef __EMX__ APIRET rc; int errnum; - + rc = DosDisConnectNPipe(fd); if (rc != NO_ERROR) { errnum = TranslateOS2Error(rc); @@ -235,15 +234,15 @@ void LyXComm::endPipe(int & fd, string const & filename) return; } #endif - + if (::close(fd) < 0) { lyxerr << "LyXComm: Could not close pipe " << filename << '\n' << strerror(errno) << endl; } - + // OS/2 pipes are deleted automatically #ifndef __EMX__ - if (lyx::unlink(filename) < 0){ + if (unlink(filename) < 0) { lyxerr << "LyXComm: Could not remove pipe " << filename << '\n' << strerror(errno) << endl; }; @@ -255,74 +254,66 @@ void LyXComm::endPipe(int & fd, string const & filename) void LyXComm::emergencyCleanup() { - endPipe(infd, inPipeName()); - endPipe(outfd, outPipeName()); + if (!pipename.empty()) { + endPipe(infd, inPipeName(), false); + endPipe(outfd, outPipeName(), true); + } } // Receives messages and sends then to client -void LyXComm::callback(int fd, void *v) +void LyXComm::read_ready() { - LyXComm * c = static_cast(v); - - if (lyxerr.debugging(Debug::LYXSERVER)) { - lyxerr << "LyXComm: Receiving from fd " << fd << endl; - } - - const int CMDBUFLEN = 100; - char charbuf[CMDBUFLEN]; - string cmd; -// nb! make lsbuf a class-member for multiple sessions - static string lsbuf; + // nb! make read_buffer_ a class-member for multiple sessions + static string read_buffer_; + read_buffer_.erase(); + + int const charbuf_size = 100; + char charbuf[charbuf_size]; errno = 0; int status; // the single = is intended here. - while((status = read(fd, charbuf, CMDBUFLEN-1))) - {// break and return in loop - if (status > 0) // got something - { - charbuf[status]= '\0'; // turn it into a c string - lsbuf += strip(charbuf, '\r'); + while ((status = ::read(infd, charbuf, charbuf_size - 1))) { + + if (status > 0) { + charbuf[status] = '\0'; // turn it into a c string + read_buffer_ += rtrim(charbuf, "\r"); // commit any commands read - while(lsbuf.find('\n') != string::npos) // while still - // commands - // left - { + while (read_buffer_.find('\n') != string::npos) { // split() grabs the entire string if - // the delim /wasn't/ found. ?:-P - lsbuf= split(lsbuf, cmd,'\n'); + // the delim /wasn't/ found. ?:-P + string cmd; + read_buffer_= split(read_buffer_, cmd,'\n'); lyxerr[Debug::LYXSERVER] << "LyXComm: status:" << status - << ", lsbuf:" << lsbuf + << ", read_buffer_:" << read_buffer_ << ", cmd:" << cmd << endl; if (!cmd.empty()) - c->clientcb(c->client, cmd); - //\n or not \n? + clientcb(client, cmd); + //\n or not \n? } } - if (errno == EAGAIN) - { // EAGAIN is not really an error , it means we're - // only reading too fast for the writing process on - // the other end of the pipe. + if (errno == EAGAIN) { errno = 0; - return; // up to libforms select-loop (*crunch*) + return; } - if (errno != 0 ) - { + if (errno != 0) { lyxerr << "LyXComm: " << strerror(errno) << endl; - if (!lsbuf.empty()) - { - lyxerr << "LyxComm: truncated command: " - << lsbuf << endl; - lsbuf.erase(); + if (!read_buffer_.empty()) { + lyxerr << "LyXComm: truncated command: " + << read_buffer_ << endl; + read_buffer_.erase(); } break; // reset connection } } - c->closeConnection(); - c->openConnection(); - errno= 0; + + // The connection gets reset in errno != EAGAIN + // Why does it need to be reset if errno == 0? + closeConnection(); + openConnection(); + errno = 0; } @@ -363,6 +354,18 @@ void LyXComm::send(string const & msg) } +string const LyXComm::inPipeName() const +{ + return pipename + string(".in"); +} + + +string const LyXComm::outPipeName() const +{ + return pipename + string(".out"); +} + + // LyXServer class LyXServer::~LyXServer() @@ -388,67 +391,68 @@ void LyXServer::callback(LyXServer * serv, string const & msg) { lyxerr[Debug::LYXSERVER] << "LyXServer: Received: '" << msg << '\'' << endl; - + char const * p = msg.c_str(); - + // --- parse the string -------------------------------------------- // // Format: LYXCMD:::\n // bool server_only = false; - while(*p) { + while (*p) { // --- 1. check 'header' --- - if (compare(p, "LYXSRV:", 7) == 0) { - server_only = true; + if (compare(p, "LYXSRV:", 7) == 0) { + server_only = true; } else if (0 != compare(p, "LYXCMD:", 7)) { - lyxerr << "LyXServer: Unknown request \"" << p << "\"" << endl; + lyxerr << "LyXServer: Unknown request \"" + << p << '"' << endl; return; } p += 7; - + // --- 2. for the moment ignore the client name --- string client; - while(*p && *p != ':') + while (*p && *p != ':') client += char(*p++); if (*p == ':') ++p; if (!*p) return; - + // --- 3. get function name --- string cmd; - while(*p && *p != ':') + while (*p && *p != ':') cmd += char(*p++); - + // --- 4. parse the argument --- string arg; if (!server_only && *p == ':' && *(++p)) { - while(*p && *p != '\n') + while (*p && *p != '\n') arg += char(*p++); if (*p) ++p; } - + lyxerr[Debug::LYXSERVER] << "LyXServer: Client: '" << client << "' Command: '" << cmd << "' Argument: '" << arg << '\'' << endl; - + // --- lookup and exec the command ------------------ - + if (server_only) { string buf; - // return the greeting to inform the client that + // return the greeting to inform the client that // we are listening. if (cmd == "hello") { // One more client - if (serv->numclients == MAX_CLIENTS){ //paranoid check + if (serv->numclients == MAX_CLIENTS) { //paranoid check lyxerr[Debug::LYXSERVER] << "LyXServer: too many clients..." << endl; return; } int i= 0; //find place in clients[] - while (!serv->clients[i].empty() - && inumclients) + while (!serv->clients[i].empty() + && inumclients) ++i; serv->clients[i] = client; serv->numclients++; @@ -478,11 +482,11 @@ void LyXServer::callback(LyXServer * serv, string const & msg) } } else { lyxerr <<"LyXServer: Undefined server command " - << cmd << "." << endl; + << cmd << '.' << endl; } return; } - + if (!cmd.empty()) { // which lyxfunc should we let it connect to? // The correct solution would be to have a @@ -491,25 +495,20 @@ void LyXServer::callback(LyXServer * serv, string const & msg) // connect to the lyxfunc in the single LyXView we // support currently. (Lgb) - int action = lyxaction.LookupFunc(cmd); - //int action = -1; - string rval, buf; - - if (action>= 0) { - rval = serv->func->dispatch(action, arg); - } else { - rval = "Unknown command"; - } - //modified june 1999 stefano@zool.su.se: + serv->func->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg)); + string const rval = serv->func->getMessage(); + + //modified june 1999 stefano@zool.su.se: //all commands produce an INFO or ERROR message //in the output pipe, even if they do not return //anything. See chapter 4 of Customization doc. - if (action<0 || serv->func->errorStat()) + string buf; + if (serv->func->errorStat()) buf = "ERROR:"; else buf = "INFO:"; - buf += string(client) + ":" + cmd + ":" + rval + "\n"; + buf += client + ':' + cmd + ':' + rval + '\n'; serv->pipes.send(buf); // !!! we don't do any error checking - @@ -517,7 +516,7 @@ void LyXServer::callback(LyXServer * serv, string const & msg) // message is lost and others too // maybe; so the client should empty // the outpipe before issuing a request. - + // not found } } /* while *p */ @@ -525,7 +524,7 @@ void LyXServer::callback(LyXServer * serv, string const & msg) /* ---F+------------------------------------------------------------------ *\ - Function : LyxNotifyClient + Function : LyXNotifyClient Called by : WorkAreaKeyPress Purpose : send a notify messge to a client Parameters: s - string to send @@ -537,4 +536,3 @@ void LyXServer::notifyClient(string const & s) string buf = string("NOTIFY:") + s + "\n"; pipes.send(buf); } -