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