property-init.ly: Organize, cleanup.
[lilypond/mpolesky.git] / lily / main.cc
blob8141a87cd1864f33f62aea45351c1c299a245f57
1 /*
2 main.cc -- implement main () entrypoint.
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2009 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
9 #include "main.hh"
11 #include <cassert>
12 #include <clocale>
13 #include <cstring>
14 #include <cerrno>
15 #include <cstdio>
16 using namespace std;
18 #include <unistd.h>
19 #include <sys/types.h>
20 #include "config.hh"
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
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"
48 * Global options that can be overridden through command line.
51 /* Names of header fields to be dumped to a separate file. */
52 vector<string> dump_header_fieldnames_global;
54 /* Name of initialisation file. */
55 string init_name_global;
58 /* Output formats to generate. */
59 string output_format_global = "";
61 /* Current output name. */
62 string output_name_global;
64 /* Run in safe mode? */
65 bool be_safe_global = false;
67 /* Provide URI links to the original file */
68 bool point_and_click_global = true;
70 /* Verbose progress indication? */
71 bool be_verbose_global = false;
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;
78 bool relocate_binary = true;
82 * Miscellaneous global stuff.
84 File_path global_path;
87 * File globals.
90 static char const *AUTHORS
91 = " Han-Wen Nienhuys <hanwen@xs4all.nl>\n"
92 " Jan Nieuwenhuizen <janneke@gnu.org>\n";
94 static char const *PROGRAM_NAME = "lilypond";
95 static char const *PROGRAM_URL = "http://lilypond.org";
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");
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");
118 /* Where the init files live. Typically:
119 LILYPOND_DATADIR = /usr/share/lilypond
121 string lilypond_datadir;
123 /* The jail specification: USER, GROUP, JAIL, DIR. */
124 string jail_spec;
126 /* The option parser */
127 static Getopt_long *option_parser = 0;
129 /* Internationalisation kludge in two steps:
130 * use _i () to get entry in POT file
131 * call gettext () explicitly for actual "translation" */
133 static Long_option_init options_static[]
135 {_i ("SYM[=VAL]"), "define-default", 'd',
136 _i ("set Scheme option SYM to VAL (default: #t).\n"
137 "Use -dhelp for help.")},
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}
163 char const *LILYPOND_DATADIR = PACKAGE_DATADIR "/" TOPLEVEL_VERSION;
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))
174 #include <fpu_control.h>
175 static void
176 configure_fpu ()
178 fpu_control_t fpu_control = 0x027f;
179 _FPU_SETCW (fpu_control);
182 #else
184 static void
185 configure_fpu ()
189 #endif /* defined(__x86__) || defined(__i386__) */
192 static void
193 env_var_info (FILE *out, char const *key)
195 if (char const *value = getenv (key))
196 fprintf (out, "%s=\"%s\"\n", key, value);
199 static void
200 dir_info (FILE *out)
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);
208 fprintf (out, "\nEffective prefix: \"%s\"\n", lilypond_datadir.c_str ());
210 if (relocate_binary)
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");
223 static void
224 copyright ()
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");
232 static void
233 identify (FILE *out)
235 fputs (gnu_lilypond_version_string ().c_str (), out);
236 fputs ("\n", out);
239 static void
240 notice ()
242 identify (stdout);
243 printf ("\n");
244 copyright ();
245 printf ("\n");
246 puts (_f (NOTICE, PROGRAM_NAME).c_str ());
249 LY_DEFINE (ly_usage, "ly:usage",
250 0, 0, 0, (),
251 "Print usage message.")
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;
278 static void
279 warranty ()
281 identify (stdout);
282 printf ("\n");
283 copyright ();
284 printf ("\n");
285 printf ("%s", (_ (WARRANTY).c_str ()));
288 static void
289 prepend_load_path (string dir)
291 string s = "(set! %load-path (cons \"" + dir + "\" %load-path))";
292 scm_c_eval_string (s.c_str ());
295 void init_global_tweak_registry ();
296 void init_fontconfig ();
298 #if HAVE_CHROOT
299 static void
300 do_chroot_jail ()
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. */
308 enum Jail
310 USER_NAME, GROUP_NAME, JAIL, DIR, JAIL_MAX
313 vector<string> components = string_split (jail_spec, ',');
314 if (components.size () != JAIL_MAX)
316 error (_f ("expected %d arguments with jail, found: %u", JAIL_MAX,
317 (unsigned) components.size ()));
318 exit (2);
321 /* Hmm. */
322 errno = 0;
324 int uid;
325 if (passwd * passwd = getpwnam (components[USER_NAME].c_str ()))
326 uid = passwd->pw_uid;
327 else
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);
338 /* Hmm. */
339 errno = 0;
341 int gid;
342 if (group * group = getgrnam (components[GROUP_NAME].c_str ()))
343 gid = group->gr_gid;
344 else
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);
355 if (chroot (components[JAIL].c_str ()))
357 error (_f ("cannot chroot to: %s: %s", components[JAIL],
358 strerror (errno)));
359 exit (3);
362 if (setgid (gid))
364 error (_f ("cannot change group id to: %d: %s", gid, strerror (errno)));
365 exit (3);
368 if (setuid (uid))
370 error (_f ("cannot change user id to: %d: %s", uid, strerror (errno)));
371 exit (3);
374 if (chdir (components[DIR].c_str ()))
376 error (_f ("cannot change working directory to: %s: %s", components[DIR],
377 strerror (errno)));
378 exit (3);
381 #endif
383 static void
384 main_with_guile (void *, int, char **)
386 /* Engravers use lily.scm contents, need to make Guile find it.
387 Prepend onto GUILE %load-path, very ugh. */
389 prepend_load_path (lilypond_datadir);
390 prepend_load_path (lilypond_datadir + "/scm");
392 if (be_verbose_global)
393 dir_info (stderr);
395 init_scheme_variables_global = "(list " + init_scheme_variables_global + ")";
396 init_scheme_code_global = "(begin " + init_scheme_code_global + ")";
398 ly_c_init_guile ();
399 call_constructors ();
400 init_fontconfig ();
402 init_freetype ();
403 ly_reset_all_fonts ();
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. */
409 SCM files = SCM_EOL;
410 SCM *tail = &files;
411 while (char const *arg = option_parser->get_next_arg ())
413 *tail = scm_cons (scm_from_locale_string (arg), SCM_EOL);
414 tail = SCM_CDRLOC (*tail);
417 delete option_parser;
418 option_parser = 0;
420 #if HAVE_CHROOT
421 if (!jail_spec.empty ())
422 do_chroot_jail ();
423 #endif
425 SCM result = scm_call_1 (ly_lily_module_constant ("lilypond-main"), files);
426 (void) result;
428 /* Unreachable. */
429 exit (0);
432 static void
433 setup_localisation ()
435 #if HAVE_GETTEXT
436 /* Enable locales */
437 setlocale (LC_ALL, "");
439 /* FIXME: check if this is still true.
440 Disable localisation of float values. */
441 setlocale (LC_NUMERIC, "C");
443 string localedir = LOCALEDIR;
444 if (char const *env = getenv ("LILYPOND_LOCALEDIR"))
445 localedir = env;
447 bindtextdomain ("lilypond", localedir.c_str ());
448 textdomain ("lilypond");
449 #endif
452 static void
453 add_output_format (string format)
455 if (output_format_global != "")
456 output_format_global += ",";
457 output_format_global += format;
460 static void
461 parse_argv (int argc, char **argv)
463 bool show_help = false;
464 option_parser = new Getopt_long (argc, argv, options_static);
465 while (Long_option_init const *opt = (*option_parser) ())
467 switch (opt->shortname_char_)
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;
478 case 'd':
480 string arg (option_parser->optional_argument_str0_);
481 ssize eq = arg.find ('=');
483 string key = arg;
484 string val = "#t";
486 if (eq != NPOS)
488 key = arg.substr (0, eq);
489 val = arg.substr (eq + 1, arg.length () - 1);
492 init_scheme_variables_global
493 += "(cons \'" + key + " '" + val + ")\n";
495 break;
497 case 'v':
498 notice ();
499 exit (0);
500 break;
501 case 'o':
503 string s = option_parser->optional_argument_str0_;
504 File_name file_name (s);
505 output_name_global = file_name.to_string ();
507 break;
508 case 'j':
509 jail_spec = option_parser->optional_argument_str0_;
510 break;
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;
521 case 'f':
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]);
528 break;
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;
554 if (output_format_global == "")
555 output_format_global = "pdf";
557 if (show_help)
559 ly_usage ();
560 if (be_verbose_global)
561 dir_info (stdout);
562 exit (0);
566 void
567 setup_guile_env ()
569 char const *yield = getenv ("LILYPOND_GC_YIELD");
570 bool overwrite = true;
571 if (!yield)
573 yield = "65";
574 overwrite = false;
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);
582 sane_putenv ("GUILE_INIT_SEGMENT_SIZE_1",
583 "10485760", overwrite);
584 sane_putenv ("GUILE_MAX_SEGMENT_SIZE",
585 "104857600", overwrite);
588 vector<string> start_environment_global;
591 main (int argc, char **argv, char **envp)
593 configure_fpu();
595 for (char **p = envp; *p; p++)
596 start_environment_global.push_back(*p);
598 if (getenv ("LILYPOND_VERBOSE"))
599 be_verbose_global = true;
601 setup_localisation ();
602 parse_argv (argc, argv);
603 if (isatty (STDIN_FILENO))
604 identify (stderr);
606 setup_paths (argv[0]);
607 setup_guile_env ();
609 #if 0
610 /* Debugging aid. */
613 scm_boot_guile (argc, argv, main_with_guile, 0);
615 catch (exception e)
617 error (_f ("exception caught: %s", e.what ()));
619 #else
620 scm_boot_guile (argc, argv, main_with_guile, 0);
621 #endif
623 /* Only reachable if GUILE exits. That is an error. */
624 return 1;