X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Flyxserver.C;h=ba1da6c533fc35389736577ac23de65d92d59b88;hb=e7f4618bcce770369cf46335c2c7f0164b4b8857;hp=903e5a1ae06c95645fae673f6fabed538ba00122;hpb=0eccdd1c3613e5170deb77b22174dd0afde833e9;p=lyx.git diff --git a/src/lyxserver.C b/src/lyxserver.C index 903e5a1ae0..ba1da6c533 100644 --- a/src/lyxserver.C +++ b/src/lyxserver.C @@ -1,381 +1,335 @@ -// -*- C++ -*- -/* This file is part of - * ====================================================== - * - * LyX, The Document Processor - * - * Copyright 1995 Matthias Ettrich - * Copyright 1995-1998 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 -#include -#include FORMS_H_LOCATION - -#ifdef __GNUG__ -#pragma implementation -#endif - #include "lyxserver.h" +#include "debug.h" +#include "funcrequest.h" +#include "LyXAction.h" #include "lyxfunc.h" -#include "lyx_main.h" -#include "error.h" +#include "frontends/Application.h" + +#include "support/filename.h" #include "support/lstrings.h" +#include "support/lyxlib.h" + +#include -#ifdef __EMX__ -#include -#include -#define OS2EMX_PLAIN_CHAR -#define INCL_DOSNMPIPES -#define INCL_DOSERRORS -#include -#include "os2_errortable.h" +#include +#ifdef HAVE_SYS_STAT_H +# include #endif +#include + + +namespace lyx { + +using support::compare; +using support::FileName; +using support::rtrim; +using support::split; +using support::unlink; + +using std::endl; +using std::string; + + +#if !defined (HAVE_MKFIFO) +// We provide a stub class that disables the lyxserver. + +void LyXComm::openConnection() +{} -// provide an empty mkfifo() if we do not have one. This disables the -// lyxserver. -#ifndef HAVE_MKFIFO -int mkfifo( char *__path, mode_t __mode ) { - return 0; + +void LyXComm::closeConnection() +{} + + +int LyXComm::startPipe(string const & filename, bool write) +{ + return -1; } -#endif -/* === variables ========================================================= */ +void LyXComm::endPipe(int & fd, string const & filename, bool write) +{} + + +void LyXComm::emergencyCleanup() +{} + +void LyXComm::read_ready() +{} + -extern LyXAction lyxaction; +void LyXComm::send(string const & msg) +{} + + +#else // defined (HAVE_MKFIFO) + + +void LyXComm::openConnection() +{ + lyxerr[Debug::LYXSERVER] << "LyXComm: Opening connection" << endl; -// LyXComm class - - // Open pipes -void LyXComm::openConnection() { - lyxerr.debug("LyXComm: Opening connection", Error::LYXSERVER); - // If we are up, that's an error if (ready) { - lyxerr.print("LyXComm: Already connected"); + lyxerr << "LyXComm: Already connected" << endl; return; } // We assume that we don't make it ready = false; - - if (pipename.empty()) return; - // --- prepare input pipe --------------------------------------- - - string tmp = pipename + ".in"; - -#ifdef __EMX__ - HPIPE fd; - APIRET rc; - int errnum; - // Try create one instance of named pipe with the mode O_RDONLY|O_NONBLOCK. - // The current emx implementation of access() won't work with pipes. - rc = DosCreateNPipe(tmp.c_str(), &fd, NP_ACCESS_INBOUND, - NP_NOWAIT|0x01, 0600, 0600, 0); - if (rc == ERROR_PIPE_BUSY) { -#else - if (access(tmp.c_str(), F_OK) == 0) { -#endif - lyxerr.print("LyXComm: Pipe " + tmp + " already exists."); - lyxerr.print("If no other LyX program is active, please delete" - " the pipe by hand and try again."); - pipename = string(); + if (pipename.empty()) { + lyxerr[Debug::LYXSERVER] + << "LyXComm: server is disabled, nothing to do" + << endl; return; } -#ifndef __EMX__ - if (mkfifo(tmp.c_str(), 0600) < 0) { - lyxerr.print("LyXComm: Could not create pipe " + tmp); - lyxerr.print(strerror(errno)); - return; - }; - infd = open(tmp.c_str(), O_RDONLY|O_NONBLOCK); -#else - if (rc != NO_ERROR) { - errnum = TranslateOS2Error(rc); - lyxerr.print("LyXComm: Could not create pipe " + tmp); - lyxerr.print(strerror(errnum)); - return; - }; - // Listen to it. - rc = DosConnectNPipe(fd); - if (rc != NO_ERROR && rc != ERROR_PIPE_NOT_CONNECTED) { - errnum = TranslateOS2Error(rc); - lyxerr.print("LyXComm: Could not create pipe " + tmp); - lyxerr.print(strerror(errnum)); + + if ((infd = startPipe(inPipeName(), false)) == -1) return; - }; - // Imported handles can be used both with OS/2 APIs and emx - // library functions. - infd = _imphandle(fd); -#endif - if (infd < 0) { - lyxerr.print("LyXComm: Could not open pipe " + tmp); - lyxerr.print(strerror(errno)); + + if ((outfd = startPipe(outPipeName(), true)) == -1) { + endPipe(infd, inPipeName(), false); return; } - fl_add_io_callback(infd, FL_READ, callback, (void*)this); - - // --- prepare output pipe --------------------------------------- - - tmp = pipename + ".out"; - -#ifndef __EMX__ - if (access(tmp.c_str(), F_OK) == 0) { -#else - rc = DosCreateNPipe(tmp.c_str(), &fd, NP_ACCESS_DUPLEX, - NP_NOWAIT|0x01, 0600, 0600, 0); - - if (rc == ERROR_PIPE_BUSY) { -#endif - lyxerr.print("LyXComm: Pipe " + tmp + " already exists."); - lyxerr.print("If no other LyX program is active, please delete" - " the pipe by hand and try again."); - pipename = string(); + + if (fcntl(outfd, F_SETFL, O_NONBLOCK) < 0) { + lyxerr << "LyXComm: Could not set flags on pipe " << outPipeName() + << '\n' << strerror(errno) << endl; return; } -#ifndef __EMX__ - if (mkfifo(tmp.c_str(), 0600) < 0) { - lyxerr.print("LyXComm: Could not create pipe " + tmp); - lyxerr.print(strerror(errno)); - return; - }; - if (access(tmp.c_str(), F_OK) != 0) { - lyxerr.print("LyXComm: Pipe " + tmp + " does not exist"); + + // We made it! + ready = true; + lyxerr[Debug::LYXSERVER] << "LyXComm: Connection established" << endl; +} + + +/// Close pipes +void LyXComm::closeConnection() +{ + lyxerr[Debug::LYXSERVER] << "LyXComm: Closing connection" << endl; + + if (pipename.empty()) { + lyxerr[Debug::LYXSERVER] + << "LyXComm: server is disabled, nothing to do" + << endl; return; } - outfd = open(tmp.c_str(), O_RDWR); -#else - if (rc != NO_ERROR) { - errnum = TranslateOS2Error(rc); - lyxerr.print("LyXComm: Could not create pipe " + tmp); - lyxerr.print(strerror(errnum)); + + if (!ready) { + lyxerr << "LyXComm: Already disconnected" << endl; return; } - rc = DosConnectNPipe(fd); - if (rc == ERROR_BAD_PIPE) { - lyxerr.print("LyXComm: Pipe " + tmp + " does not exist"); - return; + + endPipe(infd, inPipeName(), false); + endPipe(outfd, outPipeName(), true); + + ready = false; +} + + +int LyXComm::startPipe(string const & file, bool write) +{ + FileName const filename(file); + if (::access(filename.toFilesystemEncoding().c_str(), F_OK) == 0) { + lyxerr << "LyXComm: Pipe " << filename << " already exists.\n" + << "If no other LyX program is active, please delete" + " the pipe by hand and try again." << endl; + pipename.erase(); + return -1; } - if (rc != NO_ERROR && rc != ERROR_PIPE_NOT_CONNECTED) { - errnum = TranslateOS2Error(rc); - lyxerr.print("LyXComm: Could not create pipe " + tmp); - lyxerr.print(strerror(errnum)); - return; + + if (::mkfifo(filename.toFilesystemEncoding().c_str(), 0600) < 0) { + lyxerr << "LyXComm: Could not create pipe " << filename << '\n' + << strerror(errno) << endl; + return -1; + }; + int const fd = ::open(filename.toFilesystemEncoding().c_str(), + write ? (O_RDWR) : (O_RDONLY|O_NONBLOCK)); + + if (fd < 0) { + lyxerr << "LyXComm: Could not open pipe " << filename << '\n' + << strerror(errno) << endl; + unlink(filename); + return -1; } - outfd = _imphandle(fd); -#endif - if (outfd < 0) { - lyxerr.print("LyXComm: Could not open pipe " + tmp); - lyxerr.print(strerror(errno)); - return; + + if (!write) { + theApp()->registerSocketCallback(fd, + boost::bind(&LyXComm::read_ready, this)); } - if (fcntl(outfd, F_SETFL, O_NONBLOCK) < 0) { - lyxerr.print("LyXComm: Could not set flags on pipe " + tmp); - lyxerr.print(strerror(errno)); - return; - }; - // We made it! - ready = true; - lyxerr.debug("LyXComm: Connection established", Error::LYXSERVER); + + return fd; } - -/// Close pipes -void LyXComm::closeConnection() { -#ifdef __EMX__ - APIRET rc; - int errnum; -#endif - lyxerr.debug("LyXComm: Closing connection", Error::LYXSERVER); - if (pipename.empty()) { - return; - } - if (!ready) { - lyxerr.print("LyXComm: Already disconnected"); +void LyXComm::endPipe(int & fd, string const & filename, bool write) +{ + if (fd < 0) return; + + if (!write) { + theApp()->unregisterSocketCallback(fd); } - - if(infd > -1) { - fl_remove_io_callback(infd, FL_READ, callback); - - string tmp = pipename + ".in"; -#ifdef __EMX__ // Notify the operating system. - rc = DosDisConnectNPipe(infd); - if (rc != NO_ERROR) { - errnum = TranslateOS2Error(rc); - lyxerr.print("LyXComm: Could not disconnect pipe " + tmp); - lyxerr.print(strerror(errnum)); - return; - } -#endif - if (close(infd) < 0) { - lyxerr.print("LyXComm: Could not close pipe " + tmp); - lyxerr.print(strerror(errno)); - } -#ifndef __EMX__ // OS/2 named pipes will be automatically removed. - if (unlink(tmp.c_str()) < 0){ - lyxerr.print("LyXComm: Could not remove pipe " + tmp); - lyxerr.print(strerror(errno)); - }; -#endif + + if (::close(fd) < 0) { + lyxerr << "LyXComm: Could not close pipe " << filename + << '\n' << strerror(errno) << endl; } - if(outfd > -1) { - string tmp = pipename + ".out"; -#ifdef __EMX__ - rc = DosDisConnectNPipe(outfd); - if (rc != NO_ERROR) { - errnum = TranslateOS2Error(rc); - lyxerr.print("LyXComm: Could not disconnect pipe " + tmp); - lyxerr.print(strerror(errnum)); - return; - } -#endif - if (close(outfd) < 0) { - lyxerr.print("LyXComm: Could not close pipe " + tmp); - lyxerr.print(strerror(errno)); - } -#ifndef __EMX__ - if (unlink(tmp.c_str()) < 0){ - lyxerr.print("LyXComm: Could not remove pipe " + tmp); - lyxerr.print(strerror(errno)); - }; -#endif + + if (unlink(FileName(filename)) < 0) { + lyxerr << "LyXComm: Could not remove pipe " << filename + << '\n' << strerror(errno) << endl; + }; + + fd = -1; +} + + +void LyXComm::emergencyCleanup() +{ + if (!pipename.empty()) { + endPipe(infd, inPipeName(), false); + endPipe(outfd, outPipeName(), true); } - ready = false; } - + + // Receives messages and sends then to client -void LyXComm::callback(int fd, void *v) +void LyXComm::read_ready() { - LyXComm * c = (LyXComm *) v; - - if (lyxerr.debugging(Error::LYXSERVER)) { - lyxerr.print(string("LyXComm: Receiving from fd ") + tostr(fd)); - } - - 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'); - lyxerr.debug(string("LyXComm: status:") - + tostr(status) + ", lsbuf:" + lsbuf - + ", cmd:" + cmd, - Error::LYXSERVER); - if(!cmd.empty()) - c->clientcb(c->client, cmd); - //\n or not \n? + // the delim /wasn't/ found. ?:-P + string cmd; + read_buffer_= split(read_buffer_, cmd,'\n'); + lyxerr[Debug::LYXSERVER] + << "LyXComm: status:" << status + << ", read_buffer_:" << read_buffer_ + << ", cmd:" << cmd << endl; + if (!cmd.empty()) + 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 ) - { - lyxerr.print(string("LyXComm: ") + strerror(errno)); - if(!lsbuf.empty()) - { - lyxerr.print("LyxComm: truncated command: " - + lsbuf); - lsbuf.erase(); + if (errno != 0) { + lyxerr << "LyXComm: " << strerror(errno) << endl; + 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; } - -void LyXComm::send(string const & msg) { + + +void LyXComm::send(string const & msg) +{ if (msg.empty()) { - lyxerr.print("LyXComm: Request to send empty string. Ignoring."); + lyxerr << "LyXComm: Request to send empty string. Ignoring." + << endl; return; } - if (lyxerr.debugging(Error::LYXSERVER)) { - lyxerr.print("LyXComm: Sending '" + msg + '\''); + if (lyxerr.debugging(Debug::LYXSERVER)) { + lyxerr << "LyXComm: Sending '" << msg << '\'' << endl; } if (pipename.empty()) return; if (!ready) { - lyxerr.print("LyXComm: Pipes are closed. Could not send "+ msg); - } else if (write(outfd, msg.c_str(), msg.length()) < 0) { - lyxerr.print("LyXComm: Error sending message: " + msg); - lyxerr.print(strerror(errno)); - lyxerr.print("LyXComm: Resetting connection"); + lyxerr << "LyXComm: Pipes are closed. Could not send " + << msg << endl; + } else if (::write(outfd, msg.c_str(), msg.length()) < 0) { + lyxerr << "LyXComm: Error sending message: " << msg + << '\n' << strerror(errno) + << "\nLyXComm: Resetting connection" << endl; closeConnection(); openConnection(); } -#ifdef __EMX__ - APIRET rc; - int errnum; - rc = DosResetBuffer(outfd); // To avoid synchronization problems. - if (rc != NO_ERROR) { - errnum = TranslateOS2Error(rc); - lyxerr.print("LyXComm: Message could not be flushed: " +msg); - lyxerr.print(strerror(errnum)); - } -#endif +} + +#endif // defined (HAVE_MKFIFO) + + +string const LyXComm::inPipeName() const +{ + return pipename + string(".in"); +} + + +string const LyXComm::outPipeName() const +{ + return pipename + string(".out"); } @@ -387,7 +341,7 @@ LyXServer::~LyXServer() // modified june 1999 by stefano@zool.su.se to send as many bye // messages as there are clients, each with client's name. string message; - for (int i=0; i::\n // bool server_only = false; - while(*p) { + while (*p) { // --- 1. check 'header' --- - if (strncmp(p, "LYXSRV:", 7)==0) { - server_only = true; - } else if(0!=strncmp(p, "LYXCMD:", 7)) { - lyxerr.print("LyXServer: Unknown request"); + + if (compare(p, "LYXSRV:", 7) == 0) { + server_only = true; + } else if (0 != compare(p, "LYXCMD:", 7)) { + 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; - + 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') + if (!server_only && *p == ':' && *(++p)) { + while (*p && *p != '\n') arg += char(*p++); - if(*p) p++; + if (*p) ++p; } - - lyxerr.debug("LyXServer: Client: '" + client + "' Command: '" + cmd + "' Argument: '" + arg + '\'', Error::LYXSERVER); - + + 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 - lyxerr.debug("LyXServer: too many clients...", Error::LYXSERVER); + 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) - i++; + int i= 0; //find place in clients[] + while (!serv->clients[i].empty() + && inumclients) + ++i; serv->clients[i] = client; serv->numclients++; buf = "LYXSRV:" + client + ":hello\n"; - lyxerr.debug("LyXServer: Greeting " + client, Error::LYXSERVER); + lyxerr[Debug::LYXSERVER] + << "LyXServer: Greeting " + << client << endl; serv->pipes.send(buf); } else if (cmd == "bye") { - // If clients==0 maybe we should reset the pipes + // If clients == 0 maybe we should reset the pipes // to prevent fake callbacks - int i; //look if client is registered - for (i=0; inumclients; i++) { + int i = 0; //look if client is registered + for (; i < serv->numclients; ++i) { if (serv->clients[i] == client) break; } - if (inumclients) { + if (i < serv->numclients) { serv->numclients--; serv->clients[i].erase(); - lyxerr.debug("LyXServer: Client " + client + " said goodbye", - Error::LYXSERVER); + lyxerr[Debug::LYXSERVER] + << "LyXServer: Client " + << client << " said goodbye" + << endl; } else { - lyxerr.debug("LyXServer: ignoring bye messge from unregistered client" + - client + "\n", Error::LYXSERVER); + lyxerr[Debug::LYXSERVER] + << "LyXServer: ignoring bye messge from unregistered client" + << client << endl; } } else { - lyxerr.print("LyXServer: Undefined server command " + cmd + "."); + lyxerr <<"LyXServer: Undefined server command " + << cmd << '.' << endl; } return; } - + if (!cmd.empty()) { // which lyxfunc should we let it connect to? // The correct solution would be to have a @@ -494,25 +462,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.c_str()); - //int action = -1; - string rval, buf; - - if (action>=0) { - rval = serv->func->Dispatch(action, arg.c_str()); - } else { - rval = "Unknown command"; - } - //modified june 1999 stefano@zool.su.se: + serv->func->dispatch(FuncRequest(lyxaction.lookupFunc(cmd), arg)); + string const rval = to_utf8(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 - @@ -520,7 +483,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 */ @@ -528,7 +491,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 @@ -541,3 +504,5 @@ void LyXServer::notifyClient(string const & s) pipes.send(buf); } + +} // namespace lyx