More work on spacing to match lily master
[lilypond/csorensen.git] / lily / main.cc
blobe0f5dda1a2d93a34a3a06b4834b60954a6fa4dbb
1 /*
2 main.cc -- implement main () entrypoint.
4 source file of the GNU LilyPond music typesetter
6 (c) 1997--2007 Han-Wen Nienhuys <hanwen@xs4all.nl>
7 */
9 #include "main.hh"
11 #include <cassert>
12 #include <clocale>
13 #include <cstring>
14 #include <cerrno>
15 using namespace std;
17 #include <unistd.h>
18 #include <sys/types.h>
19 #include "config.hh"
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
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"
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 bool is_pango_format_global;
61 bool is_TeX_format_global;
63 /* Current output name. */
64 string output_name_global;
66 /* Run in safe mode? */
67 bool be_safe_global = false;
69 /* Provide URI links to the original file */
70 bool point_and_click_global = true;
72 /* Verbose progress indication? */
73 bool be_verbose_global = false;
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;
80 bool relocate_binary = true;
84 * Miscellaneous global stuff.
86 File_path global_path;
89 * File globals.
92 static char const *AUTHORS
93 = " Han-Wen Nienhuys <hanwen@xs4all.nl>\n"
94 " Jan Nieuwenhuizen <janneke@gnu.org>\n";
96 static char const *PROGRAM_NAME = "lilypond";
97 static char const *PROGRAM_URL = "http://lilypond.org";
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");
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");
120 /* Where the init files live. Typically:
121 LILYPOND_DATADIR = /usr/share/lilypond
123 string lilypond_datadir;
125 /* The jail specification: USER, GROUP, JAIL, DIR. */
126 string jail_spec;
128 /* The option parser */
129 static Getopt_long *option_parser = 0;
131 /* Internationalisation kludge in two steps:
132 * use _i () to get entry in POT file
133 * call gettext () explicitely for actual "translation" */
135 static Long_option_init options_static[]
137 {_i ("SYM[=VAL]"), "define-default", 'd',
138 _i ("set Scheme option SYM to VAL (default: #t).\n"
139 "Use -dhelp for help.")},
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}
167 char const *LILYPOND_DATADIR = PACKAGE_DATADIR "/" TOPLEVEL_VERSION;
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 #if ((defined(__x86__) || defined(__i386__)) \
176 && defined(HAVE_FPU_CONTROL_H) && (HAVE_FPU_CONTROL_H == 1))
178 #include <fpu_control.h>
179 static void configure_fpu() {
180 fpu_control_t fpu_control = 0x027f;
181 _FPU_SETCW (fpu_control);
184 #else
186 static void 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 printf (_f ("Copyright (c) %s by\n%s and others.",
227 "1996--2007",
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 puts (_f (NOTICE, PROGRAM_NAME).c_str ());
244 printf ("\n");
245 copyright ();
248 LY_DEFINE (ly_usage, "ly:usage",
249 0, 0, 0, (),
250 "Print usage message.")
252 /* No version number or newline here. It confuses help2man. */
253 printf (_f ("Usage: %s [OPTION]... FILE...", PROGRAM_NAME).c_str ());
254 printf ("\n\n");
255 printf (_ ("Typeset music and/or produce MIDI from FILE.").c_str ());
256 printf ("\n\n");
257 printf (_ ("LilyPond produces beautiful music notation.").c_str ());
258 printf ("\n");
259 printf (_f ("For more information, see %s", PROGRAM_URL).c_str ());
260 printf ("\n\n");
261 printf (_ ("Options:").c_str ());
262 printf ("\n");
263 printf (Long_option_init::table_string (options_static).c_str ());
264 printf ("\n");
265 printf (_f ("Report bugs via %s",
266 "http://post.gmane.org/post.php?group=gmane.comp.gnu.lilypond.bugs"
267 ).c_str ());
268 printf ("\n");
269 printf ("\n");
270 return SCM_UNSPECIFIED;
273 static void
274 warranty ()
276 identify (stdout);
277 printf ("\n");
278 copyright ();
279 printf ("\n");
280 printf (_ (WARRANTY).c_str ());
283 static void
284 prepend_load_path (string dir)
286 string s = "(set! %load-path (cons \"" + dir + "\" %load-path))";
287 scm_c_eval_string (s.c_str ());
290 void init_global_tweak_registry ();
291 void init_fontconfig ();
293 #if HAVE_CHROOT
294 static void
295 do_chroot_jail ()
297 /* Now we chroot, setuid/setgrp and chdir. If something goes wrong,
298 we exit (this is a security-sensitive area). First we split
299 jail_spec into its components, then we retrieve the user/group id
300 (necessarily *before* chroot'ing) and finally we perform the
301 actual actions. */
303 enum Jail
305 USER_NAME, GROUP_NAME, JAIL, DIR, JAIL_MAX
308 vector<string> components = string_split (jail_spec, ',');
309 if (components.size () != JAIL_MAX)
311 error (_f ("expected %d arguments with jail, found: %u", JAIL_MAX,
312 (unsigned) components.size ()));
313 exit (2);
316 /* Hmm. */
317 errno = 0;
319 int uid;
320 if (passwd * passwd = getpwnam (components[USER_NAME].c_str ()))
321 uid = passwd->pw_uid;
322 else
324 if (errno == 0)
325 error (_f ("no such user: %s", components[USER_NAME]));
326 else
327 error (_f ("cannot get user id from user name: %s: %s",
328 components[USER_NAME],
329 strerror (errno)));
330 exit (3);
333 /* Hmm. */
334 errno = 0;
336 int gid;
337 if (group * group = getgrnam (components[GROUP_NAME].c_str ()))
338 gid = group->gr_gid;
339 else
341 if (errno == 0)
342 error (_f ("no such group: %s", components[GROUP_NAME]));
343 else
344 error (_f ("cannot get group id from group name: %s: %s",
345 components[GROUP_NAME],
346 strerror (errno)));
347 exit (3);
350 if (chroot (components[JAIL].c_str ()))
352 error (_f ("cannot chroot to: %s: %s", components[JAIL],
353 strerror (errno)));
354 exit (3);
357 if (setgid (gid))
359 error (_f ("cannot change group id to: %d: %s", gid, strerror (errno)));
360 exit (3);
363 if (setuid (uid))
365 error (_f ("cannot change user id to: %d: %s", uid, strerror (errno)));
366 exit (3);
369 if (chdir (components[DIR].c_str ()))
371 error (_f ("cannot change working directory to: %s: %s", components[DIR],
372 strerror (errno)));
373 exit (3);
376 #endif
378 static void
379 main_with_guile (void *, int, char **)
381 /* Engravers use lily.scm contents, need to make Guile find it.
382 Prepend onto GUILE %load-path, very ugh. */
384 prepend_load_path (lilypond_datadir);
385 prepend_load_path (lilypond_datadir + "/scm");
387 if (be_verbose_global)
388 dir_info (stderr);
390 is_pango_format_global = !is_TeX_format_global;
391 init_scheme_variables_global = "(list " + init_scheme_variables_global + ")";
392 init_scheme_code_global = "(begin " + init_scheme_code_global + ")";
394 ly_c_init_guile ();
395 call_constructors ();
396 init_fontconfig ();
398 init_freetype ();
399 ly_reset_all_fonts ();
401 is_TeX_format_global = (get_output_backend_name () == "tex"
402 || get_output_backend_name () == "texstr");
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. This breaks TeX output. */
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_) == "dvi"
471 || string (opt->longname_str0_) == "pdf"
472 || string (opt->longname_str0_) == "png"
473 || string (opt->longname_str0_) == "ps"
474 || string (opt->longname_str0_) == "tex")
475 add_output_format (opt->longname_str0_);
476 else if (string (opt->longname_str0_) == "relocate")
477 relocate_binary = true;
478 break;
480 case 'd':
482 string arg (option_parser->optional_argument_str0_);
483 ssize eq = arg.find ('=');
485 string key = arg;
486 string val = "#t";
488 if (eq != NPOS)
490 key = arg.substr (0, eq);
491 val = arg.substr (eq + 1, arg.length () - 1);
494 init_scheme_variables_global
495 += "(cons \'" + key + " '" + val + ")\n";
497 break;
499 case 'v':
500 notice ();
501 exit (0);
502 break;
503 case 'o':
505 string s = option_parser->optional_argument_str0_;
506 File_name file_name (s);
507 output_name_global = file_name.to_string ();
509 break;
510 case 'j':
511 jail_spec = option_parser->optional_argument_str0_;
512 break;
514 case 'e':
515 init_scheme_code_global += option_parser->optional_argument_str0_ + string (" ");
516 break;
517 case 'w':
518 warranty ();
519 exit (0);
520 break;
522 case 'f':
524 vector<string> components
525 = string_split (option_parser->optional_argument_str0_, ',');
526 for (vsize i = 0; i < components.size (); i++)
527 add_output_format (components[i]);
529 break;
531 case 'H':
532 dump_header_fieldnames_global
533 .push_back (option_parser->optional_argument_str0_);
534 break;
535 case 'I':
536 global_path.append (option_parser->optional_argument_str0_);
537 break;
538 case 'i':
539 init_name_global = option_parser->optional_argument_str0_;
540 break;
541 case 'h':
542 show_help = true;
543 break;
544 case 'V':
545 be_verbose_global = true;
546 break;
547 default:
548 programming_error (to_string ("unhandled short option: %c",
549 opt->shortname_char_));
550 assert (false);
551 break;
555 if (output_format_global == "")
556 output_format_global = "pdf";
558 if (show_help)
560 identify (stdout);
561 ly_usage ();
562 if (be_verbose_global)
563 dir_info (stdout);
564 exit (0);
568 void
569 setup_guile_env ()
571 const char *yield = getenv ("LILYPOND_GC_YIELD");
572 bool overwrite = true;
573 if (!yield)
575 yield = "65";
576 overwrite = false;
579 sane_putenv ("GUILE_MIN_YIELD_1", yield, overwrite);
580 sane_putenv ("GUILE_MIN_YIELD_2", yield, overwrite);
581 sane_putenv ("GUILE_MIN_YIELD_MALLOC", yield, overwrite);
584 sane_putenv ("GUILE_INIT_SEGMENT_SIZE_1",
585 "10485760", overwrite);
586 sane_putenv ("GUILE_MAX_SEGMENT_SIZE",
587 "104857600", overwrite);
590 vector<string> start_environment_global;
593 main (int argc, char **argv, char **envp)
595 configure_fpu();
597 for (char **p = envp; *p; p++)
598 start_environment_global.push_back(*p);
600 if (getenv ("LILYPOND_VERBOSE"))
601 be_verbose_global = true;
603 setup_localisation ();
604 parse_argv (argc, argv);
605 if (isatty (STDIN_FILENO))
606 identify (stderr);
608 setup_paths (argv[0]);
609 setup_guile_env ();
611 #if 0
612 /* Debugging aid. */
615 scm_boot_guile (argc, argv, main_with_guile, 0);
617 catch (exception e)
619 error (_f ("exception caught: %s", e.what ()));
621 #else
622 scm_boot_guile (argc, argv, main_with_guile, 0);
623 #endif
625 /* Only reachable if GUILE exits. That is an error. */
626 return 1;