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 >
C/C++ Source or Header  |  1998-04-23  |  14KB  |  509 lines

  1. // -*- C++ -*-
  2. /* This file is part of
  3.  * ======================================================
  4. *           LyX, The Document Processor
  5. *        
  6. *           Copyright (C) 1995 Matthias Ettrich
  7. *           Copyright (C) 1995-1998 The LyX Team.
  8. *
  9. *======================================================*/
  10.  
  11. /**
  12.   Docu   : To use the lyxserver define the name of the pipe in your
  13.            lyxrc:
  14.            \serverpipe "/home/myhome/.lyxpipe"
  15.            Then use .lyxpipe.in and .lyxpipe.out to communicate to LyX.
  16.            Each message consists of a single line in ASCII. Input lines
  17.            (client -> LyX) have the following format:
  18.             "LYXCMD:<clientname>:<functionname>:<argument>"
  19.            Answers from LyX look like this:
  20.            "INFO:<clientname>:<functionname>:<data>"
  21.  [asierra970531] Or like this in case of error:
  22.            "ERROR:<clientname>:<functionname>:<error message>"
  23.            where <clientname> and <functionname> are just echoed.
  24.            If LyX notifies about a user defined extension key-sequence,
  25.            the line looks like this:
  26.            "NOTIFY:<key-sequence>"
  27.  [asierra970531] New server-only messages to implement a simple protocol
  28.            "LYXSRV:<clientname>:<protocol message>"
  29.            where <protocol message> can be "hello" or "bye". If hello is
  30.            received LyX will inform the client that it's listening its
  31.            messages, and 'bye' will inform that lyx is closing.
  32.  
  33.            See development/server_monitor.c for an example client.
  34.   Purpose: implement a client/server lib for LyX
  35. */
  36.  
  37. #include <config.h>
  38.  
  39. #include <string.h>
  40. #include <stdio.h>
  41. #include <sys/types.h>
  42. #include <sys/stat.h>
  43. #include <unistd.h>
  44. #include <fcntl.h>
  45. #include <errno.h>
  46. #include FORMS_H_LOCATION
  47.  
  48. #ifdef __GNUG__
  49. #pragma implementation
  50. #endif
  51.  
  52. #include "lyxserver.h"
  53. #include "lyxfunc.h"
  54. #include "lyx_main.h"
  55. #include "error.h"
  56.  
  57. #ifdef __EMX__
  58. #include <stdlib.h>
  59. #include <io.h>
  60. #define OS2EMX_PLAIN_CHAR
  61. #define INCL_DOSNMPIPES
  62. #define INCL_DOSERRORS
  63. #include <os2.h>
  64. #include "os2_errortable.h"
  65. #endif
  66.  
  67. //     $Id: lyxserver.C,v 1.1.1.1 1998/04/23 16:02:54 larsbj Exp $    
  68.  
  69. #if !defined(lint) && !defined(WITH_WARNINGS)
  70. static char vcid[] = "$Id: lyxserver.C,v 1.1.1.1 1998/04/23 16:02:54 larsbj Exp $";
  71. #endif /* lint */
  72.     
  73. /* === variables ========================================================= */
  74.  
  75. extern LyXAction lyxaction;
  76.  
  77. // LyXComm class
  78.  
  79.  // Open pipes
  80. void LyXComm::openConnection() {
  81.     lyxerr.debug("LyXComm: Opening connection", Error::LYXSERVER);
  82.        
  83.     // If we are up, that's an error
  84.     if (ready) {
  85.         lyxerr.print("LyXComm: Already connected");
  86.         return;
  87.     }
  88.     // We assume that we don't make it
  89.     ready = false;
  90.  
  91.     if (pipename.empty()) return;
  92.  
  93.     // --- prepare input pipe ---------------------------------------
  94.  
  95.     LString tmp = pipename + ".in";
  96.        
  97. #ifdef __EMX__
  98.     HPIPE fd;
  99.     APIRET rc;
  100.     int errnum;
  101.     // Try create one instance of named pipe with the mode O_RDONLY|O_NONBLOCK.
  102.     // The current emx implementation of access() won't work with pipes.
  103.     rc = DosCreateNPipe(tmp.c_str(), &fd, NP_ACCESS_INBOUND,
  104.         NP_NOWAIT|0x01, 0600, 0600, 0);
  105.     if (rc == ERROR_PIPE_BUSY) {
  106. #else
  107.     if (access(tmp.c_str(), F_OK) == 0) {
  108. #endif
  109.         lyxerr.print("LyXComm: Pipe " + tmp + " already exists.");
  110.         lyxerr.print("If no other LyX program is active, please delete"
  111.                  " the pipe by hand and try again.");
  112.         pipename = LString();
  113.         return;
  114.     }
  115. #ifndef __EMX__
  116.     if (mkfifo(tmp.c_str(), 0600) < 0) {
  117.         lyxerr.print("LyXComm: Could not create pipe " + tmp);
  118.         lyxerr.print(strerror(errno));
  119.         return;
  120.     };
  121.     infd = open(tmp.c_str(), O_RDONLY|O_NONBLOCK);
  122. #else
  123.     if (rc != NO_ERROR) {
  124.         errnum = TranslateOS2Error(rc);
  125.         lyxerr.print("LyXComm: Could not create pipe " + tmp);
  126.         lyxerr.print(strerror(errnum));
  127.         return;
  128.     };
  129.     // Listen to it.
  130.     rc = DosConnectNPipe(fd);
  131.     if (rc != NO_ERROR && rc != ERROR_PIPE_NOT_CONNECTED) {
  132.         errnum = TranslateOS2Error(rc);
  133.         lyxerr.print("LyXComm: Could not create pipe " + tmp);
  134.         lyxerr.print(strerror(errnum));
  135.         return;
  136.     };
  137.     // Imported handles can be used both with OS/2 APIs and emx library functions.
  138.     infd = _imphandle(fd);
  139. #endif
  140.     if (infd < 0) {
  141.         lyxerr.print("LyXComm: Could not open pipe " + tmp);
  142.         lyxerr.print(strerror(errno));
  143.         return;
  144.     }
  145.     fl_add_io_callback(infd, FL_READ, callback, (void*)this);
  146.  
  147.     // --- prepare output pipe ---------------------------------------
  148.  
  149.     tmp = pipename + ".out";
  150.        
  151. #ifndef __EMX__       
  152.     if (access(tmp.c_str(), F_OK) == 0) {
  153. #else
  154.     rc = DosCreateNPipe(tmp.c_str(), &fd, NP_ACCESS_DUPLEX,
  155.         NP_NOWAIT|0x01, 0600, 0600, 0);
  156.  
  157.     if (rc == ERROR_PIPE_BUSY) {
  158. #endif
  159.         lyxerr.print("LyXComm: Pipe " + tmp + " already exists.");
  160.         lyxerr.print("If no other LyX program is active, please delete"
  161.                  " the pipe by hand and try again.");
  162.         pipename = LString();
  163.         return;
  164.     }
  165. #ifndef __EMX__
  166.     if (mkfifo(tmp.c_str(), 0600) < 0) {
  167.         lyxerr.print("LyXComm: Could not create pipe " + tmp);
  168.         lyxerr.print(strerror(errno));
  169.         return;
  170.     };
  171.     if (access(tmp.c_str(), F_OK) != 0) {
  172.         lyxerr.print("LyXComm: Pipe " + tmp + " does not exist");
  173.         return;
  174.     }
  175.     outfd = open(tmp.c_str(), O_RDWR);
  176. #else
  177.     if (rc != NO_ERROR) {
  178.         errnum = TranslateOS2Error(rc);
  179.         lyxerr.print("LyXComm: Could not create pipe " + tmp);
  180.         lyxerr.print(strerror(errnum));
  181.         return;
  182.     }
  183.     rc = DosConnectNPipe(fd);
  184.     if (rc == ERROR_BAD_PIPE) {
  185.         lyxerr.print("LyXComm: Pipe " + tmp + " does not exist");
  186.         return;
  187.     }
  188.     if (rc != NO_ERROR && rc != ERROR_PIPE_NOT_CONNECTED) {
  189.         errnum = TranslateOS2Error(rc);
  190.         lyxerr.print("LyXComm: Could not create pipe " + tmp);
  191.         lyxerr.print(strerror(errnum));
  192.         return;
  193.     }
  194.     outfd = _imphandle(fd);
  195. #endif
  196.     if (outfd < 0) {
  197.         lyxerr.print("LyXComm: Could not open pipe " + tmp);
  198.         lyxerr.print(strerror(errno));
  199.         return;
  200.     }
  201.     if (fcntl(outfd, F_SETFL, O_NONBLOCK) < 0) {
  202.         lyxerr.print("LyXComm: Could not set flags on pipe " + tmp);
  203.         lyxerr.print(strerror(errno));
  204.         return;
  205.     };
  206.     // We made it!
  207.     ready = true;
  208.     lyxerr.debug("LyXComm: Connection established", Error::LYXSERVER);
  209. }
  210.  
  211. /// Close pipes
  212. void LyXComm::closeConnection() {
  213. #ifdef __EMX__
  214.     APIRET rc;
  215.     int errnum;
  216. #endif
  217.            lyxerr.debug("LyXComm: Closing connection", Error::LYXSERVER);
  218.  
  219.     if (pipename.empty()) {
  220.             return;
  221.     }
  222.  
  223.     if (!ready) {
  224.         lyxerr.print("LyXComm: Already disconnected");
  225.         return;
  226.     }
  227.  
  228.     if(infd > -1) {
  229.         fl_remove_io_callback(infd, FL_READ, callback);
  230.  
  231.         LString tmp = pipename + ".in";
  232. #ifdef __EMX__        // Notify the operating system.
  233.         rc = DosDisConnectNPipe(infd);
  234.         if (rc != NO_ERROR) {
  235.             errnum = TranslateOS2Error(rc);
  236.             lyxerr.print("LyXComm: Could not disconnect pipe " + tmp);
  237.             lyxerr.print(strerror(errnum));
  238.             return;
  239.         }
  240. #endif
  241.         if (close(infd) < 0) {
  242.             lyxerr.print("LyXComm: Could not close pipe " + tmp);
  243.             lyxerr.print(strerror(errno));
  244.         }
  245. #ifndef __EMX__        // OS/2 named pipes will be automatically removed.
  246.         if (unlink(tmp.c_str()) < 0){
  247.             lyxerr.print("LyXComm: Could not remove pipe " + tmp);
  248.             lyxerr.print(strerror(errno));
  249.         };
  250. #endif
  251.     }
  252.     if(outfd > -1) {
  253.         LString tmp = pipename + ".out";
  254. #ifdef __EMX__
  255.         rc = DosDisConnectNPipe(outfd);
  256.         if (rc != NO_ERROR) {
  257.             errnum = TranslateOS2Error(rc);
  258.             lyxerr.print("LyXComm: Could not disconnect pipe " + tmp);
  259.             lyxerr.print(strerror(errnum));
  260.             return;
  261.         }
  262. #endif
  263.         if (close(outfd) < 0) {
  264.             lyxerr.print("LyXComm: Could not close pipe " + tmp);
  265.             lyxerr.print(strerror(errno));
  266.         }
  267. #ifndef __EMX__
  268.         if (unlink(tmp.c_str()) < 0){
  269.             lyxerr.print("LyXComm: Could not remove pipe " + tmp);
  270.             lyxerr.print(strerror(errno));
  271.         };
  272. #endif
  273.     }
  274.     ready = false;
  275. }
  276.  
  277. // Receives messages and sends then to client
  278. void LyXComm::callback(int fd, void *v)
  279. {
  280.     LyXComm * c = (LyXComm *) v;
  281.  
  282.     // --- read everything -------------------------------------------
  283.  
  284.     if (lyxerr.debugging(Error::LYXSERVER)) {
  285.         lyxerr.print(LString("LyXComm: Receiving from fd ") + int(fd));
  286.     }
  287.  
  288.     LString string;
  289.     bool more = false;
  290.     int retries = 0;
  291.     do {
  292.         char s[100];
  293.         int res = read(fd, s, 99);
  294.         more = false;
  295.         if (res == -1) {
  296.             lyxerr.print(LString("LyXComm: Read error from pipe ") + int(fd));
  297.             lyxerr.print(strerror(errno));
  298.             if (errno == EAGAIN || errno == EINTR) {
  299.                 retries++;
  300.                 if (retries == 5) {
  301.                     lyxerr.print("LyXComm: Doesn't seem to help... Resetting connection.");
  302.                     c->closeConnection();
  303.                     c->openConnection();
  304.                     retries = 0;
  305.                     return;
  306.                 }
  307.                 more = true;
  308.                 lyxerr.print("LyXComm: Trying again");
  309.             } else
  310.                 return;
  311.         } else {
  312.             retries = 0;
  313.             s[res] = 0;
  314.             if (res==99) {
  315.                 more = true;
  316.             } else if (res==0) {
  317.                 lyxerr.debug("LyXComm: EOF received - resetting connection...",
  318.                          Error::LYXSERVER);
  319.                 c->closeConnection();
  320.                 c->openConnection();
  321.                 return;
  322.             }
  323.             string += s;
  324.         }
  325.     } while (more);
  326.  
  327.     string.strip('\n');
  328.     string.strip('\r');
  329.     if (string.empty()) {
  330.         lyxerr.print("LyXComm: Received empty request. Ignoring.");
  331.         return;
  332.     }
  333.  
  334.     if (lyxerr.debugging(Error::LYXSERVER)) {
  335.         lyxerr.print("LyXComm: Received '" + string + '\'');
  336.     }
  337.  
  338.     // And call client
  339.     c->clientcb(c->client, string);
  340. }
  341.  
  342. void LyXComm::send(LString const & msg) {
  343.     if (msg.empty()) {
  344.         lyxerr.print("LyXComm: Request to send empty string. Ignoring.");
  345.         return;
  346.     }
  347.  
  348.     if (lyxerr.debugging(Error::LYXSERVER)) {
  349.         lyxerr.print("LyXComm: Sending '" + msg + '\'');
  350.     }
  351.  
  352.     if (pipename.empty()) return;
  353.  
  354.     if (!ready) {
  355.         lyxerr.print("LyXComm: Pipes are closed. Could not send "+ msg);
  356.     } else if (write(outfd, msg.c_str(), msg.length()) < 0) {
  357.         lyxerr.print("LyXComm: Error sending message: " + msg);
  358.         lyxerr.print(strerror(errno));
  359.         lyxerr.print("LyXComm: Resetting connection");
  360.         closeConnection();
  361.         openConnection();
  362.     }
  363. #ifdef __EMX__
  364.     APIRET rc;
  365.     int errnum;
  366.     rc = DosResetBuffer(outfd);    // To avoid synchronization problems.
  367.     if (rc != NO_ERROR) {
  368.         errnum = TranslateOS2Error(rc);
  369.         lyxerr.print("LyXComm: Message could not be flushed: " +msg);
  370.         lyxerr.print(strerror(errnum));
  371.     }
  372. #endif
  373. }
  374.  
  375.  
  376. // LyXServer class
  377.  
  378. LyXServer::~LyXServer()
  379. {
  380.     // say goodbye to clients so they stop sending messages
  381.     if (clients > 0) pipes.send("LYXSRV:*:bye");
  382. }
  383.  
  384.  
  385. /* ---F+------------------------------------------------------------------ *\
  386.    Function  : ServerCallback
  387.     Called by : LyXComm
  388.     Purpose   : handle data gotten from communication
  389. \* ---F------------------------------------------------------------------- */
  390.  
  391. void LyXServer::callback(LyXServer * serv, LString const & msg)
  392. {
  393.     lyxerr.print("LyXServer: Received: '" + msg + '\'');
  394.  
  395.     char const *p = msg.c_str();
  396.  
  397.     // --- parse the string --------------------------------------------
  398.     //
  399.     //  Format: LYXCMD:<client>:<func>:<argstring>\n
  400.     //
  401.     bool server_only = false;
  402.     while(*p) {
  403.         // --- 1. check 'header' ---
  404.             if (strncmp(p, "LYXSRV:", 7)==0) {
  405.             server_only = true; 
  406.         } else if(0!=strncmp(p, "LYXCMD:", 7)) {
  407.             lyxerr.print("LyXServer: Unknown request");
  408.             return;
  409.         }
  410.         p += 7;
  411.         
  412.         // --- 2. for the moment ignore the client name ---
  413.         LString client;
  414.         while(*p && *p != ':')
  415.             client += char(*p++);
  416.         if(*p == ':') p++;
  417.         if(!*p) return;
  418.         
  419.         // --- 3. get function name ---
  420.         LString cmd;
  421.         while(*p && *p != ':')
  422.             cmd += char(*p++);
  423.         
  424.         // --- 4. parse the argument ---
  425.         LString arg;
  426.         if(!server_only && *p == ':' && *(++p)) {
  427.             while(*p && *p != '\n')
  428.                 arg += char(*p++);
  429.             if(*p) p++;
  430.         }
  431.  
  432.         lyxerr.debug("LyXServer: Client: '" + client + "' Command: '" + cmd + "' Argument: '" + arg + '\'', Error::LYXSERVER);
  433.         
  434.         // --- lookup and exec the command ------------------
  435.  
  436.         if (server_only) {
  437.             LString buf;
  438.             // return the greeting to inform the client that 
  439.             // we are listening.
  440.             if (cmd == "hello") {
  441.                 // One more client
  442.                 serv->clients++;
  443.                 buf = "LYXSRV:" + client + ":hello";
  444.                 lyxerr.print("LyXServer: Greeting " + client);
  445.                 serv->pipes.send(buf);
  446.             } else if (cmd == "bye") {
  447.                 // If clients==0 maybe we should reset the pipes
  448.                 // to prevent fake callbacks
  449.                 serv->clients--;
  450.                 lyxerr.print("LyXServer: Client " + client + " said goodbye");
  451.             } else {
  452.                 lyxerr.print("LyXServer: Undefined server command " + cmd + ".");
  453.             }
  454.             return;
  455.         }
  456.  
  457.         if (!cmd.empty()) {
  458.             // which lyxfunc should we let it connect to?
  459.             // The correct solution would be to have a
  460.             // specialized (non-gui) BufferView. But how do
  461.             // we do it now? Probably we should just let it
  462.             // connect to the lyxfunc in the single LyXView we
  463.             // support currently. (Lgb)
  464.  
  465.             int action = lyxaction.LookupFunc(cmd.c_str());
  466.             //int action = -1;
  467.             LString rval, buf;
  468.             
  469.             if (action>=0) {
  470.                 rval = serv->func->Dispatch(action, arg.c_str());
  471.             } else {
  472.                 rval = "Unknown command";
  473.             }
  474.  
  475.             if(!rval.empty()) {
  476.                     if (action<0 || serv->func->errorStat())
  477.                     buf = "ERROR:";
  478.                 else
  479.                     buf = "INFO:";
  480.                 buf += LString(client) + ":" + cmd + ":" + rval + "\n";
  481.                 serv->pipes.send(buf);
  482.                 // !!! we don't do any error checking -
  483.                 //  if the client won't listen, the
  484.                 //  message is lost and others too
  485.                 //  maybe; so the client should empty
  486.                 //  the outpipe before issuing a request.
  487.             }
  488.             // not found
  489.         }
  490.     }  /* while *p */
  491. }
  492.  
  493.  
  494. /* ---F+------------------------------------------------------------------ *\
  495.    Function  : LyxNotifyClient
  496.    Called by : WorkAreaKeyPress
  497.    Purpose   : send a notify messge to a client
  498.    Parameters: s - string to send
  499.    Returns   : nothing
  500.    \* ---F------------------------------------------------------------------- */
  501.  
  502. void LyXServer::notifyClient(LString const & s)
  503. {
  504.     LString buf = LString("NOTIFY:") + s + "\n";
  505.     pipes.send(buf);
  506. }
  507.  
  508.