// This may look like C code, but it is really -*- C++ -*-
// 
// <copyright> 
// 
// Copyright (c) 1993 
// Institute for Information Processing and Computer Supported New Media (IICM), 
// Graz University of Technology, Austria. 
// 
// </copyright> 
// 
// 
// <file> 
// 
// Name:        rviewer.C
// 
// Purpose:     contains some stuff for the client side of the Hyper-G viewer daemons
// 
// Created:     26 Jun 93   Joerg Faschingbauer
// 
// Modified:    19 Aug 93   Joerg Faschingbauer
// 
// </file> 
#include "rvwstuff.h"

#include <hyperg/utils/environ.h>
#include <hyperg/utils/hgunistd.h>
#include <hyperg/hyperg/assert.h>

#include <Dispatch/dispatcher.h>
#include <Dispatch/rpcstream.h>
#include <Dispatch/rpchdr.h>

#include <iostream.h>
#include <fstream.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>

#ifdef AIX
#include <sys/select.h>  /* FD_SETSIZE, normally contained elsewhere */
#endif


#define VERBOSE
#include <hyperg/hyperg/verbose.h>


#define BLOCK


HgViewerDaemon :: HgViewerDaemon (int timeout)
: binary_(RString()), 
  running_(false),
  port_(-1),
  timeout_(timeout) {
#if (defined (__GNUC__) && !defined (OSF1))
     // ++++
     // gnu's ios deletes its streambuf on destruction.
     // rpcstream has a streambuf *member* (not pointer to) and passes it to ios via 
     // init(), which will delete it then --> nasty bug
     init_port_.setf (ios::dont_close) ;
#endif   
}

HgViewerDaemon :: HgViewerDaemon (const RString& binary, int timeout)
: binary_(binary), 
  running_(false),
  port_(-1),
  timeout_(timeout) {
#if (defined (__GNUC__) && !defined (OSF1))
     // ++++
     // gnu's ios deletes its streambuf on destruction.
     // rpcstream has a streambuf *member* (not pointer to) and passes it to ios via 
     // init(), which will delete it then --> nasty bug
     init_port_.setf (ios::dont_close) ;
#endif   
}

HgViewerDaemon :: ~HgViewerDaemon() {
   DEBUGNL ("HgViewerDaemon::~HgViewerDaemon()") ;
   quit() ;
}

boolean HgViewerDaemon :: start() {
   if (binary_ == RString())
      return false ;

   if (running_) {
      DEBUGNL ("HgViewerDaemon::start(): already running") ;
      return true ;
   }
   
   DEBUGNL ("HgViewerDaemon::start(): starting ...") ;
   host_ = Environ::hostName() ;

   init_port_.listen (0) ;
   if (! init_port_)
      return false ;

   pid_ = fork() ;
   
   if (pid_ == 0) {
      // child
      int port_to_listen = init_port_.rdbuf()->port() ;
      init_port_.close() ;

      // close all the rest of the file descriptors. else some side
      // effects occur. e.g., the parent has a connection to a viewer
      // open that never gets really closed.
      for (int fd=3 ; fd<FD_SETSIZE ; fd++) 
         ::close (fd) ;

      char portstr[10] ;
      sprintf (portstr, "-c %d", port_to_listen) ;
      DEBUGNL ("HgViewerDaemon::start(): child: execlp()") ;
      execlp (binary_.string(), binary_.string(), portstr, 0) ;

      // when you reach this part, the exec failed. report this to 
      // the caller by sending him a -1 as your port
      // +++++ replace this by a simple socket fd ?
      DEBUGNL ("HgViewerDaemon::start(): execlp failed") ;
      rpcstream errstream ;
#if (defined (__GNUC__) && !defined (OSF1))
      // ++++
      // gnu's ios deletes its streambuf on destruction.
      // rpcstream has a streambuf *member* (not pointer to) and passes it to ios via 
      // init(), which will delete it then --> nasty bug
      errstream.setf (ios::dont_close) ;
#endif   
      errstream.connect (host_, port_to_listen) ;
      errstream << -1 << flush ;
      _exit(0) ;
   }
   else if (pid_ > 0) {
      // parent 
      DEBUGNL ("HgViewerDaemon::start(): pid= "<<pid_) ;

      long sec = timeout_ ;
      long usec = 0 ;
      Dispatcher dp ;
      dp.link (init_port_.rdbuf()->fd(), Dispatcher::ReadMask, this) ;
      DEBUGNL ("HgViewerDaemon::start(): parent: select()") ;
      dp.dispatch (sec, usec) ;
      DEBUGNL ("HgViewerDaemon::start(): parent: select() returned") ;
      if (port_ < 0) {
         DEBUGNL ("HgViewerDaemon::start(): timeout exceeded or no valid port") ;
         quit() ;
      }
      else 
         running_ = true ;
   }
   init_port_.close() ;
   return running_ ;
}

