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