]> 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--2009 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 () explicitly 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
176 configure_fpu ()
177 {
178   fpu_control_t fpu_control = 0x027f;
179   _FPU_SETCW (fpu_control);
180 }
181
182 #else
183
184 static void
185 configure_fpu ()
186 {
187 }
188
189 #endif /* defined(__x86__) || defined(__i386__) */
190
191
192 static void
193 env_var_info (FILE *out, char const *key)
194 {
195   if (char const *value = getenv (key))
196     fprintf (out, "%s=\"%s\"\n", key, value);
197 }
198
199 static void
200 dir_info (FILE *out)
201 {
202   fputs ("\n", out);
203   fprintf (out, "LILYPOND_DATADIR=\"%s\"\n", LILYPOND_DATADIR);
204   env_var_info (out, "LILYPONDPREFIX");
205   env_var_info (out, "LILYPOND_DATADIR");
206   fprintf (out, "LOCALEDIR=\"%s\"\n", LOCALEDIR);
207
208   fprintf (out, "\nEffective prefix: \"%s\"\n", lilypond_datadir.c_str ());
209
210   if (relocate_binary)
211     {
212       env_var_info (out, "FONTCONFIG_FILE");
213       env_var_info (out, "FONTCONFIG_PATH");
214       env_var_info (out, "GS_FONTPATH");
215       env_var_info (out, "GS_LIB");
216       env_var_info (out, "GUILE_LOAD_PATH");
217       env_var_info (out, "PANGO_RC_FILE");
218       env_var_info (out, "PANGO_PREFIX");
219       env_var_info (out, "PATH");
220     }
221 }
222
223 static void
224 copyright ()
225 {
226   /* Do not update the copyright years here, run grand-replace.sh.  */
227   printf ("%s", (_f ("Copyright (c) %s by\n%s  and others.", "1996--2009",
228                      AUTHORS).c_str ()));
229   printf ("\n");
230 }
231
232 static void
233 identify (FILE *out)
234 {
235   fputs (gnu_lilypond_version_string ().c_str (), out);
236   fputs ("\n", out);
237 }
238
239 static void
240 notice ()
241 {
242   identify (stdout);
243   printf ("\n");
244   copyright ();
245   printf ("\n");
246   puts (_f (NOTICE, PROGRAM_NAME).c_str ());
247 }
248
249 LY_DEFINE (ly_usage, "ly:usage",
250            0, 0, 0, (),
251            "Print usage message.")
252 {
253   /* No version number or newline here.  It confuses help2man.  */
254   printf ("%s", (_f ("Usage: %s [OPTION]... FILE...", PROGRAM_NAME).c_str ()));
255   printf ("\n\n");
256   printf ("%s", (_ ("Typeset music and/or produce MIDI from FILE.").c_str ()));
257   printf ("\n\n");
258   printf ("%s", (_ ("LilyPond produces beautiful music notation.").c_str ()));
259   printf ("\n");
260   printf ("%s", (_f ("For more information, see %s", PROGRAM_URL).c_str ()));
261   printf ("\n\n");
262   printf ("%s", (_ ("Options:").c_str ()));
263   printf ("\n");
264   printf ("%s", Long_option_init::table_string (options_static).c_str ());
265   printf ("\n");
266   /* Translators, please translate this string as
267      "Report bugs in English via %s",
268      or if there is a LilyPond users list or forum in your language
269      "Report bugs in English via %s or in YOUR_LANG via URI"  */
270   printf ("%s", (_f ("Report bugs via %s",
271                      "http://post.gmane.org/post.php?group=gmane.comp.gnu.lilypond.bugs"
272                      ).c_str ()));
273   printf ("\n");
274   printf ("\n");
275   return SCM_UNSPECIFIED;
276 }
277
278 static void
279 warranty ()
280 {
281   identify (stdout);
282   printf ("\n");
283   copyright ();
284   printf ("\n");
285   printf ("%s", (_ (WARRANTY).c_str ()));
286 }
287
288 static void
289 prepend_load_path (string dir)
290 {
291   string s = "(set! %load-path (cons \"" + dir + "\" %load-path))";
292   scm_c_eval_string (s.c_str ());
293 }
294
295 void init_global_tweak_registry ();
296 void init_fontconfig ();
297
298 #if HAVE_CHROOT
299 static void
300 do_chroot_jail ()
301 {
302   /* Now we chroot, setuid/setgrp and chdir.  If something goes wrong,
303      we exit (this is a security-sensitive area).  First we split
304      jail_spec into its components, then we retrieve the user/group id
305      (necessarily *before* chroot'ing) and finally we perform the
306      actual actions.  */
307
308   enum Jail
309     {
310       USER_NAME, GROUP_NAME, JAIL, DIR, JAIL_MAX
311     };
312
313   vector<string> components = string_split (jail_spec, ',');
314   if (components.size () != JAIL_MAX)
315     {
316       error (_f ("expected %d arguments with jail, found: %u", JAIL_MAX,
317                  (unsigned) components.size ()));
318       exit (2);
319     }
320
321   /* Hmm.  */
322   errno = 0;
323
324   int uid;
325   if (passwd * passwd = getpwnam (components[USER_NAME].c_str ()))
326     uid = passwd->pw_uid;
327   else
328     {
329       if (errno == 0)
330         error (_f ("no such user: %s", components[USER_NAME]));
331       else
332         error (_f ("cannot get user id from user name: %s: %s",
333                    components[USER_NAME],
334                    strerror (errno)));
335       exit (3);
336     }
337
338   /* Hmm.  */
339   errno = 0;
340
341   int gid;
342   if (group * group = getgrnam (components[GROUP_NAME].c_str ()))
343     gid = group->gr_gid;
344   else
345     {
346       if (errno == 0)
347         error (_f ("no such group: %s", components[GROUP_NAME]));
348       else
349         error (_f ("cannot get group id from group name: %s: %s",
350                    components[GROUP_NAME],
351                    strerror (errno)));
352       exit (3);
353     }
354
355   if (chroot (components[JAIL].c_str ()))
356     {
357       error (_f ("cannot chroot to: %s: %s", components[JAIL],
358                  strerror (errno)));
359       exit (3);
360     }
361
362   if (setgid (gid))
363     {
364       error (_f ("cannot change group id to: %d: %s", gid, strerror (errno)));
365       exit (3);
366     }
367
368   if (setuid (uid))
369     {
370       error (_f ("cannot change user id to: %d: %s", uid, strerror (errno)));
371       exit (3);
372     }
373
374   if (chdir (components[DIR].c_str ()))
375     {
376       error (_f ("cannot change working directory to: %s: %s", components[DIR],
377                  strerror (errno)));
378       exit (3);
379     }
380 }
381 #endif
382
383 static void
384 main_with_guile (void *, int, char **)
385 {
386   /* Engravers use lily.scm contents, need to make Guile find it.
387      Prepend onto GUILE %load-path, very ugh. */
388
389   prepend_load_path (lilypond_datadir);
390   prepend_load_path (lilypond_datadir + "/scm");
391
392   if (be_verbose_global)
393     dir_info (stderr);
394
395   init_scheme_variables_global = "(list " + init_scheme_variables_global + ")";
396   init_scheme_code_global = "(begin " + init_scheme_code_global + ")";
397
398   ly_c_init_guile ();
399   call_constructors ();
400   init_fontconfig ();
401
402   init_freetype ();
403   ly_reset_all_fonts ();
404
405   /* We accept multiple independent music files on the command line to
406      reduce compile time when processing lots of small files.
407      Starting the GUILE engine is very time consuming.  */
408
409   SCM files = SCM_EOL;
410   SCM *tail = &files;
411   while (char const *arg = option_parser->get_next_arg ())
412     {
413       *tail = scm_cons (scm_from_locale_string (arg), SCM_EOL);
414       tail = SCM_CDRLOC (*tail);
415     }
416
417   delete option_parser;
418   option_parser = 0;
419
420 #if HAVE_CHROOT
421   if (!jail_spec.empty ())
422     do_chroot_jail ();
423 #endif
424
425   SCM result = scm_call_1 (ly_lily_module_constant ("lilypond-main"), files);
426   (void) result;
427
428   /* Unreachable.  */
429   exit (0);
430 }
431
432 static void
433 setup_localisation ()
434 {
435 #if HAVE_GETTEXT
436   /* Enable locales */
437   setlocale (LC_ALL, "");
438
439   /* FIXME: check if this is still true.
440      Disable localisation of float values. */
441   setlocale (LC_NUMERIC, "C");
442
443   string localedir = LOCALEDIR;
444   if (char const *env = getenv ("LILYPOND_LOCALEDIR"))
445     localedir = env;
446
447   bindtextdomain ("lilypond", localedir.c_str ());
448   textdomain ("lilypond");
449 #endif
450 }
451
452 static void
453 add_output_format (string format)
454 {
455   if (output_format_global != "")
456     output_format_global += ",";
457   output_format_global += format;
458 }
459
460 static void
461 parse_argv (int argc, char **argv)
462 {
463   bool show_help = false;
464   option_parser = new Getopt_long (argc, argv, options_static);
465   while (Long_option_init const *opt = (*option_parser) ())
466     {
467       switch (opt->shortname_char_)
468         {
469         case 0:
470           if (string (opt->longname_str0_) == "pdf"
471               || string (opt->longname_str0_) == "png"
472               || string (opt->longname_str0_) == "ps")
473             add_output_format (opt->longname_str0_);
474           else if (string (opt->longname_str0_) == "relocate")
475             relocate_binary = true;
476           break;
477
478         case 'd':
479           {
480             string arg (option_parser->optional_argument_str0_);
481             ssize eq = arg.find ('=');
482
483             string key = arg;
484             string val = "#t";
485
486             if (eq != NPOS)
487               {
488                 key = arg.substr (0, eq);
489                 val = arg.substr (eq + 1, arg.length () - 1);
490               }
491
492             init_scheme_variables_global
493               += "(cons \'" + key + " '" + val + ")\n";
494           }
495           break;
496
497         case 'v':
498           notice ();
499           exit (0);
500           break;
501         case 'o':
502           {
503             string s = option_parser->optional_argument_str0_;
504             File_name file_name (s);
505             output_name_global = file_name.to_string ();
506           }
507           break;
508         case 'j':
509           jail_spec = option_parser->optional_argument_str0_;
510           break;
511
512         case 'e':
513           init_scheme_code_global
514             += 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       ly_usage ();
560       if (be_verbose_global)
561         dir_info (stdout);
562       exit (0);
563     }
564 }
565
566 void
567 setup_guile_env ()
568 {
569   char const *yield = getenv ("LILYPOND_GC_YIELD");
570   bool overwrite = true;
571   if (!yield)
572     {
573       yield = "65";
574       overwrite = false;
575     }
576
577   sane_putenv ("GUILE_MIN_YIELD_1", yield, overwrite);
578   sane_putenv ("GUILE_MIN_YIELD_2", yield, overwrite);
579   sane_putenv ("GUILE_MIN_YIELD_MALLOC", yield, overwrite);
580
581
582   sane_putenv ("GUILE_INIT_SEGMENT_SIZE_1",
583                "10485760", overwrite);
584   sane_putenv ("GUILE_MAX_SEGMENT_SIZE",
585                "104857600", overwrite);
586 }
587
588 vector<string> start_environment_global;
589
590 int
591 main (int argc, char **argv, char **envp)
592 {
593   configure_fpu();
594
595   for (char **p = envp; *p; p++)
596     start_environment_global.push_back(*p);
597
598   if (getenv ("LILYPOND_VERBOSE"))
599     be_verbose_global = true;
600
601   setup_localisation ();
602   parse_argv (argc, argv);
603   if (isatty (STDIN_FILENO))
604     identify (stderr);
605
606   setup_paths (argv[0]);
607   setup_guile_env ();
608
609 #if 0
610   /* Debugging aid.  */
611   try
612     {
613       scm_boot_guile (argc, argv, main_with_guile, 0);
614     }
615   catch (exception e)
616     {
617       error (_f ("exception caught: %s", e.what ()));
618     };
619 #else
620   scm_boot_guile (argc, argv, main_with_guile, 0);
621 #endif
622
623   /* Only reachable if GUILE exits.  That is an error.  */
624   return 1;
625 }