From cf40817160ec196759dfc51a86a4c0b38d9a43ce Mon Sep 17 00:00:00 2001 From: =?utf8?q?Lars=20Gullik=20Bj=C3=B8nnes?= Date: Sat, 4 Sep 2004 12:13:50 +0000 Subject: [PATCH] lyxclient moved from development and cleaned up a bit + some assorted fixes. git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@8967 a592a061-630c-0410-9148-cb99ea01b6c8 --- ChangeLog | 4 + configure.ac | 1 + po/POTFILES.in | 2 + src/ChangeLog | 7 + src/Makefile.am | 6 +- src/client/.cvsignore | 6 + src/client/Makefile.am | 29 ++ src/client/boost.C | 43 +++ src/client/client.C | 596 +++++++++++++++++++++++++++++++++++++++ src/client/debug.C | 106 +++++++ src/client/debug.h | 70 +++++ src/client/gettext.C | 61 ++++ src/client/gettext.h | 64 +++++ src/client/lyxclient.man | 83 ++++++ src/client/messages.C | 147 ++++++++++ src/client/messages.h | 33 +++ src/support/ChangeLog | 4 + src/support/tostr.C | 27 +- 18 files changed, 1269 insertions(+), 20 deletions(-) create mode 100644 src/client/.cvsignore create mode 100644 src/client/Makefile.am create mode 100644 src/client/boost.C create mode 100644 src/client/client.C create mode 100644 src/client/debug.C create mode 100644 src/client/debug.h create mode 100644 src/client/gettext.C create mode 100644 src/client/gettext.h create mode 100644 src/client/lyxclient.man create mode 100644 src/client/messages.C create mode 100644 src/client/messages.h diff --git a/ChangeLog b/ChangeLog index a26b7b0faf..afc8039472 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2004-09-04 Lars Gullik Bjonnes + + * configure.ac (AC_CONFIG_FILES): add src/client/Makefile + 2004-09-03 Georg Baum * configure.ac: print error message if uic or moc is not found diff --git a/configure.ac b/configure.ac index d37ee87019..4d578cadd4 100644 --- a/configure.ac +++ b/configure.ac @@ -413,6 +413,7 @@ AC_CONFIG_FILES([Makefile m4/Makefile \ po/Makefile.in \ sourcedoc/Doxyfile \ sourcedoc/Makefile \ + src/client/Makefile \ src/Makefile \ src/version.C-tmp:src/version.C.in \ src/tex2lyx/Makefile \ diff --git a/po/POTFILES.in b/po/POTFILES.in index 225ff4f4b4..c6375970dc 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -10,6 +10,8 @@ src/buffer.C src/buffer_funcs.C src/bufferlist.C src/bufferparams.C +src/client/debug.C +src/client/gettext.h src/converter.C src/debug.C src/exporter.C diff --git a/src/ChangeLog b/src/ChangeLog index 060df91447..ed860f8eb7 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,10 @@ +2004-09-04 Lars Gullik Bjonnes + + * client: new dir + + * Makefile.am (SUBDIRS): change order of subdirs and add client dir + (BOOST_LIBS): use top_buildir when looking for the file + 2004-08-30 Lars Gullik Bjonnes * pch.h: do not use include boost/format.hpp, multiple symbols diff --git a/src/Makefile.am b/src/Makefile.am index 3222ac92cf..815b94f370 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,7 +4,7 @@ DISTCLEANFILES += config.h libintl.h version.C stamp-version version.C-tmp MAINTAINERCLEANFILES += $(srcdir)/config.h.in -SUBDIRS = mathed insets graphics support frontends tex2lyx +SUBDIRS = mathed insets graphics support frontends . client tex2lyx EXTRA_DIST = config.h.in stamp-h.in version.C.in \ Sectioning.h \ @@ -21,8 +21,8 @@ LYX_POST_LIBS = frontends/controllers/libcontrollers.la \ support/libsupport.la if USE_INCLUDED_BOOST -BOOST_LIBS = ../boost/libs/regex/src/libboostregex.la \ - ../boost/libs/signals/src/libboostsignals.la +BOOST_LIBS = $(top_builddir)/boost/libs/regex/src/libboostregex.la \ + $(top_builddir)/boost/libs/signals/src/libboostsignals.la else BOOST_LIBS = -lboost_regex -lboost_signals endif diff --git a/src/client/.cvsignore b/src/client/.cvsignore new file mode 100644 index 0000000000..4a2dc2842b --- /dev/null +++ b/src/client/.cvsignore @@ -0,0 +1,6 @@ +Makefile.in +Makefile +lyxclient +lyxclient.1 +.deps +.libs diff --git a/src/client/Makefile.am b/src/client/Makefile.am new file mode 100644 index 0000000000..57096f9dd3 --- /dev/null +++ b/src/client/Makefile.am @@ -0,0 +1,29 @@ +include $(top_srcdir)/config/common.am + +EXTRA_DIST = lyxclient.man + +man_MANS = lyxclient.1 + +bin_PROGRAMS = lyxclient + +INCLUDES = $(BOOST_INCLUDES) + +AM_CXXFLAGS = $(PCH_FLAGS) + +lyxclient_LDADD = \ + $(top_builddir)/src/support/libsupport.la \ + $(top_builddir)/boost/libs/filesystem/src/libboostfilesystem.la \ + $(top_builddir)/boost/libs/regex/src/libboostregex.la + +lyxclient_SOURCES = \ + boost.C \ + client.C \ + debug.C \ + debug.h \ + gettext.C \ + gettext.h \ + messages.C \ + messages.h + +lyxclient.1: + cp -p $(srcdir)/lyxclient.man lyxclient.1 diff --git a/src/client/boost.C b/src/client/boost.C new file mode 100644 index 0000000000..6a1274d389 --- /dev/null +++ b/src/client/boost.C @@ -0,0 +1,43 @@ +/** + * \file boost.C + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Lars Gullik Bjønnes + * + * Full author contact details are available in file CREDITS. + */ + +#include + +#include "lyx_main.h" +#include "debug.h" +#include "support/lyxlib.h" + +#include + +#include + +using std::endl; + +namespace boost { + +void throw_exception(std::exception const & e) +{ + lyxerr << "Exception caught:\n" + << e.what() << endl; + BOOST_ASSERT(false); +} + + +void assertion_failed(char const * expr, char const * function, + char const * file, long line) +{ + lyxerr << "Assertion triggered in " << function + << " by failing check \"" << expr << "\"" + << " in file " << file << ":" << line << endl; + lyx::support::abort(); +} + + +} diff --git a/src/client/client.C b/src/client/client.C new file mode 100644 index 0000000000..56cde72886 --- /dev/null +++ b/src/client/client.C @@ -0,0 +1,596 @@ +/** + * \file client.C + * 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 + * + * Full author contact details are available in file CREDITS. + */ + + +#include + +#include "debug.h" +#include "support/lstrings.h" + +#include +#include +#include + +// getpid(), getppid() +#include +#include + +// select() +#include + +// socket(), connect() +#include +#include + +// fcntl() +#include + +#include +#include +#include +#include +#include + +using lyx::support::prefixIs; + +using boost::scoped_ptr; +namespace fs = boost::filesystem; + +using std::string; +using std::vector; +using std::cout; +using std::cerr; +using std::cin; +using std::endl; + + +namespace support { + +string itoa(unsigned int i) +{ + return boost::lexical_cast(i); +} + + +// Parts stolen from lyx::support::DirList() +// Returns the absolute pathnames of all lyx local sockets +vector lyxSockets(string const & dir, string const & pid) +{ + vector dirlist; + + fs::path dirpath(dir); + + if (!fs::exists(dirpath) || !fs::is_directory(dirpath)) { + lyxerr << dir << " does not exist or is not a directory." + << endl; + return dirlist; + } + + fs::directory_iterator beg((fs::path(dir))); + fs::directory_iterator end; + + for (; beg != end; ++beg) { + if (prefixIs(beg->leaf(), "lyx_tmpdir" + pid)) { + fs::path lyxsocket = *beg / "lyxsocket"; + if (fs::exists(lyxsocket)) { + dirlist.push_back(lyxsocket); + } + } + } + + return dirlist; +} + + +namespace socktools { + + +int connect(string const & name) +{ + int fd; // File descriptor for the socket + sockaddr_un addr; // Structure that hold the socket address + + // char sun_path[108] + string::size_type len = name.size(); + if (len > 107) { + cerr << "lyxclient: Socket address '" << name + << "' too long." << endl; + return -1; + } + // Synonims for AF_UNIX are AF_LOCAL and AF_FILE + addr.sun_family = AF_UNIX; + name.copy(addr.sun_path, 107); + addr.sun_path[len] = '\0'; + + if ((fd = ::socket(PF_UNIX, SOCK_STREAM, 0))== -1) { + cerr << "lyxclient: Could not create socket descriptor: " + << strerror(errno) << endl; + return -1; + } + if (::connect(fd, (struct sockaddr *)&addr, SUN_LEN(&addr)) == -1) { + cerr << "lyxclient: Could not connect to socket " << name + << ": " << strerror(errno) << endl; + ::close(fd); + return -1; + } + if (::fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { + cerr << "lyxclient: Could not set O_NONBLOCK for socket: " + << strerror(errno) << endl; + ::close(fd); + return -1; + } + return fd; +} + + +} // namespace socktools +} // namespace support + + + +// Class IOWatch ------------------------------------------------------------ +class IOWatch { +public: + IOWatch(); + void clear(); + void addfd(int); + bool wait(double); + bool wait(); + bool isset(int fd); +private: + fd_set des; + fd_set act; +}; + + +IOWatch::IOWatch() +{ + clear(); +} + + +void IOWatch::clear() +{ + FD_ZERO(&des); +} + + +void IOWatch::addfd(int fd) +{ + FD_SET(fd, &des); +} + + +bool IOWatch::wait(double timeout) +{ + timeval to; + to.tv_sec = static_cast(timeout); + to.tv_usec = static_cast((timeout - to.tv_sec)*1E6); + act = des; + return select(FD_SETSIZE, &act, + (fd_set *)0, (fd_set *)0, &to); +} + + +bool IOWatch::wait() +{ + act = des; + return select(FD_SETSIZE, &act, + (fd_set *)0, (fd_set *)0, (timeval *)0); +} + + +bool IOWatch::isset(int fd) { + return FD_ISSET(fd, &act); +} +// ~Class IOWatch ------------------------------------------------------------ + + +// Class LyXDataSocket ------------------------------------------------------- +// Modified LyXDataSocket class for use with the client +class LyXDataSocket { +public: + LyXDataSocket(string const &); + ~LyXDataSocket(); + // File descriptor of the connection + int fd() const; + // Connection status + bool connected() const; + // Line buffered input from the socket + bool readln(string &); + // Write the string + '\n' to the socket + void writeln(string const &); +private: + // File descriptor for the data socket + int fd_; + // True if the connection is up + bool connected_; + // buffer for input data + string buffer; +}; + + +LyXDataSocket::LyXDataSocket(string const & address) +{ + if ((fd_ = support::socktools::connect(address)) == -1) { + connected_ = false; + } else { + connected_ = true; + } +} + + +LyXDataSocket::~LyXDataSocket() +{ + ::close(fd_); +} + + +int LyXDataSocket::fd() const +{ + return fd_; +} + + +bool LyXDataSocket::connected() const +{ + return connected_; +} + + +// Returns true if there was a complete line to input +// A line is of the form : +// A line not of this form will not be passed +// The line read is splitted and stored in 'key' and 'value' +bool LyXDataSocket::readln(string & line) +{ + int const charbuf_size = 100; + char charbuf[charbuf_size]; // buffer for the ::read() system call + int count; + string::size_type pos; + + // read and store characters in buffer + while ((count = ::read(fd_, charbuf, charbuf_size - 1)) > 0) { + charbuf[count] = '\0'; // turn it into a c string + buffer += charbuf; + } + + // Error conditions. The buffer must still be + // processed for lines read + if (count == 0) { // EOF -- connection closed + connected_ = false; + } else if ((count == -1) && (errno != EAGAIN)) { // IO error + cerr << "lyxclient: IO error." << endl; + connected_ = false; + } + + // Cut a line from buffer + if ((pos = buffer.find('\n')) == string::npos) + return false; // No complete line stored + line = buffer.substr(0, pos); + buffer = buffer.substr(pos + 1); + return true; +} + + +// Write a line of the form : to the socket +void LyXDataSocket::writeln(string const & line) +{ + string linen(line + '\n'); + int size = linen.size(); + int written = ::write(fd_, linen.c_str(), size); + if (written < size) { // Allways mean end of connection. + if ((written == -1) && (errno == EPIPE)) { + // The program will also receive a SIGPIPE + // that must be catched + cerr << "lyxclient: connection closed while writing." + << endl; + } else { + // Anything else, including errno == EAGAIN, must be + // considered IO error. EAGAIN should never happen + // when line is small + cerr << "lyxclient: IO error: " << strerror(errno); + } + connected_ = false; + } +} +// ~Class LyXDataSocket ------------------------------------------------------- + + +// Class CmdLineParser ------------------------------------------------------- +class CmdLineParser { +public: + typedef int (*optfunc)(vector const & args); + std::map helper; + std::map isset; + bool parse(int, char * []); + vector nonopt; +}; + + +bool CmdLineParser::parse(int argc, char * argv[]) +{ + int opt = 1; + while (opt < argc) { + vector args; + if (helper[argv[opt]]) { + isset[argv[opt]] = true; + int arg = opt + 1; + while ((arg < argc) && (!helper[argv[arg]])) { + args.push_back(argv[arg]); + ++arg; + } + int taken = helper[argv[opt]](args); + if (taken == -1) + return false; + opt += 1 + taken; + } else { + if (argv[opt][0] == '-') { + if ((argv[opt][1] == '-') + && (argv[opt][2]== '\0')) { + ++opt; + while (opt < argc) { + nonopt.push_back(argv[opt]); + ++opt; + } + return true; + } else { + cerr << "lyxclient: unknown option " + << argv[opt] << endl; + return false; + } + } + nonopt.push_back(argv[opt]); + ++opt; + } + } + return true; +} +// ~Class CmdLineParser ------------------------------------------------------- + + + +namespace cmdline { + +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; +} + + +int h(vector const &) +{ + usage(); + exit(0); +} + + +string clientName(support::itoa(::getppid()) + ">" + support::itoa(::getpid())); + +int n(vector const & arg) +{ + if (arg.size() < 1) { + cerr << "lyxclient: The option -n requires 1 argument." + << endl; + return -1; + } + clientName = arg[0]; + return 1; +} + + +string singleCommand; + + +int c(vector const & arg) +{ + if (arg.size() < 1) { + cerr << "lyxclient: The option -c requires 1 argument." + << endl; + return -1; + } + singleCommand = arg[0]; + return 1; +} + + +int g(vector const & arg) +{ + if (arg.size() < 2) { + cerr << "lyxclient: The option -g requires 2 arguments." + << endl; + return -1; + } + singleCommand = "LYXCMD:server-goto-file-row " + + string(arg[0]) + ' ' + + string(arg[1]); + return 2; +} + + +// 0 if LYXSOCKET is not set in the environment +char * serverAddress = getenv("LYXSOCKET"); + + +int a(vector const & arg) +{ + if (arg.size() < 1) { + cerr << "lyxclient: The option -a requires 1 argument." + << endl; + return -1; + } + // -a supercedes LYXSOCKET environment variable + serverAddress = arg[0]; + return 1; +} + + +string mainTmp("/tmp"); + + +int t(vector const & arg) +{ + if (arg.size() < 1) { + cerr << "lyxclient: The option -t requires 1 argument." + << endl; + return -1; + } + mainTmp = arg[0]; + return 1; +} + + +string serverPid; // Init to empty string + + +int p(vector const & arg) +{ + if (arg.size() < 1) { + cerr << "lyxclient: The option -p requires 1 argument." + << endl; + return -1; + } + serverPid = arg[0]; + return 1; +} + + +} // namespace cmdline + + + + + +int main(int argc, char * argv[]) +{ + lyxerr.rdbuf(cerr.rdbuf()); + + CmdLineParser args; + args.helper["-h"] = cmdline::h; + args.helper["-c"] = cmdline::c; + args.helper["-g"] = cmdline::g; + args.helper["-n"] = cmdline::n; + args.helper["-a"] = cmdline::a; + args.helper["-t"] = cmdline::t; + args.helper["-p"] = cmdline::p; + + // Command line failure conditions: + if ((!args.parse(argc, argv)) + || (args.isset["-c"] && args.isset["-g"]) + || (args.isset["-a"] && args.isset["-p"])) { + cmdline::usage(); + return 1; + } + + scoped_ptr server; + + if (cmdline::serverAddress) { + server.reset(new LyXDataSocket(cmdline::serverAddress)); + if (!server->connected()) { + cerr << "lyxclient: " << "Could not connect to " + << cmdline::serverAddress << endl; + return EXIT_FAILURE; + } + } else { + // We have to look for an address. + // serverPid can be empty. + vector addrs = support::lyxSockets(cmdline::mainTmp, cmdline::serverPid); + vector::const_iterator addr = addrs.begin(); + vector::const_iterator end = addrs.end(); + for (; addr != end; ++addr) { + server.reset(new LyXDataSocket(addr->string())); + if (server->connected()) + break; + lyxerr << "lyxclient: " << "Could not connect to " + << addr->string() << endl; + } + if (addr == end) { + lyxerr << "lyxclient: No suitable server found." + << endl; + return EXIT_FAILURE; + } + cerr << "lyxclient: " << "Connected to " << addr->string() << endl; + } + + int const serverfd = server->fd(); + + IOWatch iowatch; + iowatch.addfd(serverfd); + + // Used to read from server + string answer; + + // Send greeting + server->writeln("HELLO:" + cmdline::clientName); + // wait at most 2 seconds until server responds + iowatch.wait(2.0); + if (iowatch.isset(serverfd) && server->readln(answer)) { + if (prefixIs(answer, "BYE:")) { + cerr << "lyxclient: Server disconnected." << endl; + cout << answer << endl; + return EXIT_FAILURE; + } + } else { + cerr << "lyxclient: No answer from server." << endl; + return EXIT_FAILURE; + } + + if (args.isset["-g"] || args.isset["-c"]) { + server->writeln(cmdline::singleCommand); + iowatch.wait(2.0); + if (iowatch.isset(serverfd) && server->readln(answer)) { + cout << answer; + if (prefixIs(answer, "ERROR:")) + return EXIT_FAILURE; + return EXIT_SUCCESS; + } else { + cerr << "lyxclient: No answer from server." << endl; + return EXIT_FAILURE; + } + } + + // Take commands from stdin + iowatch.addfd(0); // stdin + bool saidbye = false; + while ((!saidbye) && server->connected()) { + iowatch.wait(); + if (iowatch.isset(0)) { + string command; + cin >> command; + if (command == "BYE:") { + server->writeln("BYE:"); + saidbye = true; + } else { + server->writeln("LYXCMD:" + command); + } + } + if (iowatch.isset(serverfd)) { + while(server->readln(answer)) + cout << answer << endl; + } + } + + return EXIT_SUCCESS; +} diff --git a/src/client/debug.C b/src/client/debug.C new file mode 100644 index 0000000000..5da41b9f67 --- /dev/null +++ b/src/client/debug.C @@ -0,0 +1,106 @@ +/** + * \file debug.C + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Lars Gullik Bjønnes + * \author Jean-Marc Lasgouttes + * + * Full author contact details are available in file CREDITS. + */ + +#include + +#include "debug.h" +#include "gettext.h" + +#include "support/lstrings.h" + +#include +#include + +using lyx::support::ascii_lowercase; +using lyx::support::bformat; +using lyx::support::isStrInt; +using lyx::support::strToInt; + +using std::setw; +using std::string; +using std::ostream; + +namespace { + +struct error_item { + Debug::type level; + char const * name; + char const * desc; +}; + + +error_item errorTags[] = { + { Debug::NONE, "none", N_("No debugging message")}, + { Debug::INFO, "info", N_("General information")}, + { Debug::DEBUG, "debug", N_("Developers general debug messages")}, + { Debug::ANY, "any", N_("All debugging messages")} +}; + + +int const numErrorTags = sizeof(errorTags)/sizeof(error_item); + +} // namespace anon + + +lyx_debug_trait::type lyx_debug_trait::value(string const & val) +{ + type l = Debug::NONE; + string v(val); + while (!v.empty()) { + string::size_type st = v.find(','); + string tmp(ascii_lowercase(v.substr(0, st))); + if (tmp.empty()) + break; + // Is it a number? + if (isStrInt(tmp)) + l |= static_cast(strToInt(tmp)); + else + // Search for an explicit name + for (int i = 0 ; i < numErrorTags ; ++i) + if (tmp == errorTags[i].name) { + l |= errorTags[i].level; + break; + } + if (st == string::npos) break; + v.erase(0, st + 1); + } + return l; +} + + +void lyx_debug_trait::showLevel(ostream & os, lyx_debug_trait::type level) +{ + // Show what features are traced + for (int i = 0; i < numErrorTags ; ++i) { + if (errorTags[i].level != Debug::ANY + && errorTags[i].level != Debug::NONE + && errorTags[i].level & level) { + // avoid _(...) re-entrance problem + string const s = _(errorTags[i].desc); + os << bformat(_("Debugging `%1$s' (%2$s)"), + errorTags[i].name, s) + << '\n'; + } + } + os.flush(); +} + + +void lyx_debug_trait::showTags(ostream & os) +{ + for (int i = 0; i < numErrorTags ; ++i) + os << setw(7) << static_cast(errorTags[i].level) + << setw(10) << errorTags[i].name + << " " << _(errorTags[i].desc) << '\n'; + os.flush(); +} + +LyXErr lyxerr; diff --git a/src/client/debug.h b/src/client/debug.h new file mode 100644 index 0000000000..68caf832aa --- /dev/null +++ b/src/client/debug.h @@ -0,0 +1,70 @@ +// -*- C++ -*- +/** + * \file debug.h + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Lars Gullik Bjønnes + * \author Jean-Marc Lasgouttes + * + * Full author contact details are available in file CREDITS. + */ + +#ifndef LYXDEBUG_H +#define LYXDEBUG_H + +#include "support/debugstream.h" + +/** Ideally this should have been a namespace, but since we try to be + compilable on older C++ compilators too, we use a struct instead. + This is all the different debug levels that we have. +*/ +struct lyx_debug_trait { + /// + enum type { + /// + NONE = 0, + /// + INFO = (1 << 0), + /// + DEBUG = (1 << 31), + /// + ANY = 0xffffffff + }; + + static bool match(type a, type b) { + return (a & b); + } + + /** A function to convert symbolic string names on debug levels + to their numerical value. + */ + static type value(std::string const & val); + + /** Display the tags and descriptions of the current debug level + of ds + */ + static void showLevel(std::ostream & o, type level); + + /** show all the possible tags that can be used for debugging */ + static void showTags(std::ostream & o); + +}; + + + +inline +void operator|=(lyx_debug_trait::type & d1, lyx_debug_trait::type d2) +{ + d1 = static_cast(d1 | d2); +} + + +// std::ostream & operator<<(std::ostream & o, Debug::type t); + +typedef basic_debugstream LyXErr; +typedef LyXErr::debug Debug; + +extern LyXErr lyxerr; + +#endif diff --git a/src/client/gettext.C b/src/client/gettext.C new file mode 100644 index 0000000000..2f08de5038 --- /dev/null +++ b/src/client/gettext.C @@ -0,0 +1,61 @@ +/** + * \file src/gettext.C + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Lars Gullik Bjønnes + * \author Jean-Marc Lasgouttes + * + * Full author contact details are available in file CREDITS. + */ + +#include + +#include "gettext.h" +#include "messages.h" + + +#ifdef HAVE_LOCALE_H +# include +#endif + +using std::string; + + +namespace { + +Messages & getLyXMessages() +{ + static Messages lyx_messages; + + return lyx_messages; +} + +} // anon namespace + + +string const _(string const & str) +{ + return getLyXMessages().get(str); +} + + +#ifdef ENABLE_NLS + +void locale_init() +{ +# ifdef HAVE_LC_MESSAGES + setlocale(LC_MESSAGES, ""); +# endif + setlocale(LC_CTYPE, ""); + setlocale(LC_NUMERIC, "C"); +} + +#else // ENABLE_NLS + +void locale_init() +{ + setlocale(LC_NUMERIC, "C"); +} + +#endif diff --git a/src/client/gettext.h b/src/client/gettext.h new file mode 100644 index 0000000000..7cb097af48 --- /dev/null +++ b/src/client/gettext.h @@ -0,0 +1,64 @@ +// -*- C++ -*- +/** + * \file src/gettext.h + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Lars Gullik Bjønnes + * \author Jean-Marc Lasgouttes + * + * Full author contact details are available in file CREDITS. + */ + +#ifndef GETTEXT_H +#define GETTEXT_H + +#include + +/* + * Native Language Support + * + * The general idea is that any string that should be translated is handled + * as follows: + * _("string") + * + * Static strings are special, obviously and must be flagged as follows: + * static str = N_("string"); + * + * And wherever they are used: + * _(str) + * + * Every file where there are strings needs: + * #include "gettext.h" + * + * Remember to mention each of these files in "po/POFILES.in" + * + * The main() needs a locale_init() and a gettext_init() in the beginning. + */ + +/* + * General translation notes: + * Commands/options are not translated + * Debug messages are not translated + * Panic/fatal (that should not happen) messages need not be translated + */ + + +//#ifdef ENABLE_NLS + +/// +std::string const _(std::string const &); + +//#else // ENABLE_NLS + +/// +//# define _(str) (str) + +//#endif + +# define N_(str) (str) // for detecting static strings + +/// +void locale_init(); + +#endif diff --git a/src/client/lyxclient.man b/src/client/lyxclient.man new file mode 100644 index 0000000000..967f623343 --- /dev/null +++ b/src/client/lyxclient.man @@ -0,0 +1,83 @@ +.\" Man page for lyxclient. +.\" Use the following command to view man page: +.\" +.\" tbl lyxclient.1 | nroff -man | less +.\" +.TH LYXCLIENT 1 "Oct 2003" "Version 1.4" "lyxclient 1.4" +.SH NAME +lyxclient \- send commands to a running LyX editor +.\" +.\" setup +.de Cr +.ie n (c) +.el \(co +.. +.SH SYNOPSIS +\fBlyxclient\fR [ \fIoptions\fR ] +.br +.SH DESCRIPTION +When LyX starts, it creates a unique, per-process local socket in the +temporary directory through which commands can be sent. That is, it can act +like a server, accepting connections from clients. \fBlyxclient\fR +can be used as such client. \fBlyxclient\fR will take care of all +connection and communication protocol details, leaving you (or your app) +free to concentrate on what you want to send to LyX. +.SH SERVER IDENTIFICATION OPTIONS +\fBlyxclient\fR must first identify to which server (i.e. a running LyX) +commands are to be sent. The following options are used to specify the server. +.TP 6 +.TP +.BI \-p " pid" +specify the \fIpid\fR of the running LyX process to which \fBlyxclient\fR +should send commands. +.TP +.BI \-a " socket_address" +specify explicitly which socket special file should be used. These special +files are located inside lyx_tmpdir, in the temporary +directory. There is one per running LyX process. +.TP +.BI \-t " tmp_dir" +if LyX is configured to use a temporary directory other than /tmp, you must +inform \fBlyxclient\fR of this. +.PP +If neither \fB-a\fR nor \fB-p\fR are invoked, \fBlyxclient\fR will search for +sockets in /tmp (or \fItmp_dir\fR if the \fB-t\fR option is used) and use +the first socket to which it can connect. +This is safe if you are running only one LyX process at any one time. +.SH COMMAND MODE OPTIONS +\fBlyxclient\fR can send commands to LyX from both the command-line +and from standard input. +LyX commands are documented in . +.TP 6 +.BI \-c " command" +send a single \fIcommand\fR, print LyX information to standard output and exit. +.TP +.BI \-g " file line" +this is simply a wrapper for the command 'server-goto-file-row \fIfile\fR \fIline\fR'. It is used by the DVI previewer to elicit inverse DVI search. +.PP +If neither \fB-c\fR nor \fB-g\fR are used, \fBlyxclient\fR will regard any +standard input as commands to be sent to LyX, printing LyX's responses to +standard output. Commands are +separated by newlines (the '\\n' character). To finish communication +and terminate the \fBlyxclient\fR process, send the command 'BYE:'. +.SH MISCELANEOUS OPTIONS +.TP 6 +.BI \-n " name" +when starting communication, \fBlyxclient\fR sends an idenfifier +string to LyX. By default, this string is "PPID>PID", where PPID is +\fBlyxclient\fR's parent pid and pid is \fBlyxclient\fR's pid. +Use this option to override this default. +.TP +.BI \-h +print the \fBlyxclient\fR version and summarize its usage. +.SH ENVIRONMENT +.TP +.B LYXSOCKET +can be used to specify the socket special file that must be used. +LyX sets this variable. +It is overridden by the \fB-a\fR option. +.SH SEE ALSO +lyx(1), xdvi(1), lyx functions . +.SH AUTHORS +João Luis M. Assirati is the principal author +of lyxclient. diff --git a/src/client/messages.C b/src/client/messages.C new file mode 100644 index 0000000000..d1aa3f5221 --- /dev/null +++ b/src/client/messages.C @@ -0,0 +1,147 @@ +/* \file messages.C + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Lars Gullik Bjønnes + * + * Full author contact details are available in file CREDITS. + */ + +#include + +#include "messages.h" +#include "support/filetools.h" +#include "support/path_defines.h" + +using lyx::support::GetEnvPath; +using lyx::support::lyx_localedir; + +using std::string; + + +#ifdef ENABLE_NLS + + +#if 0 + +-#include + +// This version of the Pimpl utilizes the message capability of +// libstdc++ that is distributed with GNU G++. +class Messages::Pimpl { +public: + typedef std::messages::catalog catalog; + + Pimpl(string const & l) + : lang_(l), + loc_gl(lang_.c_str()), + mssg_gl(std::use_facet >(loc_gl)) + { + //lyxerr << "Messages: language(" << l + // << ") in dir(" << dir << ")" << std::endl; + + cat_gl = mssg_gl.open(PACKAGE, loc_gl, lyx_localedir().c_str()); + + } + + ~Pimpl() + { + mssg_gl.close(cat_gl); + } + + string const get(string const & msg) const + { + return mssg_gl.get(cat_gl, 0, 0, msg); + } +private: + /// + string lang_; + /// + std::locale loc_gl; + /// + std::messages const & mssg_gl; + /// + catalog cat_gl; +}; +#else + +#ifdef HAVE_LOCALE_H +# include +#endif + +# if HAVE_GETTEXT +# include // use the header already in the system *EK* +# else +# include "../intl/libintl.h" +# endif + +// This is a more traditional variant. +class Messages::Pimpl { +public: + Pimpl(string const & l) + : lang_(l) + { + //lyxerr << "Messages: language(" << l + // << ") in dir(" << dir << ")" << std::endl; + + } + + ~Pimpl() {} + + string const get(string const & m) const + { + if (m.empty()) + return m; + + char * old = strdup(setlocale(LC_ALL, 0)); + char * n = setlocale(LC_ALL, lang_.c_str()); + bindtextdomain(PACKAGE, lyx_localedir().c_str()); + textdomain(PACKAGE); + const char* msg = gettext(m.c_str()); + setlocale(LC_ALL, old); + free(old); + // If we are unable to honour the request we just + // return what we got in. + return (!n ? m : string(msg)); + } +private: + /// + string lang_; +}; +#endif + +#else // ENABLE_NLS +// This is the dummy variant. +class Messages::Pimpl { +public: + Pimpl(string const &) {} + + ~Pimpl() {} + + string const get(string const & m) const + { + return m; + } +}; +#endif + + +Messages::Messages() + : pimpl_(new Pimpl("")) +{} + + +Messages::Messages(string const & l) + : pimpl_(new Pimpl(l)) +{} + + +// We need this for the sake of scoped_ptr +Messages::~Messages() +{} + + +string const Messages::get(string const & msg) const +{ + return pimpl_->get(msg); +} diff --git a/src/client/messages.h b/src/client/messages.h new file mode 100644 index 0000000000..c4b2fa25ec --- /dev/null +++ b/src/client/messages.h @@ -0,0 +1,33 @@ +// -*- C++ -*- +/* \file messages.h + * This file is part of LyX, the document processor. + * Licence details can be found in the file COPYING. + * + * \author Lars Gullik Bjønnes + * + * Full author contact details are available in file CREDITS. + */ + +#ifndef MESSAGES_H +#define MESSAGES_H + +#include +#include + +/// +class Messages { +public: + /// + Messages(); + /// + Messages(std::string const & l); + /// + ~Messages(); + /// + std::string const get(std::string const & msg) const; +private: + class Pimpl; + boost::scoped_ptr pimpl_; +}; + +#endif diff --git a/src/support/ChangeLog b/src/support/ChangeLog index 743a38368a..f1636c967b 100644 --- a/src/support/ChangeLog +++ b/src/support/ChangeLog @@ -1,3 +1,7 @@ +2004-09-04 Lars Gullik Bjonnes + + * tostr.C: reimplement using lexical_cast + 2004-08-30 Lars Gullik Bjonnes * pch.h: do not use include boost/format.hpp, multiple symbols diff --git a/src/support/tostr.C b/src/support/tostr.C index 81058d5d87..d490413f84 100644 --- a/src/support/tostr.C +++ b/src/support/tostr.C @@ -4,16 +4,19 @@ * Licence details can be found in the file COPYING. * * \author André Pönitz + * \author Lars Gullik Bjønnes * * Full author contact details are available in file CREDITS. */ #include -#include +#include + + +using boost::lexical_cast; using std::string; -using std::ostringstream; string const tostr(bool b) @@ -24,33 +27,25 @@ string const tostr(bool b) string const tostr(unsigned int i) { - ostringstream os; - os << i; - return os.str(); + return lexical_cast(i); } string const tostr(long int i) { - ostringstream os; - os << i; - return os.str(); + return lexical_cast(i); } string const tostr(double d) { - ostringstream os; - os << d; - return os.str(); + return lexical_cast(d); } string const tostr(int i) { - ostringstream os; - os << i; - return os.str(); + return lexical_cast(i); } @@ -62,7 +57,5 @@ string const tostr(string const & s) string const tostr(long unsigned int i) { - ostringstream os; - os << i; - return os.str(); + return lexical_cast(i); } -- 2.39.2