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