]> git.donarmstrong.com Git - lilypond.git/blob - lily/relocate.cc
Merge tag 'upstream/2.18.0' into debian-experimental
[lilypond.git] / lily / relocate.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 2005--2012 Han-Wen Nienhuys <hanwen@xs4all.nl>
5
6   LilyPond is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10
11   LilyPond is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "relocate.hh"
21
22 #include "config.hh"
23
24 /* TODO: autoconf support */
25
26 #include <ctype.h>
27 #include <sys/types.h>
28 #include <dirent.h>
29 #include <cstring>
30
31 #if HAVE_GETTEXT
32 #include <libintl.h>
33 #endif
34
35 #include "file-name.hh"
36 #include "file-path.hh"
37 #include "international.hh"
38 #include "lily-guile.hh"
39 #include "lily-version.hh"
40 #include "main.hh"
41 #include "version.hh"
42 #include "warn.hh"
43
44 #define FRAMEWORKDIR ".."
45
46 int
47 sane_putenv (char const *key, const string &value, bool overwrite)
48 {
49   if (overwrite || !getenv (key))
50     {
51       string combine = string (key) + "=" + value;
52       char *s = strdup (combine.c_str ());
53
54       debug_output (_f ("Setting %s to %s", key, value.c_str ())
55                     + "\n");
56
57       int retval = putenv (s);
58       /*
59         unfortunately, we can't portably free S here,
60         due to various bugs in glibc prior to 2.1.1
61       */
62       return retval;
63     }
64
65   return -1;
66 }
67
68 static int
69 set_env_file (char const *key, const string &value, bool overwrite = false)
70 {
71   if (is_file (value))
72     return sane_putenv (key, value, overwrite);
73   else if (is_loglevel (LOG_DEBUG))
74     // this warning should only be printed in debug mode!
75     warning (_f ("no such file: %s for %s", value, key));
76   return -1;
77 }
78
79 static int
80 set_env_dir (char const *key, const string &value)
81 {
82   if (is_dir (value))
83     return sane_putenv (key, value, false);
84   else if (is_loglevel (LOG_DEBUG))
85     // this warning should only be printed in debug mode!
86     warning (_f ("no such directory: %s for %s", value, key));
87   return -1;
88 }
89
90 static int
91 prepend_env_path (char const *key, string value)
92 {
93   if (is_dir (value))
94     {
95       debug_output (_f ("%s=%s (prepend)\n", key, value.c_str ()), false);
96
97       if (char const *cur = getenv (key))
98         value += to_string (PATHSEP) + cur;
99
100       return sane_putenv (key, value.c_str (), true);
101     }
102   else if (is_loglevel (LOG_DEBUG))
103     // this warning should only be printed in debug mode
104     warning (_f ("no such directory: %s for %s", value, key));
105   return -1;
106 }
107
108 #ifdef __MINGW32__
109 #include <winbase.h>
110 #endif
111
112 static void
113 prefix_relocation (const string &prefix)
114 {
115   string bindir = prefix + "/bin";
116   string datadir = prefix + "/share";
117   string localedir = datadir + "/locale";
118   string package_datadir = datadir + "/lilypond/";
119   string old_lilypond_datadir = lilypond_datadir;
120
121   if (is_dir (package_datadir + "/" + TOPLEVEL_VERSION))
122     lilypond_datadir = package_datadir + "/" + TOPLEVEL_VERSION;
123   else if (is_dir (package_datadir + "/current"))
124     lilypond_datadir = package_datadir + "/current";
125   else
126     warning (_f ("not relocating, no %s/ or current/ found under %s",
127                  TOPLEVEL_VERSION, package_datadir.c_str ()));
128
129 #if HAVE_GETTEXT
130   if (is_dir (localedir))
131     bindtextdomain ("lilypond", localedir.c_str ());
132 #endif
133
134   prepend_env_path ("PATH", bindir);
135
136   debug_output (_f ("Relocation: compile datadir=%s, new datadir=%s",
137                     old_lilypond_datadir.c_str (),
138                     lilypond_datadir.c_str ()));
139 }
140
141 /*
142   UGH : this is a complete mess.
143 */
144
145 static void
146 framework_relocation (const string &prefix)
147 {
148   debug_output (_f ("Relocation: framework_prefix=%s", prefix));
149
150   sane_putenv ("INSTALLER_PREFIX", prefix, true);
151
152   read_relocation_dir (prefix + "/etc/relocate/");
153
154   string bindir = prefix + "/bin";
155
156   prepend_env_path ("PATH", bindir);
157 }
158
159 /*
160   UGH : this is a complete mess.
161 */
162 void
163 setup_paths (char const *argv0_ptr)
164 {
165   File_name argv0_filename (argv0_ptr);
166
167   if (relocate_binary)
168     {
169       string prefix_directory;
170       string argv0_abs;
171       if (argv0_filename.is_absolute ())
172         {
173           argv0_abs = argv0_filename.to_string ();
174           debug_output (_f ("Relocation: is absolute: argv0=%s\n", argv0_ptr));
175         }
176       else if (argv0_filename.dir_.length ())
177         {
178           argv0_abs = get_working_directory ()
179                       + "/" + string (argv0_filename.to_string ());
180           debug_output (_f ("Relocation : from cwd: argv0=%s\n", argv0_ptr));
181         }
182       else
183         {
184           /* Find absolute ARGV0 name, using PATH.  */
185           File_path path;
186           path.parse_path (getenv ("PATH"));
187
188 #ifndef __MINGW32__
189           argv0_abs = path.find (argv0_filename.to_string ());
190 #else /* __MINGW32__ */
191           path.prepend (get_working_directory ());
192           char const *ext[] = {"exe", "", 0 };
193           argv0_abs = path.find (argv0_filename.to_string (), ext);
194 #endif /* __MINGW32__ */
195
196           debug_output (_f ("Relocation: from PATH=%s\nargv0=%s\n",
197                             path.to_string ().c_str (), argv0_ptr), true);
198
199           if (argv0_abs.empty ())
200             programming_error ("cannot find absolute argv0");
201         }
202
203       string bindir = dir_name (argv0_abs);
204       string argv0_prefix = dir_name (bindir);
205       string compile_prefix = dir_name (dir_name (dir_name (lilypond_datadir)));
206       if (argv0_prefix != compile_prefix)
207         {
208           prefix_relocation (argv0_prefix);
209           prefix_directory = argv0_prefix;
210         }
211       if (argv0_prefix != compile_prefix || string (FRAMEWORKDIR) != "..")
212         {
213           framework_relocation (bindir + "/" + FRAMEWORKDIR);
214           prefix_directory = bindir + "/" + FRAMEWORKDIR;
215         }
216
217       lilypond_datadir = prefix_directory
218                          + "/share/lilypond/" TOPLEVEL_VERSION;
219     }
220
221   if (getenv ("LILYPONDPREFIX"))
222     error (_ ("LILYPONDPREFIX is obsolete, use LILYPOND_DATADIR"));
223
224   if (char const *env = getenv ("LILYPOND_DATADIR"))
225     {
226       /* Normalize file name.  */
227       lilypond_datadir = File_name (env).to_string ();
228     }
229
230   /* When running from build dir, a full LILYPOND_DATADIR is set-up at
231      $(OUTBASE)/{share, lib}/lilypond/current.  Configure lily using
232      ./configure --prefix=$(pwd)/out */
233   string build_datadir_current = dir_name (lilypond_datadir) + "/current";
234   if (!is_dir (lilypond_datadir.c_str ())
235       && is_dir (build_datadir_current.c_str ()))
236     lilypond_datadir = build_datadir_current;
237
238   lilypond_datadir = File_name (lilypond_datadir).canonicalized ().to_string ();
239
240   global_path.append ("");
241
242   /* Adding mf/out make lilypond unchanged source directory, when setting
243      LILYPONDPREFIX to lilypond-x.y.z */
244   char const *suffixes[] = {"ly", "ps", "scm", 0 };
245
246   vector<string> dirs;
247   for (char const **s = suffixes; *s; s++)
248     {
249       string path = lilypond_datadir + to_string ('/') + string (*s);
250       dirs.push_back (path);
251     }
252
253   dirs.push_back (lilypond_datadir + "/fonts/otf/");
254   dirs.push_back (lilypond_datadir + "/fonts/type1/");
255   dirs.push_back (lilypond_datadir + "/fonts/svg/");
256
257   for (vsize i = 0; i < dirs.size (); i++)
258     global_path.prepend (dirs[i]);
259 }
260
261 string
262 expand_environment_variables (const string &orig)
263 {
264   char const *start_ptr = orig.c_str ();
265   char const *ptr = orig.c_str ();
266   size_t len = orig.length ();
267
268   string out;
269   while (ptr < start_ptr + len)
270     {
271       char const *dollar = strchr (ptr, '$');
272
273       if (dollar != NULL)
274         {
275           char const *start_var = dollar + 1;
276           char const *end_var = start_var;
277           char const *start_next = end_var;
278
279           out += string (ptr, dollar - ptr);
280           ptr = dollar;
281
282           if (*start_var == '{')
283             {
284               start_var++;
285
286               end_var = strchr (start_var, '}');
287
288               if (end_var == NULL)
289                 {
290                   end_var = start_var + len;
291                   start_next = end_var;
292                 }
293               else
294                 {
295                   start_next = end_var + 1;
296                 }
297             }
298           else
299             {
300               /*
301                 Hmm. what to do for $1 , $~ etc.?
302               */
303               do
304                 {
305                   end_var++;
306                 }
307               while (isalnum (*end_var) || *end_var == '_');
308               start_next = end_var;
309             }
310
311           if (start_var < end_var)
312             {
313               string var_name (start_var, end_var - start_var);
314               char const *value = getenv (var_name.c_str ());
315               if (value != NULL)
316                 out += string (value);
317
318               ptr = start_next;
319             }
320         }
321       else
322         break;
323
324     }
325
326   out += ptr;
327
328   return out;
329 }
330
331 // Ugh - very inefficient, but safer than fgets.
332 static string
333 read_line (FILE *f)
334 {
335   string out;
336
337   int c = 0;
338   while ((c = fgetc (f)) != EOF && c != '\n')
339     out += char (c);
340
341   return out;
342 }
343
344 void
345 read_relocation_file (const string &filename)
346 {
347   debug_output (_f ("Relocation file: %s", filename.c_str ()) + "\n");
348   char const *cname = filename.c_str ();
349   FILE *f = fopen (cname, "r");
350   if (!f)
351     error (_f ("cannot open file: `%s'", cname));
352
353   while (!feof (f))
354     {
355       string line = read_line (f);
356       size_t idx = line.find (' ');
357       if (idx == NPOS)
358         continue;
359
360       string command = line.substr (0, idx);
361       line = line.substr (idx + 1);
362
363       if (idx == NPOS)
364         continue;
365       idx = line.find ('=');
366
367       string variable = line.substr (0, idx);
368       string value = line.substr (idx + 1);
369
370       value = expand_environment_variables (value);
371
372       if (command == "set")
373         sane_putenv (variable.c_str (), value, true);
374       else if (command == "setdir")
375         set_env_dir (variable.c_str (), value);
376       else if (command == "setfile")
377         set_env_file (variable.c_str (), value);
378       else if (command == "prependdir")
379         prepend_env_path (variable.c_str (), value);
380       else
381         error (_f ("Unknown relocation command %s", command));
382     }
383
384   fclose (f);
385 }
386
387 void
388 read_relocation_dir (const string &dirname)
389 {
390   if (DIR *dir = opendir (dirname.c_str ()))
391     while (struct dirent *ent = readdir (dir))
392       {
393         File_name name (ent->d_name);
394         if (name.ext_ == "reloc")
395           read_relocation_file (dirname + "/" + name.to_string ());
396       }
397 }