]> git.lyx.org Git - lyx.git/blob - src/lyxsocket.C
fix arabtex-related problems (bug 1225 and bug 1404)
[lyx.git] / src / lyxsocket.C
1 /**
2  * \file lyxsocket.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Jean-Marc Lasgouttes
8  * \author Angus Leeming
9  * \author John Levon
10  * \author João Luis M. Assirati
11  *
12  * Full author contact details are available in file CREDITS.
13  */
14
15 #include "lyxsocket.h"
16
17 #include "debug.h"
18 #include "funcrequest.h"
19 #include "LyXAction.h"
20 #include "lyxfunc.h"
21
22 #include "frontends/lyx_gui.h"
23
24 #include "support/lyxlib.h"
25 #include "support/socktools.h"
26
27 #include <iostream>
28 #include <cerrno>
29
30
31 using std::auto_ptr;
32 using std::endl;
33 using std::string;
34
35
36 // Address is the unix address for the socket.
37 // MAX_CLIENTS is the maximum number of clients
38 // that can connect at the same time.
39 LyXServerSocket::LyXServerSocket(LyXFunc * f, string const & addr)
40         : func(f),
41           fd_(lyx::support::socktools::listen(addr, MAX_CLIENTS)),
42           address_(addr)
43 {
44         if (fd_ == -1) {
45                 lyxerr << "lyx: Disabling LyX socket." << endl;
46                 return;
47         }
48
49         // These env vars are used by DVI inverse search
50         // Needed by xdvi
51         lyx::support::putenv("XEDITOR", "lyxclient -g %f %l");
52         // Needed by lyxclient
53         lyx::support::putenv("LYXSOCKET", address_);
54
55         lyx_gui::set_serversocket_callback(this);
56         lyxerr[Debug::LYXSERVER] << "lyx: New server socket "
57                                  << fd_ << ' ' << address_ << endl;
58 }
59
60
61 // Close the socket and remove the address of the filesystem.
62 LyXServerSocket::~LyXServerSocket()
63 {
64         ::close(fd_);
65         lyx::support::unlink(address_);
66         while (!clients.empty()) close(*clients.rbegin());
67         lyxerr[Debug::LYXSERVER] << "lyx: Server socket quitting" << endl;
68 }
69
70
71 int LyXServerSocket::fd() const
72 {
73         return fd_;
74 }
75
76
77 string const & LyXServerSocket::address() const
78 {
79         return address_;
80 }
81
82
83 // Creates a new LyXDataSocket and checks to see if the connection
84 // is OK and if the number of clients does not exceed MAX_CLIENTS
85 void LyXServerSocket::serverCallback()
86 {
87         auto_ptr<LyXDataSocket> client(new LyXDataSocket(this));
88         if (client->connected()) {
89                 if (clients.size() == MAX_CLIENTS) {
90                         client->writeln("BYE:Too many clients connected");
91                 } else {
92                         lyx_gui::set_datasocket_callback(client.get());
93                         clients.insert(client.release());
94                         return;
95                 }
96         }
97 }
98
99
100 // Reads and processes input from client and check
101 // if the connection has been closed
102 void LyXServerSocket::dataCallback(LyXDataSocket * client)
103 {
104         string line;
105         string::size_type pos;
106         bool saidbye = false;
107         while ((!saidbye) && client->readln(line)) {
108                 // The protocol must be programmed here
109                 // Split the key and the data
110                 if ((pos = line.find(':')) == string::npos) {
111                         client->writeln("ERROR:" + line + ":malformed message");
112                         continue;
113                 }
114
115                 string const key = line.substr(0, pos);
116                 if (key == "LYXCMD") {
117                         string const cmd = line.substr(pos + 1);
118                         func->dispatch(lyxaction.lookupFunc(cmd));
119                         string const rval = func->getMessage();
120                         if (func->errorStat()) {
121                                 client->writeln("ERROR:" + cmd + ':' + rval);
122                         } else {
123                                 client->writeln("INFO:" + cmd + ':' + rval);
124                         }
125                 } else if (key == "HELLO") {
126                         // no use for client name!
127                         client->writeln("HELLO:");
128                 } else if (key == "BYE") {
129                         saidbye = true;
130                 } else {
131                         client->writeln("ERROR:unknown key " + key);
132                 }
133         }
134
135         if (saidbye || (!client->connected())) {
136                 close(client);
137         }
138 }
139
140
141 // Removes client callback and deletes client object
142 void LyXServerSocket::close(LyXDataSocket * client)
143 {
144         lyx_gui::remove_datasocket_callback(client);
145         clients.erase(client);
146         delete client;
147 }
148
149 // Debug
150 // void LyXServerSocket::dump() const
151 // {
152 //      lyxerr << "LyXServerSocket debug dump.\n"
153 //           << "fd = " << fd_ << ", address = " << address_ << ".\n"
154 //           << "Clients: " << clients.size() << ".\n";
155 //      if (!clients.empty()) {
156 //              std::set<LyXDataSocket *>::const_iterator client = clients.begin();
157 //              std::set<LyXDataSocket *>::const_iterator end = clients.end();
158 //              for (; client != end; ++client)
159 //                      lyxerr << "fd = " << (*client)->fd() << "\n";
160 //      }
161 // }
162
163
164 LyXDataSocket::LyXDataSocket(LyXServerSocket * serv)
165         :server_(serv),
166          fd_(lyx::support::socktools::accept(serv->fd()))
167 {
168         if (fd_ == -1) {
169                 connected_ = false;
170         } else {
171                 lyxerr[Debug::LYXSERVER] << "lyx: New data socket " << fd_ << endl;
172                 connected_ = true;
173         }
174 }
175
176
177 LyXDataSocket::~LyXDataSocket()
178 {
179         ::close(fd_);
180         lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_ << " quitting." << endl;
181 }
182
183
184 LyXServerSocket * LyXDataSocket::server() const
185 {
186         return server_;
187 }
188
189
190 int LyXDataSocket::fd() const
191 {
192         return fd_;
193 }
194
195
196 bool LyXDataSocket::connected() const
197 {
198         return connected_;
199 }
200
201
202 // Returns true if there was a complete line to input
203 bool LyXDataSocket::readln(string & line)
204 {
205         int const charbuf_size = 100;
206         char charbuf[charbuf_size]; // buffer for the ::read() system call
207         int count;
208         string::size_type pos;
209
210         // read and store characters in buffer
211         while ((count = ::read(fd_, charbuf, charbuf_size - 1)) > 0) {
212                 charbuf[count] = '\0'; // turn it into a c string
213                 buffer += charbuf;
214         }
215
216         // Error conditions. The buffer must still be
217         // processed for lines read
218         if (count == 0) { // EOF -- connection closed
219                 lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_
220                                          << ": connection closed." << endl;
221                 connected_ = false;
222         } else if ((count == -1) && (errno != EAGAIN)) { // IO error
223                 lyxerr << "lyx: Data socket " << fd_
224                        << ": IO error." << endl;
225                 connected_ = false;
226         }
227
228         // Cut a line from buffer
229         if ((pos = buffer.find('\n')) == string::npos) {
230                 lyxerr[Debug::LYXSERVER] << "lyx: Data socket " << fd_
231                                          << ": line not completed." << endl;
232                 return false; // No complete line stored
233         }
234         line = buffer.substr(0, pos);
235         buffer = buffer.substr(pos + 1);
236         return true;
237 }
238
239
240 // Write a line of the form <key>:<value> to the socket
241 void LyXDataSocket::writeln(string const & line)
242 {
243         string linen(line + '\n');
244         int size = linen.size();
245         int written = ::write(fd_, linen.c_str(), size);
246         if (written < size) { // Allways mean end of connection.
247                 if ((written == -1) && (errno == EPIPE)) {
248                         // The program will also receive a SIGPIPE
249                         // that must be catched
250                         lyxerr << "lyx: Data socket " << fd_
251                                << " connection closed while writing." << endl;
252                 } else {
253                         // Anything else, including errno == EAGAIN, must be
254                         // considered IO error. EAGAIN should never happen
255                         // when line is small
256                         lyxerr << "lyx: Data socket " << fd_
257                              << " IO error: " << strerror(errno);
258                 }
259                 connected_ = false;
260         }
261 }