2 main.cc -- implement main () entrypoint.
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2008 Han-Wen Nienhuys <hanwen@xs4all.nl>
18 #include <sys/types.h>
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"
39 #include "output-def.hh"
40 #include "program-option.hh"
41 #include "relocate.hh"
42 #include "string-convert.hh"
47 * Global options that can be overridden through command line.
50 /* Names of header fields to be dumped to a separate file. */
51 vector<string> dump_header_fieldnames_global;
53 /* Name of initialisation file. */
54 string init_name_global;
57 /* Output formats to generate. */
58 string output_format_global = "";
60 /* Current output name. */
61 string output_name_global;
63 /* Run in safe mode? */
64 bool be_safe_global = false;
66 /* Provide URI links to the original file */
67 bool point_and_click_global = true;
69 /* Verbose progress indication? */
70 bool be_verbose_global = false;
72 /* Scheme code to execute before parsing, after .scm init.
73 This is where -e arguments are appended to. */
74 string init_scheme_code_global;
75 string init_scheme_variables_global;
77 bool relocate_binary = true;
81 * Miscellaneous global stuff.
83 File_path global_path;
89 static char const *AUTHORS
90 = " Han-Wen Nienhuys <hanwen@xs4all.nl>\n"
91 " Jan Nieuwenhuizen <janneke@gnu.org>\n";
93 static char const *PROGRAM_NAME = "lilypond";
94 static char const *PROGRAM_URL = "http://lilypond.org";
96 static char const *NOTICE
97 = _i ("This program is free software. It is covered by the GNU General Public\n"
98 "License and you are welcome to change it and/or distribute copies of it\n"
99 "under certain conditions. Invoke as `%s --warranty' for more\n"
102 static char const *WARRANTY
103 = _i (" This program is free software; you can redistribute it and/or\n"
104 "modify it under the terms of the GNU General Public License version 2\n"
105 "as published by the Free Software Foundation.\n"
107 " This program is distributed in the hope that it will be useful,\n"
108 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
109 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n"
110 "General Public License for more details.\n"
112 " You should have received a copy of the\n"
113 "GNU General Public License along with this program; if not, write to\n"
114 "the Free Software Foundation, Inc., 59 Temple Place - Suite 330,\n"
115 "Boston, MA 02111-1307, USA.\n");
117 /* Where the init files live. Typically:
118 LILYPOND_DATADIR = /usr/share/lilypond
120 string lilypond_datadir;
122 /* The jail specification: USER, GROUP, JAIL, DIR. */
125 /* The option parser */
126 static Getopt_long *option_parser = 0;
128 /* Internationalisation kludge in two steps:
129 * use _i () to get entry in POT file
130 * call gettext () explicitely for actual "translation" */
132 static Long_option_init options_static[]
134 {_i ("SYM[=VAL]"), "define-default", 'd',
135 _i ("set Scheme option SYM to VAL (default: #t).\n"
136 "Use -dhelp for help.")},
138 {_i ("EXPR"), "evaluate", 'e', _i ("evaluate scheme code")},
139 /* Bug in option parser: --output =foe is taken as an abbreviation
140 for --output-format. */
141 {_i ("FORMATs"), "formats", 'f', _i ("dump FORMAT,... Also as separate options:")},
142 {0, "pdf", 0, _i ("generate PDF (default)")},
143 {0, "png", 0, _i ("generate PNG")},
144 {0, "ps", 0, _i ("generate PostScript")},
145 {0, "help", 'h', _i ("show this help and exit")},
146 {_i ("FIELD"), "header", 'H', _i ("dump header field FIELD to file\n"
147 "named BASENAME.FIELD")},
148 {_i ("DIR"), "include", 'I', _i ("add DIR to search path")},
149 {_i ("FILE"), "init", 'i', _i ("use FILE as init file")},
151 {_i ("USER, GROUP, JAIL, DIR"), "jail", 'j', _i ("chroot to JAIL, become USER:GROUP\n"
154 {_i ("FILE"), "output", 'o', _i ("write output to FILE (suffix will be added)")},
155 {0, "relocate", 0, _i ("relocate using directory of lilypond program")},
156 {0, "version", 'v', _i ("show version number and exit")},
157 {0, "verbose", 'V', _i ("be verbose")},
158 {0, "warranty", 'w', _i ("show warranty and copyright")},
162 char const *LILYPOND_DATADIR = PACKAGE_DATADIR "/" TOPLEVEL_VERSION;
165 /* x86 defaults to using 80-bit extended precision arithmetic. This can cause
166 problems because the truncation from 80 bits to 64 bits can occur in
167 unpredictable places. To get around this, we tell the x87 FPU to use only
168 double precision. Note that this is not needed for x86_64 because that uses
169 the SSE unit by default instead of the x87 FPU. */
170 #if ((defined(__x86__) || defined(__i386__)) \
171 && defined(HAVE_FPU_CONTROL_H) && (HAVE_FPU_CONTROL_H == 1))
173 #include <fpu_control.h>
174 static void configure_fpu() {
175 fpu_control_t fpu_control = 0x027f;
176 _FPU_SETCW (fpu_control);
181 static void configure_fpu() {
184 #endif /* defined(__x86__) || defined(__i386__) */
188 env_var_info (FILE *out, char const *key)
190 if (char const *value = getenv (key))
191 fprintf (out, "%s=\"%s\"\n", key, value);
198 fprintf (out, "LILYPOND_DATADIR=\"%s\"\n", LILYPOND_DATADIR);
199 env_var_info (out, "LILYPONDPREFIX");
200 env_var_info (out, "LILYPOND_DATADIR");
201 fprintf (out, "LOCALEDIR=\"%s\"\n", LOCALEDIR);
203 fprintf (out, "\nEffective prefix: \"%s\"\n", lilypond_datadir.c_str ());
207 env_var_info (out, "FONTCONFIG_FILE");
208 env_var_info (out, "FONTCONFIG_PATH");
209 env_var_info (out, "GS_FONTPATH");
210 env_var_info (out, "GS_LIB");
211 env_var_info (out, "GUILE_LOAD_PATH");
212 env_var_info (out, "PANGO_RC_FILE");
213 env_var_info (out, "PANGO_PREFIX");
214 env_var_info (out, "PATH");
221 printf (_f ("Copyright (c) %s by\n%s and others.",
230 fputs (gnu_lilypond_version_string ().c_str (), out);
241 puts (_f (NOTICE, PROGRAM_NAME).c_str ());
244 LY_DEFINE (ly_usage, "ly:usage",
246 "Print usage message.")
248 /* No version number or newline here. It confuses help2man. */
249 printf (_f ("Usage: %s [OPTION]... FILE...", PROGRAM_NAME).c_str ());
251 printf (_ ("Typeset music and/or produce MIDI from FILE.").c_str ());
253 printf (_ ("LilyPond produces beautiful music notation.").c_str ());
255 printf (_f ("For more information, see %s", PROGRAM_URL).c_str ());
257 printf (_ ("Options:").c_str ());
259 printf (Long_option_init::table_string (options_static).c_str ());
261 printf (_f ("Report bugs via %s",
262 "http://post.gmane.org/post.php?group=gmane.comp.gnu.lilypond.bugs"
266 return SCM_UNSPECIFIED;
276 printf (_ (WARRANTY).c_str ());
280 prepend_load_path (string dir)
282 string s = "(set! %load-path (cons \"" + dir + "\" %load-path))";
283 scm_c_eval_string (s.c_str ());
286 void init_global_tweak_registry ();
287 void init_fontconfig ();
293 /* Now we chroot, setuid/setgrp and chdir. If something goes wrong,
294 we exit (this is a security-sensitive area). First we split
295 jail_spec into its components, then we retrieve the user/group id
296 (necessarily *before* chroot'ing) and finally we perform the
301 USER_NAME, GROUP_NAME, JAIL, DIR, JAIL_MAX
304 vector<string> components = string_split (jail_spec, ',');
305 if (components.size () != JAIL_MAX)
307 error (_f ("expected %d arguments with jail, found: %u", JAIL_MAX,
308 (unsigned) components.size ()));
316 if (passwd * passwd = getpwnam (components[USER_NAME].c_str ()))
317 uid = passwd->pw_uid;
321 error (_f ("no such user: %s", components[USER_NAME]));
323 error (_f ("cannot get user id from user name: %s: %s",
324 components[USER_NAME],
333 if (group * group = getgrnam (components[GROUP_NAME].c_str ()))
338 error (_f ("no such group: %s", components[GROUP_NAME]));
340 error (_f ("cannot get group id from group name: %s: %s",
341 components[GROUP_NAME],
346 if (chroot (components[JAIL].c_str ()))
348 error (_f ("cannot chroot to: %s: %s", components[JAIL],
355 error (_f ("cannot change group id to: %d: %s", gid, strerror (errno)));
361 error (_f ("cannot change user id to: %d: %s", uid, strerror (errno)));
365 if (chdir (components[DIR].c_str ()))
367 error (_f ("cannot change working directory to: %s: %s", components[DIR],
375 main_with_guile (void *, int, char **)
377 /* Engravers use lily.scm contents, need to make Guile find it.
378 Prepend onto GUILE %load-path, very ugh. */
380 prepend_load_path (lilypond_datadir);
381 prepend_load_path (lilypond_datadir + "/scm");
383 if (be_verbose_global)
386 init_scheme_variables_global = "(list " + init_scheme_variables_global + ")";
387 init_scheme_code_global = "(begin " + init_scheme_code_global + ")";
390 call_constructors ();
394 ly_reset_all_fonts ();
396 /* We accept multiple independent music files on the command line to
397 reduce compile time when processing lots of small files.
398 Starting the GUILE engine is very time consuming. */
402 while (char const *arg = option_parser->get_next_arg ())
404 *tail = scm_cons (scm_from_locale_string (arg), SCM_EOL);
405 tail = SCM_CDRLOC (*tail);
408 delete option_parser;
412 if (!jail_spec.empty ())
416 SCM result = scm_call_1 (ly_lily_module_constant ("lilypond-main"), files);
424 setup_localisation ()
428 setlocale (LC_ALL, "");
430 /* FIXME: check if this is still true.
431 Disable localisation of float values. */
432 setlocale (LC_NUMERIC, "C");
434 string localedir = LOCALEDIR;
435 if (char const *env = getenv ("LILYPOND_LOCALEDIR"))
438 bindtextdomain ("lilypond", localedir.c_str ());
439 textdomain ("lilypond");
444 add_output_format (string format)
446 if (output_format_global != "")
447 output_format_global += ",";
448 output_format_global += format;
452 parse_argv (int argc, char **argv)
454 bool show_help = false;
455 option_parser = new Getopt_long (argc, argv, options_static);
456 while (Long_option_init const *opt = (*option_parser) ())
458 switch (opt->shortname_char_)
461 if (string (opt->longname_str0_) == "pdf"
462 || string (opt->longname_str0_) == "png"
463 || string (opt->longname_str0_) == "ps")
464 add_output_format (opt->longname_str0_);
465 else if (string (opt->longname_str0_) == "relocate")
466 relocate_binary = true;
471 string arg (option_parser->optional_argument_str0_);
472 ssize eq = arg.find ('=');
479 key = arg.substr (0, eq);
480 val = arg.substr (eq + 1, arg.length () - 1);
483 init_scheme_variables_global
484 += "(cons \'" + key + " '" + val + ")\n";
494 string s = option_parser->optional_argument_str0_;
495 File_name file_name (s);
496 output_name_global = file_name.to_string ();
500 jail_spec = option_parser->optional_argument_str0_;
504 init_scheme_code_global += option_parser->optional_argument_str0_ + string (" ");
513 vector<string> components
514 = string_split (option_parser->optional_argument_str0_, ',');
515 for (vsize i = 0; i < components.size (); i++)
516 add_output_format (components[i]);
521 dump_header_fieldnames_global
522 .push_back (option_parser->optional_argument_str0_);
525 global_path.append (option_parser->optional_argument_str0_);
528 init_name_global = option_parser->optional_argument_str0_;
534 be_verbose_global = true;
537 programming_error (to_string ("unhandled short option: %c",
538 opt->shortname_char_));
544 if (output_format_global == "")
545 output_format_global = "pdf";
550 if (be_verbose_global)
559 const char *yield = getenv ("LILYPOND_GC_YIELD");
560 bool overwrite = true;
567 sane_putenv ("GUILE_MIN_YIELD_1", yield, overwrite);
568 sane_putenv ("GUILE_MIN_YIELD_2", yield, overwrite);
569 sane_putenv ("GUILE_MIN_YIELD_MALLOC", yield, overwrite);
572 sane_putenv ("GUILE_INIT_SEGMENT_SIZE_1",
573 "10485760", overwrite);
574 sane_putenv ("GUILE_MAX_SEGMENT_SIZE",
575 "104857600", overwrite);
578 vector<string> start_environment_global;
581 main (int argc, char **argv, char **envp)
585 for (char **p = envp; *p; p++)
586 start_environment_global.push_back(*p);
588 if (getenv ("LILYPOND_VERBOSE"))
589 be_verbose_global = true;
591 setup_localisation ();
592 parse_argv (argc, argv);
593 if (isatty (STDIN_FILENO))
596 setup_paths (argv[0]);
603 scm_boot_guile (argc, argv, main_with_guile, 0);
607 error (_f ("exception caught: %s", e.what ()));
610 scm_boot_guile (argc, argv, main_with_guile, 0);
613 /* Only reachable if GUILE exits. That is an error. */