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