home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Dream 52
/
Amiga_Dream_52.iso
/
Linux
/
Divers
/
lyx-0.13.2.tar.gz
/
lyx-0.13.2.tar
/
lyx-0.13.2
/
src
/
lyxserver.C
< prev
next >
Wrap
C/C++ Source or Header
|
1998-04-23
|
14KB
|
509 lines
// -*- C++ -*-
/* This file is part of
* ======================================================
*
* LyX, The Document Processor
*
* Copyright (C) 1995 Matthias Ettrich
* Copyright (C) 1995-1998 The LyX Team.
*
*======================================================*/
/**
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:<clientname>:<functionname>:<argument>"
Answers from LyX look like this:
"INFO:<clientname>:<functionname>:<data>"
[asierra970531] Or like this in case of error:
"ERROR:<clientname>:<functionname>:<error message>"
where <clientname> and <functionname> are just echoed.
If LyX notifies about a user defined extension key-sequence,
the line looks like this:
"NOTIFY:<key-sequence>"
[asierra970531] New server-only messages to implement a simple protocol
"LYXSRV:<clientname>:<protocol message>"
where <protocol message> 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.
Purpose: implement a client/server lib for LyX
*/
#include <config.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include FORMS_H_LOCATION
#ifdef __GNUG__
#pragma implementation
#endif
#include "lyxserver.h"
#include "lyxfunc.h"
#include "lyx_main.h"
#include "error.h"
#ifdef __EMX__
#include <stdlib.h>
#include <io.h>
#define OS2EMX_PLAIN_CHAR
#define INCL_DOSNMPIPES
#define INCL_DOSERRORS
#include <os2.h>
#include "os2_errortable.h"
#endif
// $Id: lyxserver.C,v 1.1.1.1 1998/04/23 16:02:54 larsbj Exp $
#if !defined(lint) && !defined(WITH_WARNINGS)
static char vcid[] = "$Id: lyxserver.C,v 1.1.1.1 1998/04/23 16:02:54 larsbj Exp $";
#endif /* lint */
/* === variables ========================================================= */
extern LyXAction lyxaction;
// 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");
return;
}
// We assume that we don't make it
ready = false;
if (pipename.empty()) return;
// --- prepare input pipe ---------------------------------------
LString 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 = LString();
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));
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));
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 = LString();
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");
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));
return;
}
rc = DosConnectNPipe(fd);
if (rc == ERROR_BAD_PIPE) {
lyxerr.print("LyXComm: Pipe " + tmp + " does not exist");
return;
}
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;
}
outfd = _imphandle(fd);
#endif
if (outfd < 0) {
lyxerr.print("LyXComm: Could not open pipe " + tmp);
lyxerr.print(strerror(errno));
return;
}
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);
}
/// 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");
return;
}
if(infd > -1) {
fl_remove_io_callback(infd, FL_READ, callback);
LString 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(outfd > -1) {
LString 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
}
ready = false;
}
// Receives messages and sends then to client
void LyXComm::callback(int fd, void *v)
{
LyXComm * c = (LyXComm *) v;
// --- read everything -------------------------------------------
if (lyxerr.debugging(Error::LYXSERVER)) {
lyxerr.print(LString("LyXComm: Receiving from fd ") + int(fd));
}
LString string;
bool more = false;
int retries = 0;
do {
char s[100];
int res = read(fd, s, 99);
more = false;
if (res == -1) {
lyxerr.print(LString("LyXComm: Read error from pipe ") + int(fd));
lyxerr.print(strerror(errno));
if (errno == EAGAIN || errno == EINTR) {
retries++;
if (retries == 5) {
lyxerr.print("LyXComm: Doesn't seem to help... Resetting connection.");
c->closeConnection();
c->openConnection();
retries = 0;
return;
}
more = true;
lyxerr.print("LyXComm: Trying again");
} else
return;
} else {
retries = 0;
s[res] = 0;
if (res==99) {
more = true;
} else if (res==0) {
lyxerr.debug("LyXComm: EOF received - resetting connection...",
Error::LYXSERVER);
c->closeConnection();
c->openConnection();
return;
}
string += s;
}
} while (more);
string.strip('\n');
string.strip('\r');
if (string.empty()) {
lyxerr.print("LyXComm: Received empty request. Ignoring.");
return;
}
if (lyxerr.debugging(Error::LYXSERVER)) {
lyxerr.print("LyXComm: Received '" + string + '\'');
}
// And call client
c->clientcb(c->client, string);
}
void LyXComm::send(LString const & msg) {
if (msg.empty()) {
lyxerr.print("LyXComm: Request to send empty string. Ignoring.");
return;
}
if (lyxerr.debugging(Error::LYXSERVER)) {
lyxerr.print("LyXComm: Sending '" + msg + '\'');
}
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");
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
}
// LyXServer class
LyXServer::~LyXServer()
{
// say goodbye to clients so they stop sending messages
if (clients > 0) pipes.send("LYXSRV:*:bye");
}
/* ---F+------------------------------------------------------------------ *\
Function : ServerCallback
Called by : LyXComm
Purpose : handle data gotten from communication
\* ---F------------------------------------------------------------------- */
void LyXServer::callback(LyXServer * serv, LString const & msg)
{
lyxerr.print("LyXServer: Received: '" + msg + '\'');
char const *p = msg.c_str();
// --- parse the string --------------------------------------------
//
// Format: LYXCMD:<client>:<func>:<argstring>\n
//
bool server_only = false;
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");
return;
}
p += 7;
// --- 2. for the moment ignore the client name ---
LString client;
while(*p && *p != ':')
client += char(*p++);
if(*p == ':') p++;
if(!*p) return;
// --- 3. get function name ---
LString cmd;
while(*p && *p != ':')
cmd += char(*p++);
// --- 4. parse the argument ---
LString arg;
if(!server_only && *p == ':' && *(++p)) {
while(*p && *p != '\n')
arg += char(*p++);
if(*p) p++;
}
lyxerr.debug("LyXServer: Client: '" + client + "' Command: '" + cmd + "' Argument: '" + arg + '\'', Error::LYXSERVER);
// --- lookup and exec the command ------------------
if (server_only) {
LString buf;
// return the greeting to inform the client that
// we are listening.
if (cmd == "hello") {
// One more client
serv->clients++;
buf = "LYXSRV:" + client + ":hello";
lyxerr.print("LyXServer: Greeting " + client);
serv->pipes.send(buf);
} else if (cmd == "bye") {
// If clients==0 maybe we should reset the pipes
// to prevent fake callbacks
serv->clients--;
lyxerr.print("LyXServer: Client " + client + " said goodbye");
} else {
lyxerr.print("LyXServer: Undefined server command " + cmd + ".");
}
return;
}
if (!cmd.empty()) {
// which lyxfunc should we let it connect to?
// The correct solution would be to have a
// specialized (non-gui) BufferView. But how do
// we do it now? Probably we should just let it
// connect to the lyxfunc in the single LyXView we
// support currently. (Lgb)
int action = lyxaction.LookupFunc(cmd.c_str());
//int action = -1;
LString rval, buf;
if (action>=0) {
rval = serv->func->Dispatch(action, arg.c_str());
} else {
rval = "Unknown command";
}
if(!rval.empty()) {
if (action<0 || serv->func->errorStat())
buf = "ERROR:";
else
buf = "INFO:";
buf += LString(client) + ":" + cmd + ":" + rval + "\n";
serv->pipes.send(buf);
// !!! we don't do any error checking -
// if the client won't listen, the
// message is lost and others too
// maybe; so the client should empty
// the outpipe before issuing a request.
}
// not found
}
} /* while *p */
}
/* ---F+------------------------------------------------------------------ *\
Function : LyxNotifyClient
Called by : WorkAreaKeyPress
Purpose : send a notify messge to a client
Parameters: s - string to send
Returns : nothing
\* ---F------------------------------------------------------------------- */
void LyXServer::notifyClient(LString const & s)
{
LString buf = LString("NOTIFY:") + s + "\n";
pipes.send(buf);
}