void HgViewerDaemon :: quit() {
   if (! running_)
      return ;
   kill (pid_, SIGTERM) ;
   running_ = false ;
}

int HgViewerDaemon :: inputReady (int fd) {
   DEBUGNL ("HgViewerDaemon::inputReady()") ;

   hgassert (fd==init_port_.rdbuf()->fd(), "HgViewerDaemon::inputReady(): invalid file number") ;


   int newfd = init_port_.accept() ;
   if (newfd < 0) {
      DEBUGNL ("HgViewerDaemon::inputReady(): accept failed") ;
      return -1 ;
   }

   ifstream* fromdaemon = new ifstream (newfd) ;
   *fromdaemon >> port_ ;

   DEBUG ("HgViewerDaemon::start(): daemon running at port ") ;
   DEBUGNL (port_) ;

   delete fromdaemon ;
   ::close (newfd) ;
   return -1 ;
}





// **********************************************************
// class RemoteViewer
// **********************************************************
RemoteViewer :: RemoteViewer (const char* host, int port, HgViewerManager* manager,
                              int timeout) 
: HgViewer (manager),
  reader_(nil),
  writer_(nil),
  propagate_errors_(false),
  doc_port_(-1) {
     writer_ = new LocalWriter (host, port, false, false) ;
     if (! writer_->server()) {
	error_ = NOCONNECTION ;
	return ;
     }
     reader_ = new LocalReader (&writer_->server(), this) ;
     if (! reader_) {
	DEBUGNL ("RemoteViewer::RemoteViewer: nix reader_") ;
	error_ = NOCONNECTION ;
	return ;
     }

     ready_ = false ;
     Dispatcher::instance().startTimer (timeout, 0, reader_) ;
     while (!ready_  &&  !error_) {
        writer_->sendReady() ;
	Dispatcher::instance().dispatch() ;
     }

     DEBUGNL ("RemoteViewer::RemoteViewer(): error:"<<errorDescription(error_)) ;

     if (error_ != TIMEOUT)
	Dispatcher::instance().stopTimer (reader_) ;

     // everything is ready now, start propagating errors to the manager
     propagate_errors_ = true ;
     DEBUGNL ("RemoteViewer::RemoteViewer(): done") ;
  }

RemoteViewer :: RemoteViewer (HgViewerDaemon& daemon, HgViewerManager* manager, int timeout)
: HgViewer (manager),
  reader_(nil),
  writer_(nil),
  propagate_errors_(false),
  doc_port_(-1) {
     if (! (daemon.running() || daemon.start())) {
	error_ = EXECFAIL ;
	DEBUGNL ("RemoteViewer::RemoteViewer(): could not exec HgViewerDaemon") ;
	return ;
     }

     writer_ = new LocalWriter (daemon.host(), daemon.port(), false, false) ;
     if (! writer_->server()) {
	error_ = NOCONNECTION ;
	return ;
     }

     reader_ = new LocalReader (&writer_->server(), this) ;
     if (! reader_) {
	DEBUGNL ("RemoteViewer::RemoteViewer: nix reader_") ;
	error_ = NOCONNECTION ;
	return ;
     }

     ready_ = false ;
     Dispatcher::instance().startTimer (timeout, 0, reader_) ;
     while (!ready_  &&  !error_) {
        writer_->sendReady() ;
        DEBUGNL ("RemoteViewer::RemoteViewer(HgViewerDaemon&,...): dispatching") ;
	Dispatcher::instance().dispatch() ;        
        DEBUGNL ("RemoteViewer::RemoteViewer(HgViewerDaemon&,...): dispatched") ;
     }

     DEBUGNL ("RemoteViewer::RemoteViewer(): error: "<<errorDescription(error_)) ;

     if (error_ != TIMEOUT)
	Dispatcher::instance().stopTimer (reader_) ;

     // everything is ready now, start propagating errors to the manager
     propagate_errors_ = true ;
     DEBUGNL ("RemoteViewer::RemoteViewer(): done") ;
  }

