* This file is part of LyX, the document processor.
* Licence details can be found in the file COPYING.
*
- * \author João Luis M. Assirati
- * \author Lars Gullik Bjønnes
+ * \author João Luis M. Assirati
+ * \author Lars Gullik Bjønnes
*
* Full author contact details are available in file CREDITS.
*/
#include <config.h>
-#include "debug.h"
+#include "LyXRC.h"
+
+#include "support/ConsoleApplication.h"
+#include "support/debug.h"
#include "support/FileName.h"
-#include "support/unicode.h"
+#include "support/FileNameList.h"
#include "support/lstrings.h"
-
-#include <boost/filesystem/operations.hpp>
-#include <boost/lexical_cast.hpp>
-#include <boost/scoped_ptr.hpp>
+#include "support/Messages.h"
+#include "support/unicode.h"
+#include "support/unique_ptr.h"
// getpid(), getppid()
#ifdef HAVE_SYS_TYPES_H
// fcntl()
#include <fcntl.h>
+// strerror()
+#include <string.h>
+
#include <cerrno>
+#include <cstdio>
#include <cstdlib>
+#include <exception>
#include <string>
#include <vector>
#include <map>
#include <iostream>
+using namespace std;
+using namespace lyx::support;
+
namespace lyx {
-using support::FileName;
-using support::prefixIs;
+// Required global variables
+bool verbose = false;
+LyXRC lyxrc;
-using boost::scoped_ptr;
-namespace fs = boost::filesystem;
+// Keep the linker happy on Windows
+void lyx_exit(int)
+{}
-using std::string;
-using std::vector;
-using std::cout;
-using std::cerr;
-using std::cin;
-using std::endl;
+// Dummy language support
+Messages const & getGuiMessages()
+{
+ static Messages lyx_messages;
+
+ return lyx_messages;
+}
+
+
+Messages const & getMessages(string const &)
+{
+ return getGuiMessages();
+}
namespace support {
string itoa(unsigned int i)
{
- return boost::lexical_cast<string>(i);
+ char buf[20];
+ sprintf(buf, "%d", i);
+ return buf;
}
/// Returns the absolute pathnames of all lyx local sockets in
/// file system encoding.
/// Parts stolen from lyx::support::DirList().
-vector<fs::path> lyxSockets(string const & dir, string const & pid)
+FileNameList lyxSockets(string const & dir, string const & pid)
{
- vector<fs::path> dirlist;
+ FileNameList dirlist;
- fs::path dirpath(dir);
+ FileName dirpath(dir + "/");
- if (!fs::exists(dirpath) || !fs::is_directory(dirpath)) {
+ if (!dirpath.exists() || !dirpath.isDirectory()) {
lyxerr << dir << " does not exist or is not a directory."
<< endl;
return dirlist;
}
- fs::directory_iterator beg((fs::path(dir)));
- fs::directory_iterator end;
+ FileNameList dirs = dirpath.dirList("");
+ FileNameList::const_iterator it = dirs.begin();
+ FileNameList::const_iterator end = dirs.end();
- for (; beg != end; ++beg) {
- if (prefixIs(beg->leaf(), "lyx_tmpdir" + pid)) {
- fs::path lyxsocket = beg->path() / "lyxsocket";
- if (fs::exists(lyxsocket)) {
- dirlist.push_back(lyxsocket);
- }
- }
+ for (; it != end; ++it) {
+ if (!it->isDirectory())
+ continue;
+ string const tmpdir = it->absFileName();
+ if (!contains(tmpdir, "lyx_tmpdir" + pid))
+ continue;
+
+ FileName lyxsocket(tmpdir + "/lyxsocket");
+ if (lyxsocket.exists())
+ dirlist.push_back(lyxsocket);
}
return dirlist;
if (::connect(fd,
reinterpret_cast<struct sockaddr *>(&addr),
sizeof(addr)) == -1) {
- cerr << "lyxclient: Could not connect to socket " << name.absFilename()
+ cerr << "lyxclient: Could not connect to socket " << name.absFileName()
<< ": " << strerror(errno) << endl;
::close(fd);
return -1;
-// Class IOWatch ------------------------------------------------------------
+/////////////////////////////////////////////////////////////////////
+//
+// IOWatch
+//
+/////////////////////////////////////////////////////////////////////
+
class IOWatch {
public:
IOWatch();
}
-bool IOWatch::isset(int fd) {
+bool IOWatch::isset(int fd)
+{
return FD_ISSET(fd, &act);
}
-// ~Class IOWatch ------------------------------------------------------------
-// Class LyXDataSocket -------------------------------------------------------
+
+/////////////////////////////////////////////////////////////////////
+//
+// LyXDataSocket
+//
+/////////////////////////////////////////////////////////////////////
+
// Modified LyXDataSocket class for use with the client
class LyXDataSocket {
public:
LyXDataSocket::LyXDataSocket(FileName const & address)
{
- if ((fd_ = support::socktools::connect(address)) == -1) {
+ if ((fd_ = socktools::connect(address)) == -1) {
connected_ = false;
} else {
connected_ = true;
// Returns true if there was a complete line to input
// A line is of the form <key>:<value>
// A line not of this form will not be passed
-// The line read is splitted and stored in 'key' and 'value'
+// The line read is split and stored in 'key' and 'value'
bool LyXDataSocket::readln(string & line)
{
int const charbuf_size = 100;
connected_ = false;
}
}
-// ~Class LyXDataSocket -------------------------------------------------------
-// Class CmdLineParser -------------------------------------------------------
+/////////////////////////////////////////////////////////////////////
+//
+// CmdLineParser
+//
+/////////////////////////////////////////////////////////////////////
+
class CmdLineParser {
public:
typedef int (*optfunc)(vector<docstring> const & args);
- std::map<string, optfunc> helper;
- std::map<string, bool> isset;
+ map<string, optfunc> helper;
+ map<string, bool> isset;
bool parse(int, char * []);
vector<char *> nonopt;
};
namespace cmdline {
+docstring mainTmp(from_ascii("/tmp"));
+
+
+class StopException : public exception
+{
+public:
+ StopException(int status) : status_(status) {}
+ int status() const { return status_; }
+private:
+ int status_;
+};
+
+
void usage()
{
- cerr << "Usage: lyxclient [options]" << endl
- << "Options are:" << endl
- << " -a address set address of the lyx socket" << endl
- << " -t directory set system temporary directory" << endl
- << " -p pid select a running lyx by pid" << endl
- << " -c command send a single command and quit" << endl
- << " -g file row send a command to go to file and row" << endl
- << " -n name set client name" << endl
- << " -h name display this help end exit" << endl
- << "If -a is not used, lyxclient will use the arguments of -t and -p to look for" << endl
- << "a running lyx. If -t is not set, 'directory' defaults to /tmp. If -p is set," << endl
- << "lyxclient will connect only to a lyx with the specified pid. Options -c and -g" << endl
- << "cannot be set simultaneoulsly. If no -c or -g options are given, lyxclient" << endl
- << "will read commands from standard input and disconnect when command read is BYE:"
- << endl;
+ cerr <<
+ "Usage: lyxclient [options]\n"
+ "Options are:\n"
+ " -a address set address of the lyx socket\n"
+ " -t directory set system temporary directory (for detecting sockets)\n"
+ " -p pid select a running lyx by pidi\n"
+ " -c command send a single command and quit (LYXCMD prefix needed)\n"
+ " -g file row send a command to go to file and row\n"
+ " -n name set client name\n"
+ " -h name display this help end exit\n"
+ "If -a is not used, lyxclient will use the arguments of -t and -p to look for\n"
+ "a running lyx. If -t is not set, 'directory' defaults to the system directory. If -p is set,\n"
+ "lyxclient will connect only to a lyx with the specified pid. Options -c and -g\n"
+ "cannot be set simultaneoulsly. If no -c or -g options are given, lyxclient\n"
+ "will read commands from standard input and disconnect when command read is BYE:\n"
+ "\n"
+ "System directory is: " << to_utf8(cmdline::mainTmp)
+ << endl;
}
int h(vector<docstring> const &)
{
usage();
- exit(0);
+ throw StopException(EXIT_SUCCESS);
}
-docstring clientName(from_ascii(support::itoa(::getppid()) + ">" + support::itoa(::getpid())));
+docstring clientName =
+ from_ascii(itoa(::getppid()) + ">" + itoa(::getpid()));
int n(vector<docstring> const & arg)
{
- if (arg.size() < 1) {
+ if (arg.empty()) {
cerr << "lyxclient: The option -n requires 1 argument."
<< endl;
return -1;
int c(vector<docstring> const & arg)
{
- if (arg.size() < 1) {
+ if (arg.empty()) {
cerr << "lyxclient: The option -c requires 1 argument."
<< endl;
return -1;
<< endl;
return -1;
}
- singleCommand = "LYXCMD:server-goto-file-row "
- + arg[0] + ' '
- + arg[1];
+ singleCommand = "LYXCMD:command-sequence "
+ "server-goto-file-row "
+ + arg[0] + ' '
+ + arg[1] + "; " +
+ "lyx-activate";
return 2;
}
int a(vector<docstring> const & arg)
{
- if (arg.size() < 1) {
+ if (arg.empty()) {
cerr << "lyxclient: The option -a requires 1 argument."
<< endl;
return -1;
}
-docstring mainTmp(from_ascii("/tmp"));
int t(vector<docstring> const & arg)
{
- if (arg.size() < 1) {
+ if (arg.empty()) {
cerr << "lyxclient: The option -t requires 1 argument."
<< endl;
return -1;
int p(vector<docstring> const & arg)
{
- if (arg.size() < 1) {
+ if (arg.empty()) {
cerr << "lyxclient: The option -p requires 1 argument."
<< endl;
return -1;
} // namespace cmdline
-} // namespace lyx
+/// The main application class
+class LyXClientApp : public ConsoleApplication
+{
+public:
+ LyXClientApp(int & argc, char * argv[])
+ : ConsoleApplication("client" PROGRAM_SUFFIX, argc, argv),
+ argc_(argc), argv_(argv)
+ {
+ }
+ void doExec()
+ {
+ try {
+ int const exit_status = run();
+ exit(exit_status);
+ }
+ catch (cmdline::StopException & e) {
+ exit(e.status());
+ }
+ }
+private:
+ int run();
+ int & argc_;
+ char ** argv_;
+};
-int main(int argc, char * argv[])
+int LyXClientApp::run()
{
- using namespace lyx;
- lyxerr.rdbuf(cerr.rdbuf());
+ // qt changes this, and our numeric conversions require the C locale
+ setlocale(LC_NUMERIC, "C");
+ // Set defaults
char const * const lyxsocket = getenv("LYXSOCKET");
if (lyxsocket)
cmdline::serverAddress = from_local8bit(lyxsocket);
+ // Default temporary
+ cmdline::mainTmp = FileName::tempPath().absoluteFilePath();
+
+ // Command line builder
CmdLineParser args;
args.helper["-h"] = cmdline::h;
args.helper["-c"] = cmdline::c;
args.helper["-p"] = cmdline::p;
// Command line failure conditions:
- if ((!args.parse(argc, argv))
+ if ((!args.parse(argc_, argv_))
|| (args.isset["-c"] && args.isset["-g"])
|| (args.isset["-a"] && args.isset["-p"])) {
cmdline::usage();
- return 1;
+ return EXIT_FAILURE;
}
- scoped_ptr<LyXDataSocket> server;
+ unique_ptr<LyXDataSocket> server;
if (!cmdline::serverAddress.empty()) {
server.reset(new LyXDataSocket(FileName(to_utf8(cmdline::serverAddress))));
} else {
// We have to look for an address.
// serverPid can be empty.
- vector<fs::path> addrs = support::lyxSockets(to_filesystem8bit(cmdline::mainTmp), cmdline::serverPid);
- vector<fs::path>::const_iterator addr = addrs.begin();
- vector<fs::path>::const_iterator end = addrs.end();
+ FileNameList addrs = lyxSockets(to_filesystem8bit(cmdline::mainTmp), cmdline::serverPid);
+ FileNameList::const_iterator addr = addrs.begin();
+ FileNameList::const_iterator end = addrs.end();
for (; addr != end; ++addr) {
// Caution: addr->string() is in filesystem encoding
- server.reset(new LyXDataSocket(FileName(to_utf8(from_filesystem8bit(addr->string())))));
+ server.reset(new LyXDataSocket(*addr));
if (server->connected())
break;
lyxerr << "lyxclient: " << "Could not connect to "
- << addr->string() << endl;
+ << addr->absFileName() << endl;
}
if (addr == end) {
lyxerr << "lyxclient: No suitable server found."
<< endl;
return EXIT_FAILURE;
}
- cerr << "lyxclient: " << "Connected to " << addr->string() << endl;
+ cerr << "lyxclient: " << "Connected to " << addr->absFileName() << endl;
}
int const serverfd = server->fd();
iowatch.wait();
if (iowatch.isset(0)) {
string command;
- cin >> command;
+ getline(cin, command);
+ if (command.empty())
+ continue;
if (command == "BYE:") {
server->writeln("BYE:");
saidbye = true;
return EXIT_SUCCESS;
}
+} // namespace lyx
+
+
+int main(int argc, char * argv[])
+{
+ lyx::lyxerr.setStream(cerr);
+
+ lyx::LyXClientApp app(argc, argv);
+ return app.exec();
+}
+
namespace boost {
}
} // namespace boost
-