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