RemoteViewer :: ~RemoteViewer() {
   delete writer_ ;
   delete reader_ ;
}


void RemoteViewer :: load (const char* doc, const char* anchors, char* info) {
   if (! info) {
      writer_->sendLoad (doc, anchors) ; // I don't want additional info back from the viewer
      return ;
   }

   writer_->sendLoad1 (doc, anchors) ; // I do want !
   running_ = true ;
   while (running_)
      Dispatcher::instance().dispatch() ;
   strcpy (info, string_.string()) ;
}
void RemoteViewer :: browse (const char* dest) {
   writer_->sendBrowse (dest) ;
}
void RemoteViewer :: terminate() {
   reader_->terminated (true) ;
   writer_->sendTerminate() ;
}

void RemoteViewer :: cancelRequest (const char* str) {
   writer_->sendCancelRequest (str) ;
}

void RemoteViewer :: holdDocument (const char* s) {
   writer_->sendHoldDocument (s) ;
}
void RemoteViewer :: iconify() {
   writer_->sendIconify() ;
}
void RemoteViewer :: deiconify() {
   writer_->sendDeiconify() ;
}
void RemoteViewer :: moveto (float x, float y) {
   writer_->sendMoveto (x,y) ;
}
void RemoteViewer :: resize (float x, float y) {
   writer_->sendResize (x,y) ;
}
void RemoteViewer :: map() {
   writer_->sendMap() ;
}
void RemoteViewer :: unmap() {
   writer_->sendUnmap() ;
}
void RemoteViewer :: raise() {
   writer_->sendRaise() ;
}
void RemoteViewer :: lower() {
   writer_->sendLower() ;
}

void RemoteViewer :: setLanguage (HgLanguage::Language l) {
   writer_->sendSetLanguage (l) ;
}

void RemoteViewer :: saveResources() {
   writer_->sendSaveResources() ;
}

void RemoteViewer :: doEdit (const RString& object, const RString& anchors) {
   writer_->sendDoEdit (object, anchors) ;
}
void RemoteViewer :: newDoc (int refno, const RString& info) {
   writer_->sendNewDoc (refno, info) ;
}

void RemoteViewer :: getPos (RString& pos) {
   Dispatcher::instance().unlink (writer_->server().rdbuf()->fd()) ;
   Dispatcher dp ;
   dp.link (writer_->server().rdbuf()->fd(), Dispatcher::ReadMask, reader_) ;
   writer_->sendGetPos() ;
   running_ = true ;
   string_ = RString() ;
   while (running_) {
      long sec = 10 ;
      long usec = 0 ;
      dp.dispatch (sec, usec) ;
      if (! sec) {
         DEBUGNL ("RemoteViewer::getPos(): timeout") ;
         running_ = false ;
      }
   }
   pos = string_ ;
   dp.unlink (writer_->server().rdbuf()->fd()) ;
   Dispatcher::instance().link (writer_->server().rdbuf()->fd(), Dispatcher::ReadMask, reader_) ;
}


int RemoteViewer :: port() const {
   return doc_port_ ;
}


// **********************************************************
// class LocalWriter
// **********************************************************
LocalWriter :: LocalWriter(const char* host, int port, boolean fatal, boolean binary)
: RpcWriter(host, port, fatal, binary) {
#if (defined (__GNUC__) && !defined (OSF1))
   // ++++
   // gnu's ios deletes its streambuf on destruction.
   // rpcstream has a streambuf *member* (not pointer to) and passes it to ios via 
   // init(), which will delete it then --> nasty bug
   server().setf (ios::dont_close) ;
#endif   
}

