]> git.donarmstrong.com Git - lilypond.git/blob - lily/relocate.cc
628187363bd39aa8417064146bf779189f5cbfdd
[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
25 /* TODO: autoconf support */
26
27 #include <sys/types.h>
28 #include <dirent.h>
29
30 #if HAVE_GETTEXT
31 #include <libintl.h>
32 #endif
33
34 #include "file-name.hh"
35 #include "file-path.hh"
36 #include "international.hh"
37 #include "lily-guile.hh"
38 #include "lily-version.hh"
39 #include "main.hh"
40 #include "version.hh"
41 #include "warn.hh"
42
43 #define FRAMEWORKDIR ".."
44
45 int
46 sane_putenv (char const *key, string value, bool overwrite)
47 {
48   if (overwrite || !getenv (key))
49     {
50       string combine = string (key) + "=" + value;
51       char *s = strdup (combine.c_str ());
52
53       if (be_verbose_global)
54         progress_indication (_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, string value, bool overwrite = false)
70 {
71   if (is_file (value))
72     return sane_putenv (key, value, overwrite);
73   else if (be_verbose_global)
74     warning (_f ("no such file: %s for %s", value, key));
75   return -1;
76 }
77
78 static int
79 set_env_dir (char const *key, string value)
80 {
81   if (is_dir (value))
82     return sane_putenv (key, value, false);
83   else if (be_verbose_global)
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       if (be_verbose_global)
94         progress_indication (_f ("%s=%s (prepend)\n", key, value.c_str ())); 
95
96       if (char const *cur = getenv (key))
97         value += to_string (PATHSEP) + cur;
98
99       return sane_putenv (key, value.c_str (), true);
100     }
101   else if (be_verbose_global)
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   if (be_verbose_global)
135     warning (_f ("Relocation: compile datadir=%s, new datadir=%s",
136                  old_lilypond_datadir.c_str (),
137                  lilypond_datadir.c_str ()));
138 }
139
140 /*
141   UGH : this is a complete mess.
142 */
143
144 static void
145 framework_relocation (string prefix)
146 {
147   if (be_verbose_global)
148     warning (_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       if (getenv ("LILYPOND_RELOCATE_PREFIX"))
171         {
172           prefix_directory = getenv ("LILYPOND_RELOCATE_PREFIX");
173 #ifdef __MINGW32__
174           /* Normalize file name.  */
175           prefix_directory = File_name (prefix_directory).to_string ();
176 #endif /* __MINGW32__ */
177       
178           prefix_relocation (prefix_directory);
179           string bindir = prefix_directory + "/bin";
180           framework_relocation (bindir);
181         }
182       else if (relocate_binary)
183         {
184           string argv0_abs;
185           if (argv0_filename.is_absolute ())
186             {
187               argv0_abs = argv0_filename.to_string ();
188               if (be_verbose_global)
189                 warning (_f ("Relocation: is absolute: argv0=%s", argv0_ptr));
190             }
191           else if (argv0_filename.dir_.length ())
192             {
193               argv0_abs = get_working_directory ()
194                 + "/" + string (argv0_filename.to_string ());
195               if (be_verbose_global)
196                 warning (_f ("Relocation: from cwd: argv0=%s", argv0_ptr));
197             }
198           else
199             {
200               /* Find absolute ARGV0 name, using PATH.  */
201               File_path path;
202               path.parse_path (getenv ("PATH"));
203
204 #ifndef __MINGW32__
205               argv0_abs = path.find (argv0_filename.to_string ());
206 #else /* __MINGW32__ */
207               path.prepend (get_working_directory ());
208               char const *ext[] = {"exe", "", 0 };
209               argv0_abs = path.find (argv0_filename.to_string (), ext);
210 #endif /* __MINGW32__ */
211
212               if (be_verbose_global)
213                 warning (_f ("Relocation: from PATH=%s\nargv0=%s",
214                              path.to_string ().c_str (), argv0_ptr));
215
216               if (argv0_abs.empty ())
217                 programming_error ("cannot find absolute argv0");
218             }
219
220           string bindir = dir_name (argv0_abs);
221           string argv0_prefix = dir_name (bindir);
222           string compile_prefix = dir_name (dir_name (dir_name (lilypond_datadir)));
223           if (argv0_prefix != compile_prefix)
224             {
225               prefix_relocation (argv0_prefix);
226               prefix_directory = argv0_prefix;
227             }
228           if (argv0_prefix != compile_prefix || string (FRAMEWORKDIR) != "..")
229             {
230               framework_relocation (bindir + "/" + FRAMEWORKDIR);
231               prefix_directory = bindir + "/" + FRAMEWORKDIR;
232             }
233         }
234
235       lilypond_datadir = prefix_directory
236         + "/share/lilypond/" TOPLEVEL_VERSION;
237     }
238
239   if (getenv ("LILYPONDPREFIX"))
240     error (_ ("LILYPONDPREFIX is obsolete, use LILYPOND_DATADIR"));
241
242   if (char const *env = getenv ("LILYPOND_DATADIR"))
243     {
244       /* Normalize file name.  */
245       lilypond_datadir = File_name (env).to_string ();
246     }
247
248   /* When running from build dir, a full LILYPOND_DATADIR is set-up at
249      $(OUTBASE)/{share, lib}/lilypond/current.  Configure lily using
250      ./configure --prefix=$(pwd)/out */
251   string build_datadir_current = dir_name (lilypond_datadir) + "/current";
252   if (!is_dir (lilypond_datadir.c_str ())
253       && is_dir (build_datadir_current.c_str ()))
254     lilypond_datadir = build_datadir_current;
255
256
257   lilypond_datadir = File_name (lilypond_datadir).canonicalized().to_string();
258   
259   global_path.append ("");
260
261   /* Adding mf/out make lilypond unchanged source directory, when setting
262      LILYPONDPREFIX to lilypond-x.y.z */
263   char const *suffixes[] = {"ly", "ps", "scm", 0 };
264   
265   vector<string> dirs;
266   for (char const **s = suffixes; *s; s++)
267     {
268       string path = lilypond_datadir + to_string ('/') + string (*s);
269       dirs.push_back (path);
270     }
271   
272   dirs.push_back (lilypond_datadir + "/fonts/otf/");
273   dirs.push_back (lilypond_datadir + "/fonts/type1/");
274   dirs.push_back (lilypond_datadir + "/fonts/svg/");
275   
276   for (vsize i = 0; i < dirs.size (); i++)
277     global_path.prepend (dirs[i]);
278 }
279
280 string
281 expand_environment_variables (string orig)
282 {
283   char const *start_ptr = orig.c_str ();
284   char const *ptr = orig.c_str ();
285   size_t len = orig.length ();
286
287   string out;
288   while (ptr < start_ptr + len)
289     {
290       char const *dollar = strchr (ptr, '$');
291       
292       if (dollar != NULL)
293         {
294           char const *start_var = dollar + 1;
295           char const *end_var = start_var;
296           char const *start_next = end_var;
297           
298           out += string (ptr, dollar - ptr);
299           ptr = dollar;
300
301           if (*start_var == '{')
302             {
303               start_var ++;
304               
305               end_var = strchr (start_var, '}');
306               
307               if (end_var == NULL)
308                 {
309                   end_var = start_var + len;
310                   start_next = end_var;
311                 }
312               else
313                 {
314                   start_next = end_var + 1; 
315                 }
316             }
317           else 
318             {
319               /*
320                 Hmm. what to do for $1 , $~ etc.?
321               */
322               do
323                 {
324                   end_var ++;
325                 }
326               while (isalnum (*end_var) || *end_var == '_');
327               start_next = end_var;
328             }
329
330           if (start_var < end_var)
331             {
332               string var_name (start_var, end_var - start_var);
333               char const *value = getenv (var_name.c_str ());
334               if (value != NULL)
335                 out += string (value);
336
337               ptr = start_next;
338             }
339         }
340       else
341         break;
342
343     }
344
345   out += ptr;
346
347   return out;
348 }
349
350 // Ugh - very inefficient, but safer than fgets.
351 static string
352 read_line (FILE *f)
353 {
354   string out;
355   
356   int c = 0;
357   while ((c = fgetc (f)) != EOF && c != '\n')
358     out += char(c);
359
360   return out;
361 }
362
363 void
364 read_relocation_file (string filename)
365 {
366   if (be_verbose_global)
367     progress_indication (_f ("Relocation file: %s", filename.c_str ())
368                          + "\n");
369       
370   char const *cname = filename.c_str ();
371   FILE *f = fopen (cname, "r");
372   if (!f)
373     error (_f ("cannot open file: `%s'", cname));
374
375   while (!feof (f))
376     {
377       string line = read_line (f);
378       size_t idx = line.find (' ');
379       if (idx == NPOS)
380         continue;
381       
382       string command = line.substr (0, idx);
383       line = line.substr (idx + 1);
384       
385       if (idx == NPOS)
386         continue;
387       idx = line.find ('=');
388
389       string variable = line.substr (0, idx);
390       string value = line.substr (idx + 1);
391
392       value = expand_environment_variables (value);
393
394       if (command == "set")
395         sane_putenv (variable.c_str (), value, true);
396       else if (command == "setdir")
397         set_env_dir (variable.c_str (), value);
398       else if (command == "setfile")
399         set_env_file (variable.c_str (), value);
400       else if (command == "prependdir")
401         prepend_env_path (variable.c_str (), value);
402       else
403         error (_f ("Unknown relocation command %s", command));
404     }
405
406   fclose (f);
407 }
408
409 void
410 read_relocation_dir (string dirname)
411 {
412   if (DIR *dir = opendir (dirname.c_str ()))
413     while (struct dirent *ent = readdir (dir))
414       {
415         File_name name (ent->d_name);
416         if (name.ext_ == "reloc")
417           read_relocation_file (dirname + "/" + name.to_string ());
418       }
419 }