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