void LocalWriter :: sendReady() {
   DEBUGNL ("LocalWriter::sendReady()") ;
   server() << RpcHdr (nil, READY) ;
   server().flush() ;
}
void LocalWriter :: sendLoad (const char* doc, const char* anchors) {
   DEBUGNL ("LocalWriter::sendLoad():") ;
   DEBUGNL ("Document:\n"<<doc) ;
   DEBUGNL ("Anchors:\n"<<anchors) ;
   server() << RpcHdr (nil, LOAD) ;
   server() << doc ;
   server() << anchors ;
   server().flush() ;
}
void LocalWriter :: sendLoad1 (const char* doc, const char* anchors) {
   DEBUGNL ("LocalWriter::sendLoad1():") ;
   DEBUGNL ("Document:\n"<<doc) ;
   DEBUGNL ("Anchors:\n"<<anchors) ;
   server() << RpcHdr (nil, LOAD1) ;
   server() << doc ;
   server() << anchors ;
   server().flush() ;
}
void LocalWriter :: sendBrowse (const char* dest) {
   DEBUGNL ("LocalWriter::sendBrowse():") ;
   server() << RpcHdr (nil, BROWSE) ;
   if (dest) {
      DEBUGNL ("Destination:\n"<<dest) ;
      server() << dest ;
   }
   server().flush() ;
}
void LocalWriter :: sendTerminate() {
   DEBUGNL ("LocalWriter::sendTerminate()") ;
   server() << RpcHdr (nil, TERMINATE) ;
   server().flush() ;
}
void LocalWriter :: sendCancelRequest (const char* s) {
   DEBUGNL ("LocalWriter::sendCancelRequest()") ;
   server() << RpcHdr (nil, CANCELREQUEST) ;
   server() << s ;
   server().flush() ;
}

void LocalWriter :: sendHoldDocument (const char* s) {
   DEBUGNL ("LocalWriter::sendHoldDocument()") ;
   server() << RpcHdr (nil, HOLDDOCUMENT) ;
   server() << s ;
   server().flush() ;
}
void LocalWriter :: sendIconify() {
   DEBUGNL ("LocalWriter::sendIconify()") ;
   server() << RpcHdr (nil, ICONIFY) ;
   server().flush() ;
}
void LocalWriter :: sendDeiconify() {
   DEBUGNL ("LocalWriter::sendDeiconify()") ;
   server() << RpcHdr (nil, DEICONIFY) ;
   server().flush() ;
}
void LocalWriter :: sendMoveto (float x, float y) {
   DEBUGNL ("LocalWriter::sendMoveto()") ;
   server() << RpcHdr (nil, MOVETO) ;
   server() << x ;
   server() << y ;
   server().flush() ;
}
void LocalWriter :: sendResize (float x, float y) {
   DEBUGNL ("LocalWriter::sendRaise()") ;
   server() << RpcHdr (nil, RAISE) ;
   server() << x ;
   server() << y ;
   server().flush() ;
}
void LocalWriter :: sendMap() {
   DEBUGNL ("LocalWriter::sendMap()") ;
   server() << RpcHdr (nil, MAP) ;
   server().flush() ;
}
void LocalWriter :: sendUnmap() {
   DEBUGNL ("LocalWriter::sendUnmap()") ;
   server() << RpcHdr (nil, UNMAP) ;
   server().flush() ;
}
void LocalWriter :: sendRaise() {
   DEBUGNL ("LocalWriter::sendRaise()") ;
   server() << RpcHdr (nil, RAISE) ;
   server().flush() ;
}
void LocalWriter :: sendLower() {
   DEBUGNL ("LocalWriter::sendLower()") ;
   server() << RpcHdr (nil, LOWER) ;
   server().flush() ;
}

void LocalWriter :: sendSetLanguage (int l) {
   DEBUGNL ("LocalWriter::sendSetLanguage(): "<<l) ;
   server() << RpcHdr (nil, SETLANG) ;
   server() << l ;
   server().flush() ;
}

