]> git.donarmstrong.com Git - lilypond.git/blob - lily/main.cc
Merge commit 'origin' into beamlets2
[lilypond.git] / lily / main.cc
1 /*
2   main.cc -- implement main () entrypoint.
3
4   source file of the GNU LilyPond music typesetter
5
6   (c) 1997--2008 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
8
9 #include "main.hh"
10
11 #include <cassert>
12 #include <clocale>
13 #include <cstring>
14 #include <cerrno>
15 #include <cstdio>
16 using namespace std;
17
18 #include <unistd.h>
19 #include <sys/types.h>
20 #include "config.hh"
21
22 #if HAVE_GRP_H
23 #include <grp.h>
24 #endif
25 #if HAVE_PWD_H
26 #include <pwd.h>
27 #endif
28 #if HAVE_GETTEXT
29 #include <libintl.h>
30 #endif
31
32 #include "all-font-metrics.hh"
33 #include "file-name.hh"
34 #include "freetype.hh"
35 #include "getopt-long.hh"
36 #include "global-ctor.hh"
37 #include "international.hh"
38 #include "lily-version.hh"
39 #include "misc.hh"
40 #include "output-def.hh"
41 #include "program-option.hh"
42 #include "relocate.hh"
43 #include "string-convert.hh"
44 #include "version.hh"
45 #include "warn.hh"
46
47 /*
48  * Global options that can be overridden through command line.
49  */
50
51 /* Names of header fields to be dumped to a separate file. */
52 vector<string> dump_header_fieldnames_global;
53
54 /* Name of initialisation file. */
55 string init_name_global;
56
57
58 /* Output formats to generate.  */
59 string output_format_global = "";
60
61 /* Current output name. */
62 string output_name_global;
63
64 /* Run in safe mode? */
65 bool be_safe_global = false;
66
67 /* Provide URI links to the original file */
68 bool point_and_click_global = true;
69
70 /* Verbose progress indication? */
71 bool be_verbose_global = false;
72
73 /* Scheme code to execute before parsing, after .scm init.
74    This is where -e arguments are appended to.  */
75 string init_scheme_code_global;
76 string init_scheme_variables_global;
77
78 bool relocate_binary = true;
79
80
81 /*
82  * Miscellaneous global stuff.
83  */
84 File_path global_path;
85
86 /*
87  * File globals.
88  */
89
90 static char const *AUTHORS
91 = "  Han-Wen Nienhuys <hanwen@xs4all.nl>\n"
92   "  Jan Nieuwenhuizen <janneke@gnu.org>\n";
93
94 static char const *PROGRAM_NAME = "lilypond";
95 static char const *PROGRAM_URL = "http://lilypond.org";
96
97 static char const *NOTICE
98 = _i ("This program is free software.  It is covered by the GNU General Public\n"
99       "License and you are welcome to change it and/or distribute copies of it\n"
100       "under certain conditions.  Invoke as `%s --warranty' for more\n"
101       "information.\n");
102
103 static char const *WARRANTY
104 = _i ("    This program is free software; you can redistribute it and/or\n"
105       "modify it under the terms of the GNU General Public License version 2\n"
106       "as published by the Free Software Foundation.\n"
107       "\n"
108       "    This program is distributed in the hope that it will be useful,\n"
109       "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
110       "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n"
111       "General Public License for more details.\n"
112       "\n"
113       "    You should have received a copy of the\n"
114       "GNU General Public License along with this program; if not, write to\n"
115       "the Free Software Foundation, Inc., 59 Temple Place - Suite 330,\n"
116       "Boston, MA 02111-1307, USA.\n");
117
118 /* Where the init files live.  Typically:
119    LILYPOND_DATADIR = /usr/share/lilypond
120 */
121 string lilypond_datadir;
122
123 /* The jail specification: USER, GROUP, JAIL, DIR. */
124 string jail_spec;
125
126 /*  The option parser */
127 static Getopt_long *option_parser = 0;
128
129 /* Internationalisation kludge in two steps:
130  * use _i () to get entry in POT file
131  * call gettext () explicitely for actual "translation"  */
132
133 static Long_option_init options_static[]
134 = {
135   {_i ("SYM[=VAL]"), "define-default", 'd',
136    _i ("set Scheme option SYM to VAL (default: #t).\n"
137        "Use -dhelp for help.")},
138
139   {_i ("EXPR"), "evaluate", 'e', _i ("evaluate scheme code")},
140   /* Bug in option parser: --output =foe is taken as an abbreviation
141      for --output-format.  */
142   {_i ("FORMATs"), "formats", 'f', _i ("dump FORMAT,...  Also as separate options:")},
143   {0, "pdf", 0, _i ("generate PDF (default)")},
144   {0, "png", 0, _i ("generate PNG")},
145   {0, "ps", 0, _i ("generate PostScript")},
146   {0, "help", 'h',  _i ("show this help and exit")},
147   {_i ("FIELD"), "header", 'H',  _i ("dump header field FIELD to file\n"
148                                      "named BASENAME.FIELD")},
149   {_i ("DIR"), "include", 'I',  _i ("add DIR to search path")},
150   {_i ("FILE"), "init", 'i',  _i ("use FILE as init file")},
151 #if HAVE_CHROOT
152   {_i ("USER, GROUP, JAIL, DIR"), "jail", 'j', _i ("chroot to JAIL, become USER:GROUP\n"
153                                                 "and cd into DIR")},
154 #endif
155   {_i ("FILE"), "output", 'o',  _i ("write output to FILE (suffix will be added)")},
156   {0, "relocate", 0, _i ("relocate using directory of lilypond program")},
157   {0, "version", 'v',  _i ("show version number and exit")},
158   {0, "verbose", 'V', _i ("be verbose")},
159   {0, "warranty", 'w',  _i ("show warranty and copyright")},
160   {0, 0, 0, 0}
161 };
162
163 char const *LILYPOND_DATADIR = PACKAGE_DATADIR "/" TOPLEVEL_VERSION;
164
165
166 /* x86 defaults to using 80-bit extended precision arithmetic. This can cause
167    problems because the truncation from 80 bits to 64 bits can occur in
168    unpredictable places. To get around this, we tell the x87 FPU to use only
169    double precision. Note that this is not needed for x86_64 because that uses
170    the SSE unit by default instead of the x87 FPU. */
171 #if ((defined(__x86__) || defined(__i386__)) \
172   && defined(HAVE_FPU_CONTROL_H) && (HAVE_FPU_CONTROL_H == 1))
173
174 #include <fpu_control.h>
175 static void configure_fpu() {
176   fpu_control_t fpu_control = 0x027f;
177   _FPU_SETCW (fpu_control);
178 }
179
180 #else
181
182 static void configure_fpu() {
183 }
184
185 #endif /* defined(__x86__) || defined(__i386__) */
186
187
188 static void
189 env_var_info (FILE *out, char const *key)
190 {
191   if (char const *value = getenv (key))
192     fprintf (out, "%s=\"%s\"\n", key, value);
193 }
194
195 static void
196 dir_info (FILE *out)
197 {
198   fputs ("\n", out);
199   fprintf (out, "LILYPOND_DATADIR=\"%s\"\n", LILYPOND_DATADIR);
200   env_var_info (out, "LILYPONDPREFIX");
201   env_var_info (out, "LILYPOND_DATADIR");
202   fprintf (out, "LOCALEDIR=\"%s\"\n", LOCALEDIR);
203
204   fprintf (out, "\nEffective prefix: \"%s\"\n", lilypond_datadir.c_str ());
205
206   if (relocate_binary)
207     {
208       env_var_info (out, "FONTCONFIG_FILE");
209       env_var_info (out, "FONTCONFIG_PATH");
210       env_var_info (out, "GS_FONTPATH");
211       env_var_info (out, "GS_LIB");
212       env_var_info (out, "GUILE_LOAD_PATH");
213       env_var_info (out, "PANGO_RC_FILE");
214       env_var_info (out, "PANGO_PREFIX");
215       env_var_info (out, "PATH");
216     }
217 }
218
219 static void
220 copyright ()
221 {
222   printf (_f ("Copyright (c) %s by\n%s  and others.",
223               "1996--2008",
224               AUTHORS).c_str ());
225   printf ("\n");
226 }
227
228 static void
229 identify (FILE *out)
230 {
231   fputs (gnu_lilypond_version_string ().c_str (), out);
232   fputs ("\n", out);
233 }
234
235 static void
236 notice ()
237 {
238   identify (stdout);
239   printf ("\n");
240   copyright ();
241   printf ("\n");
242   puts (_f (NOTICE, PROGRAM_NAME).c_str ());
243 }
244
245 LY_DEFINE (ly_usage, "ly:usage",
246            0, 0, 0, (),
247            "Print usage message.")
248 {
249   /* No version number or newline here.  It confuses help2man.  */
250   printf (_f ("Usage: %s [OPTION]... FILE...", PROGRAM_NAME).c_str ());
251   printf ("\n\n");
252   printf (_ ("Typeset music and/or produce MIDI from FILE.").c_str ());
253   printf ("\n\n");
254   printf (_ ("LilyPond produces beautiful music notation.").c_str ());
255   printf ("\n");
256   printf (_f ("For more information, see %s", PROGRAM_URL).c_str ());
257   printf ("\n\n");
258   printf (_ ("Options:").c_str ());
259   printf ("\n");
260   printf (Long_option_init::table_string (options_static).c_str ());
261   printf ("\n");
262   /* Translators, please translate this string as
263          "Report bugs in English via %s",
264      or if there is a LilyPond users list or forum in your language
265          "Report bugs in English via %s or in YOUR_LANG via URI"  */
266   printf (_f ("Report bugs via %s",
267               "http://post.gmane.org/post.php?group=gmane.comp.gnu.lilypond.bugs"
268               ).c_str ());
269   printf ("\n");
270   printf ("\n");
271   return SCM_UNSPECIFIED;
272 }
273
274 static void
275 warranty ()
276 {
277   identify (stdout);
278   printf ("\n");
279   copyright ();
280   printf ("\n");
281   printf (_ (WARRANTY).c_str ());
282 }
283
284 static void
285 prepend_load_path (string dir)
286 {
287   string s = "(set! %load-path (cons \"" + dir + "\" %load-path))";
288   scm_c_eval_string (s.c_str ());
289 }
290
291 void init_global_tweak_registry ();
292 void init_fontconfig ();
293
294 #if HAVE_CHROOT
295 static void
296 do_chroot_jail ()
297 {
298   /* Now we chroot, setuid/setgrp and chdir.  If something goes wrong,
299      we exit (this is a security-sensitive area).  First we split
300      jail_spec into its components, then we retrieve the user/group id
301      (necessarily *before* chroot'ing) and finally we perform the
302      actual actions.  */
303
304   enum Jail
305     {
306       USER_NAME, GROUP_NAME, JAIL, DIR, JAIL_MAX
307     };
308
309   vector<string> components = string_split (jail_spec, ',');
310   if (components.size () != JAIL_MAX)
311     {
312       error (_f ("expected %d arguments with jail, found: %u", JAIL_MAX,
313                  (unsigned) components.size ()));
314       exit (2);
315     }
316
317   /* Hmm.  */
318   errno = 0;
319
320   int uid;
321   if (passwd * passwd = getpwnam (components[USER_NAME].c_str ()))
322     uid = passwd->pw_uid;
323   else
324     {
325       if (errno == 0)
326         error (_f ("no such user: %s", components[USER_NAME]));
327       else
328         error (_f ("cannot get user id from user name: %s: %s",
329                    components[USER_NAME],
330                    strerror (errno)));
331       exit (3);
332     }
333
334   /* Hmm.  */
335   errno = 0;
336
337   int gid;
338   if (group * group = getgrnam (components[GROUP_NAME].c_str ()))
339     gid = group->gr_gid;
340   else
341     {
342       if (errno == 0)
343         error (_f ("no such group: %s", components[GROUP_NAME]));
344       else
345         error (_f ("cannot get group id from group name: %s: %s",
346                    components[GROUP_NAME],
347                    strerror (errno)));
348       exit (3);
349     }
350
351   if (chroot (components[JAIL].c_str ()))
352     {
353       error (_f ("cannot chroot to: %s: %s", components[JAIL],
354                  strerror (errno)));
355       exit (3);
356     }
357
358   if (setgid (gid))
359     {
360       error (_f ("cannot change group id to: %d: %s", gid, strerror (errno)));
361       exit (3);
362     }
363
364   if (setuid (uid))
365     {
366       error (_f ("cannot change user id to: %d: %s", uid, strerror (errno)));
367       exit (3);
368     }
369
370   if (chdir (components[DIR].c_str ()))
371     {
372       error (_f ("cannot change working directory to: %s: %s", components[DIR],
373                  strerror (errno)));
374       exit (3);
375     }
376 }
377 #endif
378
379 static void
380 main_with_guile (void *, int, char **)
381 {
382   /* Engravers use lily.scm contents, need to make Guile find it.
383      Prepend onto GUILE %load-path, very ugh. */
384
385   prepend_load_path (lilypond_datadir);
386   prepend_load_path (lilypond_datadir + "/scm");
387
388   if (be_verbose_global)
389     dir_info (stderr);
390
391   init_scheme_variables_global = "(list " + init_scheme_variables_global + ")";
392   init_scheme_code_global = "(begin " + init_scheme_code_global + ")";
393
394   ly_c_init_guile ();
395   call_constructors ();
396   init_fontconfig ();
397
398   init_freetype ();
399   ly_reset_all_fonts ();
400
401   /* We accept multiple independent music files on the command line to
402      reduce compile time when processing lots of small files.
403      Starting the GUILE engine is very time consuming.  */
404
405   SCM files = SCM_EOL;
406   SCM *tail = &files;
407   while (char const *arg = option_parser->get_next_arg ())
408     {
409       *tail = scm_cons (scm_from_locale_string (arg), SCM_EOL);
410       tail = SCM_CDRLOC (*tail);
411     }
412   
413   delete option_parser;
414   option_parser = 0;
415
416 #if HAVE_CHROOT
417   if (!jail_spec.empty ())
418     do_chroot_jail ();
419 #endif
420
421   SCM result = scm_call_1 (ly_lily_module_constant ("lilypond-main"), files);
422   (void) result;
423
424   /* Unreachable.  */
425   exit (0);
426 }
427
428 static void
429 setup_localisation ()
430 {
431 #if HAVE_GETTEXT
432   /* Enable locales */
433   setlocale (LC_ALL, "");
434
435   /* FIXME: check if this is still true.
436      Disable localisation of float values. */
437   setlocale (LC_NUMERIC, "C");
438
439   string localedir = LOCALEDIR;
440   if (char const *env = getenv ("LILYPOND_LOCALEDIR"))
441     localedir = env;
442
443   bindtextdomain ("lilypond", localedir.c_str ());
444   textdomain ("lilypond");
445 #endif
446 }
447
448 static void
449 add_output_format (string format)
450 {
451   if (output_format_global != "")
452     output_format_global += ",";
453   output_format_global += format;
454 }
455
456 static void
457 parse_argv (int argc, char **argv)
458 {
459   bool show_help = false;
460   option_parser = new Getopt_long (argc, argv, options_static);
461   while (Long_option_init const *opt = (*option_parser) ())
462     {
463       switch (opt->shortname_char_)
464         {
465         case 0:
466           if (string (opt->longname_str0_) == "pdf"
467               || string (opt->longname_str0_) == "png"
468               || string (opt->longname_str0_) == "ps")
469             add_output_format (opt->longname_str0_);
470           else if (string (opt->longname_str0_) == "relocate")
471             relocate_binary = true;
472           break;
473
474         case 'd':
475           {
476             string arg (option_parser->optional_argument_str0_);
477             ssize eq = arg.find ('=');
478
479             string key = arg;
480             string val = "#t";
481
482             if (eq != NPOS)
483               {
484                 key = arg.substr (0, eq);
485                 val = arg.substr (eq + 1, arg.length () - 1);
486               }
487
488             init_scheme_variables_global
489               += "(cons \'" + key + " '" + val + ")\n";
490           }
491           break;
492
493         case 'v':
494           notice ();
495           exit (0);
496           break;
497         case 'o':
498           {
499             string s = option_parser->optional_argument_str0_;
500             File_name file_name (s);
501             output_name_global = file_name.to_string ();
502           }
503           break;
504         case 'j':
505           jail_spec = option_parser->optional_argument_str0_;
506           break;
507           
508         case 'e':
509           init_scheme_code_global += option_parser->optional_argument_str0_ + string (" ");
510           break;
511         case 'w':
512           warranty ();
513           exit (0);
514           break;
515
516         case 'f':
517           {
518             vector<string> components
519               = string_split (option_parser->optional_argument_str0_, ',');
520             for (vsize i = 0; i < components.size (); i++)
521               add_output_format (components[i]);
522           }
523           break;
524
525         case 'H':
526           dump_header_fieldnames_global
527             .push_back (option_parser->optional_argument_str0_);
528           break;
529         case 'I':
530           global_path.append (option_parser->optional_argument_str0_);
531           break;
532         case 'i':
533           init_name_global = option_parser->optional_argument_str0_;
534           break;
535         case 'h':
536           show_help = true;
537           break;
538         case 'V':
539           be_verbose_global = true;
540           break;
541         default:
542           programming_error (to_string ("unhandled short option: %c",
543                                         opt->shortname_char_));
544           assert (false);
545           break;
546         }
547     }
548
549   if (output_format_global == "")
550     output_format_global = "pdf";
551
552   if (show_help)
553     {
554       ly_usage ();
555       if (be_verbose_global)
556         dir_info (stdout);
557       exit (0);
558     }
559 }
560
561 void
562 setup_guile_env ()
563 {
564   const char *yield = getenv ("LILYPOND_GC_YIELD");
565   bool overwrite = true;
566   if (!yield)
567     {
568       yield = "65";
569       overwrite = false;
570     }
571
572   sane_putenv ("GUILE_MIN_YIELD_1", yield, overwrite);
573   sane_putenv ("GUILE_MIN_YIELD_2", yield, overwrite);
574   sane_putenv ("GUILE_MIN_YIELD_MALLOC", yield, overwrite);
575
576
577   sane_putenv ("GUILE_INIT_SEGMENT_SIZE_1",
578                "10485760", overwrite);
579   sane_putenv ("GUILE_MAX_SEGMENT_SIZE",
580                "104857600", overwrite);
581 }
582
583 vector<string> start_environment_global;
584  
585 int
586 main (int argc, char **argv, char **envp)
587 {
588   configure_fpu();
589
590   for (char **p = envp; *p; p++)
591     start_environment_global.push_back(*p);
592   
593   if (getenv ("LILYPOND_VERBOSE"))
594     be_verbose_global = true;
595
596   setup_localisation ();
597   parse_argv (argc, argv);
598   if (isatty (STDIN_FILENO))
599     identify (stderr);
600
601   setup_paths (argv[0]);
602   setup_guile_env ();
603
604 #if 0
605   /* Debugging aid.  */
606   try
607     {
608       scm_boot_guile (argc, argv, main_with_guile, 0);
609     }
610   catch (exception e)
611     {
612       error (_f ("exception caught: %s", e.what ()));
613     };
614 #else
615   scm_boot_guile (argc, argv, main_with_guile, 0);
616 #endif
617         
618   /* Only reachable if GUILE exits.  That is an error.  */
619   return 1;
620 }