]> git.lyx.org Git - lyx.git/blob - src/support/Package.cpp
6231a660ac69b067452af5d283f34937741c004d
[lyx.git] / src / support / Package.cpp
1 // -*- C++ -*-
2 /**
3  * \file package.C
4  * This file is part of LyX, the document processor.
5  * Licence details can be found in the file COPYING.
6  *
7  * \author Angus Leeming
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "support/Package.h"
15
16 #include "debug.h"
17 #include "gettext.h"
18
19 #include "support/environment.h"
20 #include "support/filetools.h"
21 #include "support/lstrings.h"
22 #include "support/ExceptionMessage.h"
23 #include "support/os.h"
24
25 #if defined (USE_WINDOWS_PACKAGING)
26 # include "support/os_win32.h"
27 #endif
28
29
30 #include <list>
31
32 #if !defined (USE_WINDOWS_PACKAGING) && \
33     !defined (USE_MACOSX_PACKAGING) && \
34     !defined (USE_POSIX_PACKAGING)
35 #error USE_FOO_PACKAGING must be defined for FOO = WINDOWS, MACOSX or POSIX.
36 #endif
37
38 #if defined (USE_MACOSX_PACKAGING)
39 # include <CoreServices/CoreServices.h> // FSFindFolder, FSRefMakePath
40 #endif
41
42 using std::string;
43
44 namespace lyx {
45 namespace support {
46
47 namespace {
48
49 Package package_;
50 bool initialised_ = false;
51
52 } // namespace anon
53
54
55 void init_package(string const & command_line_arg0,
56                   string const & command_line_system_support_dir,
57                   string const & command_line_user_support_dir,
58                   exe_build_dir_to_top_build_dir top_build_dir_location)
59 {
60         // Can do so only once.
61         if (initialised_)
62                 return;
63
64         package_ = Package(command_line_arg0,
65                            command_line_system_support_dir,
66                            command_line_user_support_dir,
67                            top_build_dir_location);
68         initialised_ = true;
69 }
70
71
72 Package const & package()
73 {
74         // Commented out because package().locale_dir() can be called
75         // from the message translation code in Messages.cpp before
76         // init_package() is called. Lars is on the case...
77         // BOOST_ASSERT(initialised_);
78         return package_;
79 }
80
81
82 namespace {
83
84 FileName const abs_path_from_binary_name(string const & exe);
85
86 void buildDirs(FileName const & abs_binary,
87         exe_build_dir_to_top_build_dir top_build_dir_location,
88         FileName &, FileName &);
89
90 FileName const get_document_dir(FileName const & home_dir);
91
92 FileName const get_home_dir();
93
94 FileName const get_locale_dir(FileName const & system_support_dir);
95
96 FileName const get_system_support_dir(FileName const & abs_binary,
97                                     string const & command_line_system_support_dir);
98
99 FileName const get_temp_dir();
100
101 FileName const get_default_user_support_dir(FileName const & home_dir);
102
103 bool userSupportDir(FileName const & default_user_support_dir,
104                      string const & command_line_user_support_dir, FileName & result);
105
106
107 string const & with_version_suffix();
108
109 } // namespace anon
110
111
112 Package::Package(string const & command_line_arg0,
113                  string const & command_line_system_support_dir,
114                  string const & command_line_user_support_dir,
115                  exe_build_dir_to_top_build_dir top_build_dir_location)
116         : explicit_user_support_dir_(false)
117 {
118         home_dir_ = get_home_dir();
119         system_temp_dir_ = get_temp_dir();
120         temp_dir_ = system_temp_dir_;
121         document_dir_ = get_document_dir(home_dir_);
122
123         FileName const abs_binary = abs_path_from_binary_name(command_line_arg0);
124         string const bdir = onlyPath(abs_binary.absFilename());
125         // We may be using libtools
126         if (suffixIs(bdir, ".libs/"))
127                 binary_dir_ = FileName(addPath(bdir, "../"));
128         else
129                 binary_dir_ = FileName(bdir);
130
131         // Is LyX being run in-place from the build tree?
132         buildDirs(abs_binary, top_build_dir_location,
133                 build_support_dir_, system_support_dir_);
134
135         if (build_support_dir_.empty())
136                 system_support_dir_ =
137                         get_system_support_dir(abs_binary,
138                                                command_line_system_support_dir);
139
140         locale_dir_ = get_locale_dir(system_support_dir_);
141
142         FileName const default_user_support_dir =
143                 get_default_user_support_dir(home_dir_);
144
145         explicit_user_support_dir_ = userSupportDir(default_user_support_dir,
146                                      command_line_user_support_dir, user_support_dir_);
147
148         FileName const configure_script(addName(system_support().absFilename(), "configure.py"));
149         configure_command_ = os::python() + ' ' +
150                         quoteName(configure_script.toFilesystemEncoding()) +
151                         with_version_suffix();
152
153         lyxerr[Debug::INIT]
154                 << "<package>\n"
155                 << "\tbinary_dir " << binary_dir().absFilename() << '\n'
156                 << "\tsystem_support " << system_support().absFilename() << '\n'
157                 << "\tbuild_support " << build_support().absFilename() << '\n'
158                 << "\tuser_support " << user_support().absFilename() << '\n'
159                 << "\tlocale_dir " << locale_dir().absFilename() << '\n'
160                 << "\tdocument_dir " << document_dir().absFilename() << '\n'
161                 << "\ttemp_dir " << temp_dir().absFilename() << '\n'
162                 << "\thome_dir " << home_dir().absFilename() << '\n'
163                 << "</package>\n" << std::endl;
164 }
165
166
167 namespace {
168
169 // These next functions contain the stuff that is substituted at
170 // configuration-time.
171 FileName const hardcoded_localedir()
172 {
173         // FIXME UNICODE
174         // The build system needs to make sure that this is in utf8 encoding.
175         return FileName(LYX_ABS_INSTALLED_LOCALEDIR);
176 }
177
178
179 FileName const hardcoded_system_support_dir()
180 {
181         // FIXME UNICODE
182         // The build system needs to make sure that this is in utf8 encoding.
183         return FileName(LYX_ABS_INSTALLED_DATADIR);
184 }
185
186
187 string const & with_version_suffix()
188 {
189         static string const program_suffix = PROGRAM_SUFFIX;
190         static string const with_version_suffix =
191                 " --with-version-suffix=" PROGRAM_SUFFIX;
192         return program_suffix.empty() ? program_suffix : with_version_suffix;
193 }
194
195 } // namespace anon
196
197
198 FileName const & Package::top_srcdir()
199 {
200         // FIXME UNICODE
201         // The build system needs to make sure that this is in utf8 encoding.
202         static FileName const dir(LYX_ABS_TOP_SRCDIR);
203         return dir;
204 }
205
206
207 namespace {
208
209 bool check_command_line_dir(string const & dir,
210                             string const & file,
211                             string const & command_line_switch);
212
213 FileName const extract_env_var_dir(string const & env_var);
214
215 bool check_env_var_dir(FileName const & dir,
216                        string const & env_var);
217
218 bool check_env_var_dir(FileName const & dir,
219                        string const & file,
220                        string const & env_var);
221
222 string const relative_locale_dir();
223
224 string const relative_system_support_dir();
225
226
227 /**
228  * Convert \p name to internal path and strip a trailing slash, since it
229  * comes from user input (commandline or environment).
230  * \p name is encoded in utf8.
231  */
232 string const fix_dir_name(string const & name)
233 {
234         return rtrim(os::internal_path(name), "/");
235 }
236
237
238 FileName buildSupportDir(string const & binary_dir,
239                       exe_build_dir_to_top_build_dir top_build_dir_location)
240 {
241         string indirection;
242         switch (top_build_dir_location) {
243         case top_build_dir_is_one_level_up:
244                 indirection = "../lib";
245                 break;
246         case top_build_dir_is_two_levels_up:
247                 indirection = "../../lib";
248                 break;
249         }
250         return FileName(addPath(binary_dir, indirection));
251 }
252
253
254 void buildDirs(FileName const & abs_binary,
255   exe_build_dir_to_top_build_dir top_build_dir_location,
256         FileName & build_support_dir, FileName & system_support_dir)
257 {
258         string const check_text = "Checking whether LyX is run in place...";
259
260         // We're looking for "Makefile" in a directory
261         //   binary_dir/../lib
262         // We're also looking for "chkconfig.ltx" in a directory
263         //   top_srcdir()/lib
264         // If both are found, then we're running LyX in-place.
265
266         // Note that the name of the lyx binary may be a symbolic link.
267         // If that is the case, then we follow the links too.
268         FileName binary = abs_binary;
269         while (true) {
270                 // Try and find "lyxrc.defaults".
271                 string binary_dir = onlyPath(binary.absFilename());
272                 // We may be using libtools with static linking.
273                 if (suffixIs(binary_dir, ".libs/"))
274                         binary_dir = addPath(binary_dir, "../");
275                 build_support_dir = buildSupportDir(binary_dir, top_build_dir_location);
276                 if (!fileSearch(build_support_dir.absFilename(), "Makefile").empty()) {
277                         // Try and find "chkconfig.ltx".
278                         system_support_dir =
279                                 FileName(addPath(Package::top_srcdir().absFilename(), "lib"));
280
281                         if (!fileSearch(system_support_dir.absFilename(), "chkconfig.ltx").empty()) {
282                                 lyxerr[Debug::INIT] << check_text << " yes"
283                                                     << std::endl;
284                                 return;
285                         }
286                 }
287
288                 // Check whether binary is a symbolic link.
289                 // If so, resolve it and repeat the exercise.
290                 if (!binary.isSymLink())
291                         break;
292
293                 FileName link;
294                 if (readLink(binary, link)) {
295                         binary = link;
296                 } else {
297                         // Unable to resolve the link.
298                         break;
299                 }
300         }
301
302         lyxerr[Debug::INIT] << check_text << " no" << std::endl;
303         system_support_dir = FileName();
304         build_support_dir = FileName();
305 }
306
307
308 // Specification of document_dir_ may be reset by LyXRC,
309 // but the default is fixed for a given OS.
310 FileName const get_document_dir(FileName const & home_dir)
311 {
312 #if defined (USE_WINDOWS_PACKAGING)
313         (void)home_dir; // Silence warning about unused variable.
314         os::GetFolderPath win32_folder_path;
315         return FileName(win32_folder_path(os::GetFolderPath::PERSONAL));
316 #else // Posix-like.
317         return home_dir;
318 #endif
319 }
320
321
322 // The specification of home_dir_ is fixed for a given OS.
323 // A typical example on Windows: "C:/Documents and Settings/USERNAME"
324 // and on a Posix-like machine: "/home/USERNAME".
325 FileName const get_home_dir()
326 {
327 #if defined (USE_WINDOWS_PACKAGING)
328         string const home_dir = getEnv("USERPROFILE");
329 #else // Posix-like.
330         string const home_dir = getEnv("HOME");
331 #endif
332
333         return FileName(fix_dir_name(home_dir));
334 }
335
336
337 // Several sources are probed to ascertain the locale directory.
338 // The only requirement is that the result is indeed a directory.
339 FileName const get_locale_dir(FileName const & system_support_dir)
340 {
341         // 1. Use the "LYX_LOCALEDIR" environment variable.
342         FileName const path_env = extract_env_var_dir("LYX_LOCALEDIR");
343         if (!path_env.empty() && check_env_var_dir(path_env, "LYX_LOCALEDIR"))
344                 return path_env;
345
346         // 2. Search for system_support_dir / <relative locale dir>
347         // The <relative locale dir> is OS-dependent. (On Unix, it will
348         // be "../locale/".)
349         FileName path(addPath(system_support_dir.absFilename(),
350                 relative_locale_dir()));
351
352         if (path.exists() && path.isDirectory())
353                 return path;
354
355         // 3. Fall back to the hard-coded LOCALEDIR.
356         path = hardcoded_localedir();
357         if (path.exists() && path.isDirectory())
358                 return path;
359
360         return FileName();
361 }
362
363
364 // Specification of temp_dir_ may be reset by LyXRC,
365 // but the default is fixed for a given OS.
366 FileName const get_temp_dir()
367 {
368 #if defined (USE_WINDOWS_PACKAGING)
369         // Typical example: C:/TEMP/.
370         char path[MAX_PATH];
371         GetTempPath(MAX_PATH, path);
372         return FileName(os::internal_path(to_utf8(from_local8bit(path))));
373 #else // Posix-like.
374         return FileName("/tmp");
375 #endif
376 }
377
378
379 // Extracts the absolute path from the foo of "-sysdir foo" or "-userdir foo"
380 FileName const abs_path_from_command_line(string const & command_line)
381 {
382         if (command_line.empty())
383                 return FileName();
384
385         string const path = fix_dir_name(command_line);
386         return os::is_absolute_path(path) ? FileName(path) : makeAbsPath(path);
387 }
388
389
390 // Does the grunt work for abs_path_from_binary_name()
391 FileName const get_binary_path(string const & exe)
392 {
393 #if defined (USE_WINDOWS_PACKAGING)
394         // The executable may have been invoked either with or
395         // without the .exe extension.
396         // Ensure that it is present.
397         string const as_internal_path = os::internal_path(exe);
398         string const exe_path = suffixIs(as_internal_path, ".exe") ?
399                 as_internal_path : as_internal_path + ".exe";
400 #else
401         string const exe_path = os::internal_path(exe);
402 #endif
403         if (os::is_absolute_path(exe_path))
404                 return FileName(exe_path);
405
406         // Two possibilities present themselves.
407         // 1. The binary is relative to the CWD.
408         FileName const abs_exe_path = makeAbsPath(exe_path);
409         if (abs_exe_path.exists())
410                 return abs_exe_path;
411
412         // 2. exe must be the name of the binary only and it
413         // can be found on the PATH.
414         string const exe_name = onlyFilename(exe_path);
415         if (exe_name != exe_path)
416                 return FileName();
417
418         std::vector<string> const path = getEnvPath("PATH");
419         std::vector<string>::const_iterator it = path.begin();
420         std::vector<string>::const_iterator const end = path.end();
421         for (; it != end; ++it) {
422                 // This will do nothing if *it is already absolute.
423                 string const exe_dir = makeAbsPath(*it).absFilename();
424
425                 FileName const exe_path(addName(exe_dir, exe_name));
426                 if (exe_path.exists())
427                         return exe_path;
428         }
429
430         // Didn't find anything.
431         return FileName();
432 }
433
434
435 // Extracts the absolute path to the binary name received as argv[0].
436 FileName const abs_path_from_binary_name(string const & exe)
437 {
438         FileName const abs_binary = get_binary_path(exe);
439         if (abs_binary.empty()) {
440                 // FIXME UNICODE
441                 throw ExceptionMessage(ErrorException,
442                         _("LyX binary not found"),
443                         bformat(_("Unable to determine the path to the LyX binary from the command line %1$s"),
444                                 from_utf8(exe)));
445         }
446         return abs_binary;
447 }
448
449
450 // A plethora of directories is searched to ascertain the system
451 // lyxdir which is defined as the first directory to contain
452 // "chkconfig.ltx".
453 FileName const
454 get_system_support_dir(FileName const & abs_binary,
455                   string const & command_line_system_support_dir)
456 {
457         string const chkconfig_ltx = "chkconfig.ltx";
458
459         // searched_dirs is used for diagnostic purposes only in the case
460         // that "chkconfig.ltx" is not found.
461         std::list<FileName> searched_dirs;
462
463         // 1. Use the -sysdir command line parameter.
464         FileName path = abs_path_from_command_line(command_line_system_support_dir);
465         if (!path.empty()) {
466                 searched_dirs.push_back(path);
467                 if (check_command_line_dir(path.absFilename(), chkconfig_ltx, "-sysdir"))
468                         return path;
469         }
470
471         // 2. Use the "LYX_DIR_15x" environment variable.
472         path = extract_env_var_dir("LYX_DIR_15x");
473         if (!path.empty()) {
474                 searched_dirs.push_back(path);
475                 if (check_env_var_dir(path, chkconfig_ltx, "LYX_DIR_15x"))
476                         return path;
477         }
478
479         // 3. Search relative to the lyx binary.
480         // We're looking for "chkconfig.ltx" in a directory
481         //   OnlyPath(abs_binary) / <relative dir> / PACKAGE /
482         // PACKAGE is hardcoded in config.h. Eg "lyx" or "lyx-1.3.6cvs".
483         // <relative dir> is OS-dependent; on Unix, it will be "../share/".
484         string const relative_lyxdir = relative_system_support_dir();
485
486         // One subtlety to be aware of. The name of the lyx binary may be
487         // a symbolic link. If that is the case, then we follow the links too.
488         FileName binary = abs_binary;
489         while (true) {
490                 // Try and find "chkconfig.ltx".
491                 string const binary_dir = onlyPath(binary.absFilename());
492
493                 FileName const lyxdir(addPath(binary_dir, relative_lyxdir));
494                 searched_dirs.push_back(lyxdir);
495
496                 if (!fileSearch(lyxdir.absFilename(), chkconfig_ltx).empty()) {
497                         // Success! "chkconfig.ltx" has been found.
498                         return lyxdir;
499                 }
500
501                 // Check whether binary is a symbolic link.
502                 // If so, resolve it and repeat the exercise.
503                 if (!binary.isSymLink())
504                         break;
505
506                 FileName link;
507                 if (readLink(binary, link)) {
508                         binary = link;
509                 } else {
510                         // Unable to resolve the link.
511                         break;
512                 }
513         }
514
515         // 4. Repeat the exercise on the directory itself.
516         FileName binary_dir(onlyPath(abs_binary.absFilename()));
517         while (true) {
518                 // This time test whether the directory is a symbolic link
519                 // *before* looking for "chkconfig.ltx".
520                 // (We've looked relative to the original already.)
521                 if (!binary.isSymLink())
522                         break;
523
524                 FileName link;
525                 if (readLink(binary_dir, link)) {
526                         binary_dir = link;
527                 } else {
528                         // Unable to resolve the link.
529                         break;
530                 }
531
532                 // Try and find "chkconfig.ltx".
533                 FileName const lyxdir(addPath(binary_dir.absFilename(),
534                         relative_lyxdir));
535                 searched_dirs.push_back(lyxdir);
536
537                 if (!fileSearch(lyxdir.absFilename(), chkconfig_ltx).empty()) {
538                         // Success! "chkconfig.ltx" has been found.
539                         return lyxdir;
540                 }
541         }
542
543         // 5. In desparation, try the hard-coded system support dir.
544         path = hardcoded_system_support_dir();
545         if (!fileSearch(path.absFilename(), chkconfig_ltx).empty())
546                 return path;
547
548         // Everything has failed :-(
549         // So inform the user and exit.
550         string searched_dirs_str;
551         typedef std::list<FileName>::const_iterator iterator;
552         iterator const begin = searched_dirs.begin();
553         iterator const end = searched_dirs.end();
554         for (iterator it = begin; it != end; ++it) {
555                 if (it != begin)
556                         searched_dirs_str += "\n\t";
557                 searched_dirs_str += it->absFilename();
558         }
559
560         // FIXME UNICODE
561         throw ExceptionMessage(ErrorException, _("No system directory"),
562                 bformat(_("Unable to determine the system directory "
563                                          "having searched\n"
564                                          "\t%1$s\n"
565                                          "Use the '-sysdir' command line parameter or "
566                                          "set the environment variable LYX_DIR_15x to "
567                                          "the LyX system directory containing the file "
568                                          "`chkconfig.ltx'."),
569                           from_utf8(searched_dirs_str)));
570
571         // Keep the compiler happy.
572         return FileName();
573 }
574
575
576 // Returns the absolute path to the user lyxdir, together with a flag
577 // indicating whether this directory was specified explicitly (as -userdir
578 // or through an environment variable) or whether it was deduced.
579 bool userSupportDir(FileName const & default_user_support_dir,
580         string const & command_line_user_support_dir, FileName & result)
581 {
582         // 1. Use the -userdir command line parameter.
583         result = abs_path_from_command_line(command_line_user_support_dir);
584         if (!result.empty())
585                 return true;
586
587         // 2. Use the LYX_USERDIR_15x environment variable.
588         result = extract_env_var_dir("LYX_USERDIR_15x");
589         if (!result.empty())
590                 return true;
591
592         // 3. Use the OS-dependent default_user_support_dir
593         result = default_user_support_dir;
594         return false;
595 }
596
597
598 // $HOME/.lyx on POSIX but on Win32 it will be something like
599 // "C:/Documents and Settings/USERNAME/Application Data/LyX"
600 FileName const get_default_user_support_dir(FileName const & home_dir)
601 {
602 #if defined (USE_WINDOWS_PACKAGING)
603         (void)home_dir; // Silence warning about unused variable.
604
605         os::GetFolderPath win32_folder_path;
606         return FileName(addPath(win32_folder_path(os::GetFolderPath::APPDATA), PACKAGE));
607
608 #elif defined (USE_MACOSX_PACKAGING)
609         (void)home_dir; // Silence warning about unused variable.
610
611         FSRef fsref;
612         OSErr const error_code =
613                 FSFindFolder(kUserDomain, kApplicationSupportFolderType,
614                              kDontCreateFolder, &fsref);
615         if (error_code != 0)
616                 return FileName();
617
618         // FSRefMakePath returns the result in utf8
619         char store[PATH_MAX + 1];
620         OSStatus const status_code =
621                 FSRefMakePath(&fsref,
622                               reinterpret_cast<UInt8*>(store), PATH_MAX);
623         if (status_code != 0)
624                 return FileName();
625
626         return FileName(addPath(reinterpret_cast<char const *>(store), PACKAGE));
627
628 #else // USE_POSIX_PACKAGING
629         return FileName(addPath(home_dir.absFilename(), string(".") + PACKAGE));
630 #endif
631 }
632
633
634 // Check that directory @c dir contains @c file.
635 // Else emit a warning about an invalid @c command_line_switch.
636 bool check_command_line_dir(string const & dir,
637                             string const & file,
638                             string const & command_line_switch)
639 {
640         FileName const abs_path = fileSearch(dir, file);
641         if (abs_path.empty()) {
642                 // FIXME UNICODE
643                 throw ExceptionMessage(WarningException, _("File not found"), bformat(
644                         _("Invalid %1$s switch.\nDirectory %2$s does not contain %3$s."),
645                         from_utf8(command_line_switch), from_utf8(dir),
646                         from_utf8(file)));
647         }
648
649         return !abs_path.empty();
650 }
651
652
653 // The environment variable @c env_var expands to a (single) file path.
654 FileName const extract_env_var_dir(string const & env_var)
655 {
656         string const dir = fix_dir_name(getEnv(env_var));
657         return dir.empty() ? FileName() : makeAbsPath(dir);
658 }
659
660
661 // Check that directory @c dir contains @c file.
662 // Else emit a warning about an invalid @c env_var.
663 bool check_env_var_dir(FileName const & dir,
664                        string const & file,
665                        string const & env_var)
666 {
667         FileName const abs_path = fileSearch(dir.absFilename(), file);
668         if (abs_path.empty()) {
669                 // FIXME UNICODE
670                 throw ExceptionMessage(WarningException, _("File not found"), bformat(
671                         _("Invalid %1$s environment variable.\n"
672                                 "Directory %2$s does not contain %3$s."),
673                         from_utf8(env_var), from_utf8(dir.absFilename()),
674                         from_utf8(file)));
675         }
676
677         return !abs_path.empty();
678 }
679
680
681 // Check that directory @c dir is indeed a directory.
682 // Else emit a warning about an invalid @c env_var.
683 bool check_env_var_dir(FileName const & dir,
684                        string const & env_var)
685 {
686         bool const success = dir.exists() && dir.isDirectory();
687
688         if (!success) {
689                 // Put this string on a single line so that the gettext
690                 // search mechanism in po/Makefile.in.in will register
691                 // Package.cpp.in as a file containing strings that need
692                 // translation.
693                 // FIXME UNICODE
694                 docstring const fmt =
695                         _("Invalid %1$s environment variable.\n%2$s is not a directory.");
696
697                 throw ExceptionMessage(WarningException, _("Directory not found"), bformat(
698                         fmt, from_utf8(env_var), from_utf8(dir.absFilename())));
699         }
700
701         return success;
702 }
703
704
705 // The locale directory relative to the LyX system directory.
706 string const relative_locale_dir()
707 {
708 #if defined (USE_WINDOWS_PACKAGING) || defined (USE_MACOSX_PACKAGING)
709         return "locale/";
710 #else
711         return "../locale/";
712 #endif
713 }
714
715
716 // The system lyxdir is relative to the directory containing the LyX binary.
717 string const relative_system_support_dir()
718 {
719         string result;
720
721 #if defined (USE_WINDOWS_PACKAGING) || defined (USE_MACOSX_PACKAGING)
722         result = "../Resources/";
723 #else // Posix-like.
724         result = addPath("../share/", PACKAGE);
725 #endif
726
727         return result;
728 }
729
730 } // namespace anon
731
732 } // namespace support
733 } // namespace lyx