void LocalWriter :: sendSaveResources() {
   DEBUGNL ("LocalWriter::sendSaveResources()") ;
   server() << RpcHdr (nil, SAVERESOURCES) ;
   server().flush() ;
}
void LocalWriter :: sendDoEdit (const RString& object, const RString& anchors) {
   DEBUGNL ("LocalWriter::sendDoEdit()") ;
   server() << RpcHdr (nil, DOEDIT) ;
   server() << object.string() ;
   server() << anchors.string() ;
   server().flush() ;
}
void LocalWriter :: sendNewDoc (int refno, const RString& info) {
   DEBUGNL ("LocalWriter::sendNewDoc()") ;
   server() << RpcHdr (nil, NEWDOC) ;
   server() << refno ;
   server() << info.string() ;
   server().flush() ;
}
void LocalWriter :: sendGetPos() {
   DEBUGNL ("LocalWriter::sendGetPos()") ;
   server() << RpcHdr (nil, GETPOS) ;
   server().flush() ;
}

// **********************************************************
// class LocalReader
// **********************************************************
LocalReader :: LocalReader (rpcstream* stream, RemoteViewer* viewer) 
: RpcReader (stream, NFCNS), 
  viewer_(viewer),
  viewer_terminated_ (false) {
   DEBUGNL ("LocalReader: nonblocking: "<<client().rdbuf()->nonblocking()) ;

   _function[READY] = &LocalReader::receive_READY ;
   _function[FOLLOWLINK] = &LocalReader::receive_FOLLOWLINK ;
   _function[ERROR] = &LocalReader::receive_ERROR ;
   _function[TERMINATED] = &LocalReader::receive_TERMINATED ;
   _function[ADDINFO] = &LocalReader::receive_ADDINFO ;
   _function[DEFSRCANCH] = &LocalReader::receive_DEFSRCANCH ;
   _function[DEFDESTANCH] = &LocalReader::receive_DEFDESTANCH ;
   _function[DELLINK] = &LocalReader::receive_DELLINK ;
   _function[SHOWREFERENCES] = &LocalReader::receive_SHOWREFERENCES ;
   _function[SHOWANNOTATIONS] = &LocalReader::receive_SHOWANNOTATIONS ;
   _function[SHOWPARENTS] = &LocalReader::receive_SHOWPARENTS ;
   _function[SHOWATTRIBUTES] = &LocalReader::receive_SHOWATTRIBUTES ;
   _function[BACK] = &LocalReader::receive_BACK ;
   _function[FORWARD] = &LocalReader::receive_FORWARD ;
   _function[HISTORY] = &LocalReader::receive_HISTORY ;
   _function[HOLD] = &LocalReader::receive_HOLD ;
   _function[EDIT] = &LocalReader::receive_EDIT ;
   _function[UPLOAD] = &LocalReader::receive_UPLOAD ;
   _function[CANCELEDIT] = &LocalReader::receive_CANCELEDIT ;
   _function[MODIFYATTRIBUTE] = &LocalReader::receive_MODIFYATTRIBUTE ;
   _function[RELOADDOCUMENT] = &LocalReader::receive_RELOADDOCUMENT ;
   _function[STRING] = &LocalReader::receive_STRING ;
}



// overloaded for testing purposes:
int LocalReader::inputReady(int fd) {
   DEBUGNL ("LocalReader::inputReady") ;
   RpcHdr hdr;

   client() >> hdr;
   
   if (client().good() && !client().incomplete_request()) {
      RpcReader* reader = map(hdr.reader());
      
      if (!execute(reader, hdr)) {
	 DEBUGNL ("LocalReader::inputReady: nix execute "<<hdr.request()) ;
	 client().ignore(hdr.ndata());
      }
   }

   if (client().eof() || client().fail()) {
      if (client().fail())
	 DEBUGNL ("LocalReader::inputReady(): fail") ;
      if (client().eof())
	 DEBUGNL ("LocalReader::inputReady(): eof") ;
      connectionClosed(fd);
      return -1;		// don't ever call me again (i.e., detach me)
   } else if (client().incomplete_request()) {
      DEBUGNL ("RemoteReader::inputReady: incomplete_request") ;
      return 0;		// call me only when more input arrives
   } else {
      return 1;		// call me again as soon as possible
   }
}

