]> git.lyx.org Git - features.git/blob - src/client/client.cpp
Fix pasting as LaTeX in documents with no modules
[features.git] / src / client / client.cpp
1 /**
2  * \file client.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author João Luis M. Assirati
7  * \author Lars Gullik Bjønnes
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12
13 #include <config.h>
14
15 #include "LyXRC.h"
16
17 #include "support/ConsoleApplication.h"
18 #include "support/debug.h"
19 #include "support/FileName.h"
20 #include "support/FileNameList.h"
21 #include "support/lstrings.h"
22 #include "support/Messages.h"
23 #include "support/unicode.h"
24 #include "support/unique_ptr.h"
25
26 // getpid(), getppid()
27 #ifdef HAVE_SYS_TYPES_H
28 # include <sys/types.h>
29 #endif
30 #ifdef HAVE_UNISTD_H
31 # include <unistd.h>
32 #endif
33
34 // struct timeval
35 #ifdef HAVE_SYS_TIME_H
36 # include <sys/time.h>
37 #endif
38
39 // select()
40 #ifdef HAVE_SYS_SELECT_H
41 # include <sys/select.h>
42 #endif
43
44 // socket(), connect()
45 #ifdef HAVE_SYS_SOCKET_H
46 # include <sys/socket.h>
47 #endif
48 #include <sys/un.h>
49
50 // fcntl()
51 #include <fcntl.h>
52
53 // strerror()
54 #include <string.h>
55
56 #include <cerrno>
57 #include <cstdio>
58 #include <cstdlib>
59 #include <exception>
60 #include <string>
61 #include <vector>
62 #include <map>
63 #include <iostream>
64
65
66 using namespace std;
67 using namespace lyx::support;
68
69 namespace lyx {
70
71 // Required global variables
72 bool verbose = false;
73 LyXRC lyxrc;
74
75 // Keep the linker happy on Windows
76 void lyx_exit(int)
77 {}
78
79 // Dummy language support
80 Messages const & getGuiMessages()
81 {
82         static Messages lyx_messages;
83
84         return lyx_messages;
85 }
86
87
88 Messages const & getMessages(string const &)
89 {
90         return getGuiMessages();
91 }
92
93
94 namespace support {
95
96 /// Returns the absolute pathnames of all lyx local sockets in
97 /// file system encoding.
98 /// Parts stolen from lyx::support::DirList().
99 FileNameList lyxSockets(string const & dir, string const & pid)
100 {
101         FileNameList dirlist;
102
103         FileName dirpath(dir + "/");
104
105         if (!dirpath.exists() || !dirpath.isDirectory()) {
106                 lyxerr << dir << " does not exist or is not a directory."
107                        << endl;
108                 return dirlist;
109         }
110
111         FileNameList dirs = dirpath.dirList("");
112         FileNameList::const_iterator it = dirs.begin();
113         FileNameList::const_iterator end = dirs.end();
114
115         for (; it != end; ++it) {
116                 if (!it->isDirectory())
117                         continue;
118                 string const tmpdir = it->absFileName();
119                 if (!contains(tmpdir, "lyx_tmpdir" + pid))
120                         continue;
121
122                 FileName lyxsocket(tmpdir + "/lyxsocket");
123                 if (lyxsocket.exists())
124                         dirlist.push_back(lyxsocket);
125         }
126
127         return dirlist;
128 }
129
130
131 namespace socktools {
132
133
134 /// Connect to the socket \p name.
135 int connect(FileName const & name)
136 {
137         int fd; // File descriptor for the socket
138         sockaddr_un addr; // Structure that hold the socket address
139
140         string const encoded = name.toFilesystemEncoding();
141         // char sun_path[108]
142         string::size_type len = encoded.size();
143         if (len > 107) {
144                 cerr << "lyxclient: Socket address '" << name
145                      << "' too long." << endl;
146                 return -1;
147         }
148         // Synonims for AF_UNIX are AF_LOCAL and AF_FILE
149         addr.sun_family = AF_UNIX;
150         encoded.copy(addr.sun_path, 107);
151         addr.sun_path[len] = '\0';
152
153         if ((fd = ::socket(PF_UNIX, SOCK_STREAM, 0))== -1) {
154                 cerr << "lyxclient: Could not create socket descriptor: "
155                      << strerror(errno) << endl;
156                 return -1;
157         }
158         if (::connect(fd,
159                       reinterpret_cast<struct sockaddr *>(&addr),
160                       sizeof(addr)) == -1) {
161                 cerr << "lyxclient: Could not connect to socket " << name.absFileName()
162                      << ": " << strerror(errno) << endl;
163                 ::close(fd);
164                 return -1;
165         }
166         if (::fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
167                 cerr << "lyxclient: Could not set O_NONBLOCK for socket: "
168                      << strerror(errno) << endl;
169                 ::close(fd);
170                 return -1;
171         }
172         return fd;
173 }
174
175
176 } // namespace socktools
177 } // namespace support
178
179
180
181 /////////////////////////////////////////////////////////////////////
182 //
183 // IOWatch
184 //
185 /////////////////////////////////////////////////////////////////////
186
187 class IOWatch {
188 public:
189         IOWatch();
190         void clear();
191         void addfd(int);
192         bool wait(double);
193         bool wait();
194         bool isset(int fd);
195 private:
196         fd_set des;
197         fd_set act;
198 };
199
200
201 IOWatch::IOWatch()
202 {
203         clear();
204 }
205
206
207 void IOWatch::clear()
208 {
209         FD_ZERO(&des);
210 }
211
212
213 void IOWatch::addfd(int fd)
214 {
215         FD_SET(fd, &des);
216 }
217
218
219 bool IOWatch::wait(double timeout)
220 {
221         timeval to;
222         to.tv_sec = static_cast<long int>(timeout);
223         to.tv_usec = static_cast<long int>((timeout - to.tv_sec)*1E6);
224         act = des;
225         return select(FD_SETSIZE, &act,
226                       (fd_set *)0, (fd_set *)0, &to);
227 }
228
229
230 bool IOWatch::wait()
231 {
232         act = des;
233         return select(FD_SETSIZE, &act,
234                       (fd_set *)0, (fd_set *)0, (timeval *)0);
235 }
236
237
238 bool IOWatch::isset(int fd)
239 {
240         return FD_ISSET(fd, &act);
241 }
242
243
244
245 /////////////////////////////////////////////////////////////////////
246 //
247 // LyXDataSocket
248 //
249 /////////////////////////////////////////////////////////////////////
250
251 // Modified LyXDataSocket class for use with the client
252 class LyXDataSocket {
253 public:
254         LyXDataSocket(FileName const &);
255         ~LyXDataSocket();
256         // File descriptor of the connection
257         int fd() const;
258         // Connection status
259         bool connected() const;
260         // Line buffered input from the socket
261         bool readln(string &);
262         // Write the string + '\n' to the socket
263         void writeln(string const &);
264 private:
265         // File descriptor for the data socket
266         int fd_;
267         // True if the connection is up
268         bool connected_;
269         // buffer for input data
270         string buffer;
271 };
272
273
274 LyXDataSocket::LyXDataSocket(FileName const & address)
275 {
276         if ((fd_ = socktools::connect(address)) == -1) {
277                 connected_ = false;
278         } else {
279                 connected_ = true;
280         }
281 }
282
283
284 LyXDataSocket::~LyXDataSocket()
285 {
286         ::close(fd_);
287 }
288
289
290 int LyXDataSocket::fd() const
291 {
292         return fd_;
293 }
294
295
296 bool LyXDataSocket::connected() const
297 {
298         return connected_;
299 }
300
301
302 // Returns true if there was a complete line to input
303 // A line is of the form <key>:<value>
304 //   A line not of this form will not be passed
305 // The line read is split and stored in 'key' and 'value'
306 bool LyXDataSocket::readln(string & line)
307 {
308         int const charbuf_size = 100;
309         char charbuf[charbuf_size]; // buffer for the ::read() system call
310         int count;
311         string::size_type pos;
312
313         // read and store characters in buffer
314         while ((count = ::read(fd_, charbuf, charbuf_size - 1)) > 0) {
315                 charbuf[count] = '\0'; // turn it into a c string
316                 buffer += charbuf;
317         }
318
319         // Error conditions. The buffer must still be
320         // processed for lines read
321         if (count == 0) { // EOF -- connection closed
322                 connected_ = false;
323         } else if ((count == -1) && (errno != EAGAIN)) { // IO error
324                 cerr << "lyxclient: IO error." << endl;
325                 connected_ = false;
326         }
327
328         // Cut a line from buffer
329         if ((pos = buffer.find('\n')) == string::npos)
330                 return false; // No complete line stored
331         line = buffer.substr(0, pos);
332         buffer = buffer.substr(pos + 1);
333         return true;
334 }
335
336
337 // Write a line of the form <key>:<value> to the socket
338 void LyXDataSocket::writeln(string const & line)
339 {
340         string linen(line + '\n');
341         int size = linen.size();
342         int written = ::write(fd_, linen.c_str(), size);
343         if (written < size) { // Always mean end of connection.
344                 if ((written == -1) && (errno == EPIPE)) {
345                         // The program will also receive a SIGPIPE
346                         // that must be caught
347                         cerr << "lyxclient: connection closed while writing."
348                              << endl;
349                 } else {
350                         // Anything else, including errno == EAGAIN, must be
351                         // considered IO error. EAGAIN should never happen
352                         // when line is small
353                         cerr << "lyxclient: IO error: " << strerror(errno);
354                 }
355                 connected_ = false;
356         }
357 }
358
359
360 /////////////////////////////////////////////////////////////////////
361 //
362 // CmdLineParser
363 //
364 /////////////////////////////////////////////////////////////////////
365
366 class CmdLineParser {
367 public:
368         typedef int (*optfunc)(vector<docstring> const & args);
369         map<string, optfunc> helper;
370         map<string, bool> isset;
371         bool parse(int, char * []);
372         vector<char *> nonopt;
373 };
374
375
376 bool CmdLineParser::parse(int argc, char * argv[])
377 {
378         int opt = 1;
379         while (opt < argc) {
380                 vector<docstring> args;
381                 if (helper[argv[opt]]) {
382                         isset[argv[opt]] = true;
383                         int arg = opt + 1;
384                         while ((arg < argc) && (!helper[argv[arg]])) {
385                                 args.push_back(from_local8bit(argv[arg]));
386                                 ++arg;
387                         }
388                         int taken = helper[argv[opt]](args);
389                         if (taken == -1)
390                                 return false;
391                         opt += 1 + taken;
392                 } else {
393                         if (argv[opt][0] == '-') {
394                                 if ((argv[opt][1] == '-')
395                                    && (argv[opt][2]== '\0')) {
396                                         ++opt;
397                                         while (opt < argc) {
398                                                 nonopt.push_back(argv[opt]);
399                                                 ++opt;
400                                         }
401                                         return true;
402                                 } else {
403                                         cerr << "lyxclient: unknown option "
404                                              << argv[opt] << endl;
405                                         return false;
406                                 }
407                         }
408                         nonopt.push_back(argv[opt]);
409                         ++opt;
410                 }
411         }
412         return true;
413 }
414 // ~Class CmdLineParser -------------------------------------------------------
415
416
417
418 namespace cmdline {
419
420 docstring mainTmp(from_ascii("/tmp"));
421
422
423 class StopException : public exception
424 {
425 public:
426         StopException(int status) : status_(status) {}
427         int status() const { return status_; }
428 private:
429         int status_;
430 };
431
432
433 void usage()
434 {
435         cerr <<
436                 "Usage: lyxclient [options]\n"
437           "Options are:\n"
438           "  -a address    set address of the lyx socket\n"
439           "  -t directory  set system temporary directory (for detecting sockets)\n"
440           "  -p pid        select a running lyx by pidi\n"
441           "  -c command    send a single command and quit (LYXCMD prefix needed)\n"
442           "  -g file row   send a command to go to file and row\n"
443           "  -n name       set client name\n"
444           "  -h name       display this help end exit\n"
445           "If -a is not used, lyxclient will use the arguments of -t and -p to look for\n"
446           "a running lyx. If -t is not set, 'directory' defaults to the system directory. If -p is set,\n"
447           "lyxclient will connect only to a lyx with the specified pid. Options -c and -g\n"
448           "cannot be set simultaneoulsly. If no -c or -g options are given, lyxclient\n"
449           "will read commands from standard input and disconnect when command read is BYE:\n"
450           "\n"
451           "System directory is: " << to_utf8(cmdline::mainTmp)
452            << endl;
453 }
454
455
456 int h(vector<docstring> const &)
457 {
458         usage();
459         throw StopException(EXIT_SUCCESS);
460 }
461
462
463 docstring clientName =
464         from_ascii(to_string(::getppid()) + ">" + to_string(::getpid()));
465
466 int n(vector<docstring> const & arg)
467 {
468         if (arg.empty()) {
469                 cerr << "lyxclient: The option -n requires 1 argument."
470                      << endl;
471                 return -1;
472         }
473         clientName = arg[0];
474         return 1;
475 }
476
477
478 docstring singleCommand;
479
480
481 int c(vector<docstring> const & arg)
482 {
483         if (arg.empty()) {
484                 cerr << "lyxclient: The option -c requires 1 argument."
485                      << endl;
486                 return -1;
487         }
488         singleCommand = arg[0];
489         return 1;
490 }
491
492
493 int g(vector<docstring> const & arg)
494 {
495         if (arg.size() < 2) {
496                 cerr << "lyxclient: The option -g requires 2 arguments."
497                      << endl;
498                 return -1;
499         }
500         singleCommand = "LYXCMD:command-sequence "
501                 "server-goto-file-row "
502                         + arg[0] + ' '
503                         + arg[1] + "; " +
504                 "lyx-activate";
505         return 2;
506 }
507
508
509 // empty if LYXSOCKET is not set in the environment
510 docstring serverAddress;
511
512
513 int a(vector<docstring> const & arg)
514 {
515         if (arg.empty()) {
516                 cerr << "lyxclient: The option -a requires 1 argument."
517                      << endl;
518                 return -1;
519         }
520         // -a supersedes LYXSOCKET environment variable
521         serverAddress = arg[0];
522         return 1;
523 }
524
525
526
527
528 int t(vector<docstring> const & arg)
529 {
530         if (arg.empty()) {
531                 cerr << "lyxclient: The option -t requires 1 argument."
532                      << endl;
533                 return -1;
534         }
535         mainTmp = arg[0];
536         return 1;
537 }
538
539
540 string serverPid; // Init to empty string
541
542
543 int p(vector<docstring> const & arg)
544 {
545         if (arg.empty()) {
546                 cerr << "lyxclient: The option -p requires 1 argument."
547                      << endl;
548                 return -1;
549         }
550         serverPid = to_ascii(arg[0]);
551         return 1;
552 }
553
554
555 } // namespace cmdline
556
557 /// The main application class
558 class LyXClientApp : public ConsoleApplication
559 {
560 public:
561         LyXClientApp(int & argc, char * argv[])
562                 : ConsoleApplication("client" PROGRAM_SUFFIX, argc, argv),
563                   argc_(argc), argv_(argv)
564         {
565         }
566         void doExec()
567         {
568                 try {
569                         int const exit_status = run();
570                         exit(exit_status);
571                 }
572                 catch (cmdline::StopException & e) {
573                         exit(e.status());
574                 }
575         }
576 private:
577         int run();
578         int & argc_;
579         char ** argv_;
580 };
581
582
583 int LyXClientApp::run()
584 {
585         // qt changes this, and our numeric conversions require the C locale
586         setlocale(LC_NUMERIC, "C");
587
588         // Set defaults
589         char const * const lyxsocket = getenv("LYXSOCKET");
590         if (lyxsocket)
591                 cmdline::serverAddress = from_local8bit(lyxsocket);
592
593         // Default temporary
594         cmdline::mainTmp = FileName::tempPath().absoluteFilePath();
595
596         // Command line builder
597         CmdLineParser args;
598         args.helper["-h"] = cmdline::h;
599         args.helper["-c"] = cmdline::c;
600         args.helper["-g"] = cmdline::g;
601         args.helper["-n"] = cmdline::n;
602         args.helper["-a"] = cmdline::a;
603         args.helper["-t"] = cmdline::t;
604         args.helper["-p"] = cmdline::p;
605
606         // Command line failure conditions:
607         if ((!args.parse(argc_, argv_))
608            || (args.isset["-c"] && args.isset["-g"])
609            || (args.isset["-a"] && args.isset["-p"])) {
610                 cmdline::usage();
611                 return EXIT_FAILURE;
612         }
613
614         unique_ptr<LyXDataSocket> server;
615
616         if (!cmdline::serverAddress.empty()) {
617                 server.reset(new LyXDataSocket(FileName(to_utf8(cmdline::serverAddress))));
618                 if (!server->connected()) {
619                         cerr << "lyxclient: " << "Could not connect to "
620                              << to_utf8(cmdline::serverAddress) << endl;
621                         return EXIT_FAILURE;
622                 }
623         } else {
624                 // We have to look for an address.
625                 // serverPid can be empty.
626                 FileNameList addrs = lyxSockets(to_filesystem8bit(cmdline::mainTmp), cmdline::serverPid);
627                 FileNameList::const_iterator addr = addrs.begin();
628                 FileNameList::const_iterator end = addrs.end();
629                 for (; addr != end; ++addr) {
630                         // Caution: addr->string() is in filesystem encoding
631                         server.reset(new LyXDataSocket(*addr));
632                         if (server->connected())
633                                 break;
634                         lyxerr << "lyxclient: " << "Could not connect to "
635                              << addr->absFileName() << endl;
636                 }
637                 if (addr == end) {
638                         lyxerr << "lyxclient: No suitable server found."
639                                << endl;
640                         return EXIT_FAILURE;
641                 }
642                 cerr << "lyxclient: " << "Connected to " << addr->absFileName() << endl;
643         }
644
645         int const serverfd = server->fd();
646
647         IOWatch iowatch;
648         iowatch.addfd(serverfd);
649
650         // Used to read from server
651         string answer;
652
653         // Send greeting
654         server->writeln("HELLO:" + to_utf8(cmdline::clientName));
655         // wait at most 2 seconds until server responds
656         iowatch.wait(2.0);
657         if (iowatch.isset(serverfd) && server->readln(answer)) {
658                 if (prefixIs(answer, "BYE:")) {
659                         cerr << "lyxclient: Server disconnected." << endl;
660                         cout << answer << endl;
661                         return EXIT_FAILURE;
662                 }
663         } else {
664                 cerr << "lyxclient: No answer from server." << endl;
665                 return EXIT_FAILURE;
666         }
667
668         if (args.isset["-g"] || args.isset["-c"]) {
669                 server->writeln(to_utf8(cmdline::singleCommand));
670                 iowatch.wait(2.0);
671                 if (iowatch.isset(serverfd) && server->readln(answer)) {
672                         cout << answer;
673                         if (prefixIs(answer, "ERROR:"))
674                                 return EXIT_FAILURE;
675                         return EXIT_SUCCESS;
676                 } else {
677                         cerr << "lyxclient: No answer from server." << endl;
678                         return EXIT_FAILURE;
679                 }
680         }
681
682         // Take commands from stdin
683         iowatch.addfd(0); // stdin
684         bool saidbye = false;
685         while ((!saidbye) && server->connected()) {
686                 iowatch.wait();
687                 if (iowatch.isset(0)) {
688                         string command;
689                         getline(cin, command);
690                         if (command.empty())
691                                 continue;
692                         if (command == "BYE:") {
693                                 server->writeln("BYE:");
694                                 saidbye = true;
695                         } else {
696                                 server->writeln("LYXCMD:" + command);
697                         }
698                 }
699                 if (iowatch.isset(serverfd)) {
700                         while(server->readln(answer))
701                                 cout << answer << endl;
702                 }
703         }
704
705         return EXIT_SUCCESS;
706 }
707
708 } // namespace lyx
709
710
711 int main(int argc, char * argv[])
712 {
713         lyx::lyxerr.setStream(cerr);
714
715         lyx::LyXClientApp app(argc, argv);
716         return app.exec();
717 }
718