void LocalReader :: connectionClosed (int) {
   DEBUGNL ("LocalReader::connectionClosed()") ;
   // if the viewer was terminated previously, don't report this to the boss
   // because it's not an error
   if (! viewer_terminated_) {
      DEBUGNL ("LocalReader::connectionClosed(): ! viewer_terminated_") ;
      hgassert (viewer_, "LocalReader::connectionClosed(): viewer_ nil") ;
      Dispatcher::instance().unlink (viewer_->writer_->server().rdbuf()->fd()) ;
      viewer_->error_ = HgViewer::CONNCLOSED ;
      viewer_->running_ = false ;
      if (viewer_->propagate_errors_) {
         DEBUGNL ("LocalReader::connectionClosed(): propagate_errors_") ;
         hgassert (viewer_->manager_, "LocalReader::connectionClosed(): viewer_->manager_ nil") ;
	 viewer_->manager_->viewerError (viewer_) ;
      }
   }
   viewer_terminated_ = false ;
   DEBUGNL ("LocalReader::connectionClosed(): propagate_errors_") ;
}

void LocalReader :: timerExpired (long, long) {
   viewer_->error_ = HgViewer::TIMEOUT ;
   if (viewer_->propagate_errors_)
      viewer_->manager_->viewerError (viewer_) ;
}

void LocalReader :: receive_READY (RpcReader* reader, RpcHdr& hdr, rpcstream& client) {
   DEBUGNL ("LocalReader::receive_READY()") ;

   if (((LocalReader*)reader)->viewer_->ready_) {
      DEBUGNL ("LocalReader::receive_READY(): ignoring READY") ;
      client.ignore(hdr.ndata()) ;
      return ;
   }

   // read doc receive port of the viewer
   client >> ((LocalReader*)reader)->viewer_->doc_port_ ;
   DEBUGNL ("LocalReader::receive_READY(): doc_port_="<< 
            ((LocalReader*)reader)->viewer_->doc_port_) ;

   // read the options of the viewer
   char* options = new char [hdr.ndata()+1] ;
   client >> options ;
   DEBUG ("LocalReader::receive_READY(): options_=") ;
   DEBUGNL (options) ;
   ((LocalReader*)reader)->viewer_->options_ = options ;
   delete options ;
   
   ((LocalReader*)reader)->viewer_->ready_ = true ;
   ((LocalReader*)reader)->viewer_->running_ = false ;
}

void LocalReader :: receive_FOLLOWLINK (RpcReader* reader, RpcHdr& hdr, rpcstream& client) {
   DEBUGNL ("LocalReader::receive_FOLLOWLINK()") ;
   char* link = new char [hdr.ndata() + 1] ;
   client >> link ;
   ((LocalReader*)reader)->viewer_->manager_->followLink (link, ((LocalReader*)reader)->viewer_) ;
   delete link ;
}
void LocalReader :: receive_ERROR (RpcReader* reader, RpcHdr&, rpcstream& client) {
   DEBUGNL ("LocalReader::receive_ERROR()") ;
   int error ;
   client >> error ;
   ((LocalReader*)reader)->viewer_->error_ = (HgViewer::VwError) error ;
   ((LocalReader*)reader)->viewer_->manager_->viewerError (((LocalReader*)reader)->viewer_) ;
}
void LocalReader :: receive_TERMINATED (RpcReader* reader, RpcHdr& header, rpcstream& client) {
   char* obj = new char[header.ndata()+1] ;
   client >> obj ;
   DEBUGNL ("LocalReader::receive_TERMINATED()") ;
   // a connectionClosed() will follow immediately, don't report it to the boss:
   ((LocalReader*)reader)->viewer_terminated_ = true ;
   ((LocalReader*)reader)->viewer_->manager_->viewerTerminated (obj, 
                                                                ((LocalReader*)reader)->viewer_) ;
   delete obj ;
}

void LocalReader :: receive_ADDINFO (RpcReader* reader, RpcHdr& header, rpcstream& client) {
   DEBUGNL ("LocalReader::receive_ADDINFO()") ;

   char* info = new char[header.ndata() + 1] ;
   client >> info ;
   ((LocalReader*)reader)->viewer_->string_ = info ;
   delete info ;
   ((LocalReader*)reader)->viewer_->running_ = false ;
}

void LocalReader :: receive_DEFSRCANCH (RpcReader* reader, RpcHdr& header, rpcstream& client) {
   DEBUGNL ("LocalReader::receive_DEFSRCANCH()") ;
   RString anchor ;
   char* tmp = new char[header.ndata() + 1] ;
   client >> tmp ;   anchor = tmp ;
   delete tmp ;
   ((LocalReader*)reader)->viewer_->manager_->defineSourceAnchor (anchor,
                                                                  ((LocalReader*)reader)->viewer_);
}
void LocalReader :: receive_DEFDESTANCH (RpcReader* reader, RpcHdr& header, rpcstream& client) {
   DEBUGNL ("LocalReader::receive_DEFDESTANCH()") ;
   RString anchor ;
   char* tmp = new char[header.ndata() + 1] ;
   client >> tmp ;   anchor = tmp ;
   delete tmp ;
   ((LocalReader*)reader)->viewer_->manager_->defineDestAnchor (anchor, 
                                                                ((LocalReader*)reader)->viewer_) ;
}

void LocalReader :: receive_DELLINK (RpcReader* reader, RpcHdr& header, rpcstream& client) {
   DEBUGNL ("LocalReader::receive_DELLINK()") ;
   RString link ;
   char* tmp = new char[header.ndata() + 1] ;
   client >> tmp ;  link = tmp ;
   delete tmp ;
   ((LocalReader*)reader)->viewer_->manager_->deleteLink (link,
                                                          ((LocalReader*)reader)->viewer_) ;
}


void LocalReader :: receive_SHOWREFERENCES (RpcReader* reader, RpcHdr& header, rpcstream& client) {
   DEBUGNL ("LocalReader::receive_SHOWREFERENCES()") ;
   RString object ;
   char* tmp = new char[header.ndata() + 1] ;
   client >> tmp ;   object = tmp ;
   delete tmp ;
   ((LocalReader*)reader)->viewer_->manager_->showReferences (object,
                                                              ((LocalReader*)reader)->viewer_) ;
}
void LocalReader :: receive_SHOWANNOTATIONS (RpcReader* reader, RpcHdr& header, rpcstream& client) {
   DEBUGNL ("LocalReader::receive_SHOWANNOTATIONS()") ;
   RString object ;
   char* tmp = new char[header.ndata() + 1] ;
   client >> tmp ;   object = tmp ;
   delete tmp ;
   ((LocalReader*)reader)->viewer_->manager_->showAnnotations (object,
                                                               ((LocalReader*)reader)->viewer_) ;
}
void LocalReader :: receive_SHOWPARENTS (RpcReader* reader, RpcHdr& header, rpcstream& client) {
   DEBUGNL ("LocalReader::receive_SHOWPARENTS()") ;
   RString object ;
   char* tmp = new char[header.ndata() + 1] ;
   client >> tmp ;   object = tmp ;
   delete tmp ;
   ((LocalReader*)reader)->viewer_->manager_->showParents (object,
                                                           ((LocalReader*)reader)->viewer_) ;
}
void LocalReader :: receive_SHOWATTRIBUTES (RpcReader* reader, RpcHdr& header, rpcstream& client) {
   DEBUGNL ("LocalReader::receive_SHOWATTRIBUTES()") ;
   RString object ;
   char* tmp = new char[header.ndata() + 1] ;
   client >> tmp ;   object = tmp ;
   delete tmp ;
   ((LocalReader*)reader)->viewer_->manager_->showAttributes (object,
                                                              ((LocalReader*)reader)->viewer_) ;
}
void LocalReader :: receive_BACK (RpcReader* reader, RpcHdr&, rpcstream&) {
   DEBUGNL ("LocalReader::receive_BACK()") ;
   ((LocalReader*)reader)->viewer_->manager_->back (((LocalReader*)reader)->viewer_) ;
}
void LocalReader :: receive_FORWARD (RpcReader* reader, RpcHdr&, rpcstream&) {
   DEBUGNL ("LocalReader::receive_FORWARD()") ;
   ((LocalReader*)reader)->viewer_->manager_->forward (((LocalReader*)reader)->viewer_) ;
}
void LocalReader :: receive_HISTORY (RpcReader* reader, RpcHdr&, rpcstream&) {
   DEBUGNL ("LocalReader::receive_HISTORY()") ;
   ((LocalReader*)reader)->viewer_->manager_->history (((LocalReader*)reader)->viewer_) ;
}
void LocalReader :: receive_HOLD (RpcReader* reader, RpcHdr& header, rpcstream& client) {
   DEBUGNL ("LocalReader::receive_HOLD()") ;
   RString object ;
   char* tmp = new char[header.ndata() + 1] ;
   client >> tmp ;   object = tmp ;
   delete tmp ;
   ((LocalReader*)reader)->viewer_->manager_->hold (object, ((LocalReader*)reader)->viewer_) ;
}
void LocalReader :: receive_EDIT (RpcReader* reader, RpcHdr& header, rpcstream& client) {
   DEBUGNL ("LocalReader::receive_EDIT()") ;
   RString object ;
   char* tmp = new char[header.ndata() + 1] ;
   client >> tmp ;   object = tmp ;
   delete tmp ;
   ((LocalReader*)reader)->viewer_->manager_->edit (object, ((LocalReader*)reader)->viewer_) ;
}
void LocalReader :: receive_UPLOAD (RpcReader* reader, RpcHdr& header, rpcstream& client) {
   DEBUGNL ("LocalReader::receive_UPLOAD()") ;
   RString object ;
   RString anchors ;
   RString host ;
   int port ;
   int refno ;
   char* tmp = new char[header.ndata() + 1] ;
   client >> tmp ;   object = tmp ;
   client >> tmp ;   anchors = tmp ;
   client >> tmp ;   host = tmp ;
   client >> port ;
   client >> refno ;
   delete tmp ;
   ((LocalReader*)reader)->viewer_->manager_->upload (object, anchors, host, port, 
                                                      ((LocalReader*)reader)->viewer_, refno) ;
}
void LocalReader :: receive_CANCELEDIT (RpcReader* reader, RpcHdr& header, rpcstream& client) {
   DEBUGNL ("LocalReader::receive_CANCELEDIT()") ;
   RString object ;
   char* tmp = new char[header.ndata() + 1] ;
   client >> tmp ;   object = tmp ;
   delete tmp ;
   ((LocalReader*)reader)->viewer_->manager_->cancelEdit (object, ((LocalReader*)reader)->viewer_) ;
}
void LocalReader :: receive_STRING (RpcReader* reader, RpcHdr& header, rpcstream& client) {
   DEBUGNL ("LocalReader::receive_STRING()") ;
   char* tmp = new char [header.ndata() + 1] ;
   client >> tmp ;
   ((LocalReader*)reader)->viewer_->string_ = tmp ;
   ((LocalReader*)reader)->viewer_->running_ = false ;
   delete tmp ;
}
void LocalReader :: receive_MODIFYATTRIBUTE (RpcReader* reader, RpcHdr& header, rpcstream& client) {
   DEBUGNL ("LocalReader::receive_MODIFYATTRIBUTE()") ;
   RString object ;
   char* tmp = new char[header.ndata() + 1] ;
   client >> tmp ;   object = tmp ;
   delete tmp ;
   ((LocalReader*)reader)->viewer_->manager_->modifyAttribute (object) ;
}
void LocalReader :: receive_RELOADDOCUMENT (RpcReader* reader, RpcHdr& header, rpcstream& client) {
   DEBUGNL ("LocalReader::receive_RELOADDOCUMENT()") ;
   RString object ;
   char* tmp = new char[header.ndata() + 1] ;
   client >> tmp ;   object = tmp ;
   delete tmp ;
   ((LocalReader*)reader)->viewer_->manager_->reloadDocument (object) ;
}

