]> git.donarmstrong.com Git - lilypond.git/blob - lily/main.cc
MusicXML: src-dir rather than srcdir.
[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--2008 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 using namespace std;
16
17 #include <unistd.h>
18 #include <sys/types.h>
19 #include "config.hh"
20
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
30
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"
45
46 /*
47  * Global options that can be overridden through command line.
48  */
49
50 /* Names of header fields to be dumped to a separate file. */
51 vector<string> dump_header_fieldnames_global;
52
53 /* Name of initialisation file. */
54 string init_name_global;
55
56
57 /* Output formats to generate.  */
58 string output_format_global = "";
59
60 /* Current output name. */
61 string output_name_global;
62
63 /* Run in safe mode? */
64 bool be_safe_global = false;
65
66 /* Provide URI links to the original file */
67 bool point_and_click_global = true;
68
69 /* Verbose progress indication? */
70 bool be_verbose_global = false;
71
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;
76
77 bool relocate_binary = true;
78
79
80 /*
81  * Miscellaneous global stuff.
82  */
83 File_path global_path;
84
85 /*
86  * File globals.
87  */
88
89 static char const *AUTHORS
90 = "  Han-Wen Nienhuys <hanwen@xs4all.nl>\n"
91   "  Jan Nieuwenhuizen <janneke@gnu.org>\n";
92
93 static char const *PROGRAM_NAME = "lilypond";
94 static char const *PROGRAM_URL = "http://lilypond.org";
95
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"
100       "information.\n");
101
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"
106       "\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"
111       "\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");
116
117 /* Where the init files live.  Typically:
118    LILYPOND_DATADIR = /usr/share/lilypond
119 */
120 string lilypond_datadir;
121
122 /* The jail specification: USER, GROUP, JAIL, DIR. */
123 string jail_spec;
124
125 /*  The option parser */
126 static Getopt_long *option_parser = 0;
127
128 /* Internationalisation kludge in two steps:
129  * use _i () to get entry in POT file
130  * call gettext () explicitely for actual "translation"  */
131
132 static Long_option_init options_static[]
133 = {
134   {_i ("SYM[=VAL]"), "define-default", 'd',
135    _i ("set Scheme option SYM to VAL (default: #t).\n"
136        "Use -dhelp for help.")},
137
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")},
150 #if HAVE_CHROOT
151   {_i ("USER, GROUP, JAIL, DIR"), "jail", 'j', _i ("chroot to JAIL, become USER:GROUP\n"
152                                                 "and cd into DIR")},
153 #endif
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")},
159   {0, 0, 0, 0}
160 };
161
162 char const *LILYPOND_DATADIR = PACKAGE_DATADIR "/" TOPLEVEL_VERSION;
163
164
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))
172
173 #include <fpu_control.h>
174 static void configure_fpu() {
175   fpu_control_t fpu_control = 0x027f;
176   _FPU_SETCW (fpu_control);
177 }
178
179 #else
180
181 static void configure_fpu() {
182 }
183
184 #endif /* defined(__x86__) || defined(__i386__) */
185
186
187 static void
188 env_var_info (FILE *out, char const *key)
189 {
190   if (char const *value = getenv (key))
191     fprintf (out, "%s=\"%s\"\n", key, value);
192 }
193
194 static void
195 dir_info (FILE *out)
196 {
197   fputs ("\n", out);
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);
202
203   fprintf (out, "\nEffective prefix: \"%s\"\n", lilypond_datadir.c_str ());
204
205   if (relocate_binary)
206     {
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");
215     }
216 }
217
218 static void
219 copyright ()
220 {
221   printf (_f ("Copyright (c) %s by\n%s  and others.",
222               "1996--2008",
223               AUTHORS).c_str ());
224   printf ("\n");
225 }
226
227 static void
228 identify (FILE *out)
229 {
230   fputs (gnu_lilypond_version_string ().c_str (), out);
231   fputs ("\n", out);
232 }
233
234 static void
235 notice ()
236 {
237   identify (stdout);
238   printf ("\n");
239   copyright ();
240   printf ("\n");
241   puts (_f (NOTICE, PROGRAM_NAME).c_str ());
242 }
243
244 LY_DEFINE (ly_usage, "ly:usage",
245            0, 0, 0, (),
246            "Print usage message.")
247 {
248   /* No version number or newline here.  It confuses help2man.  */
249   printf (_f ("Usage: %s [OPTION]... FILE...", PROGRAM_NAME).c_str ());
250   printf ("\n\n");
251   printf (_ ("Typeset music and/or produce MIDI from FILE.").c_str ());
252   printf ("\n\n");
253   printf (_ ("LilyPond produces beautiful music notation.").c_str ());
254   printf ("\n");
255   printf (_f ("For more information, see %s", PROGRAM_URL).c_str ());
256   printf ("\n\n");
257   printf (_ ("Options:").c_str ());
258   printf ("\n");
259   printf (Long_option_init::table_string (options_static).c_str ());
260   printf ("\n");
261   /* Translators, please translate this string as
262          "Report bugs in English via %s",
263      or if there is a LilyPond users list or forum in your language
264          "Report bugs in English via %s or in YOUR_LANG via URI"  */
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;
271 }
272
273 static void
274 warranty ()
275 {
276   identify (stdout);
277   printf ("\n");
278   copyright ();
279   printf ("\n");
280   printf (_ (WARRANTY).c_str ());
281 }
282
283 static void
284 prepend_load_path (string dir)
285 {
286   string s = "(set! %load-path (cons \"" + dir + "\" %load-path))";
287   scm_c_eval_string (s.c_str ());
288 }
289
290 void init_global_tweak_registry ();
291 void init_fontconfig ();
292
293 #if HAVE_CHROOT
294 static void
295 do_chroot_jail ()
296 {
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.  */
302
303   enum Jail
304     {
305       USER_NAME, GROUP_NAME, JAIL, DIR, JAIL_MAX
306     };
307
308   vector<string> components = string_split (jail_spec, ',');
309   if (components.size () != JAIL_MAX)
310     {
311       error (_f ("expected %d arguments with jail, found: %u", JAIL_MAX,
312                  (unsigned) components.size ()));
313       exit (2);
314     }
315
316   /* Hmm.  */
317   errno = 0;
318
319   int uid;
320   if (passwd * passwd = getpwnam (components[USER_NAME].c_str ()))
321     uid = passwd->pw_uid;
322   else
323     {
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);
331     }
332
333   /* Hmm.  */
334   errno = 0;
335
336   int gid;
337   if (group * group = getgrnam (components[GROUP_NAME].c_str ()))
338     gid = group->gr_gid;
339   else
340     {
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);
348     }
349
350   if (chroot (components[JAIL].c_str ()))
351     {
352       error (_f ("cannot chroot to: %s: %s", components[JAIL],
353                  strerror (errno)));
354       exit (3);
355     }
356
357   if (setgid (gid))
358     {
359       error (_f ("cannot change group id to: %d: %s", gid, strerror (errno)));
360       exit (3);
361     }
362
363   if (setuid (uid))
364     {
365       error (_f ("cannot change user id to: %d: %s", uid, strerror (errno)));
366       exit (3);
367     }
368
369   if (chdir (components[DIR].c_str ()))
370     {
371       error (_f ("cannot change working directory to: %s: %s", components[DIR],
372                  strerror (errno)));
373       exit (3);
374     }
375 }
376 #endif
377
378 static void
379 main_with_guile (void *, int, char **)
380 {
381   /* Engravers use lily.scm contents, need to make Guile find it.
382      Prepend onto GUILE %load-path, very ugh. */
383
384   prepend_load_path (lilypond_datadir);
385   prepend_load_path (lilypond_datadir + "/scm");
386
387   if (be_verbose_global)
388     dir_info (stderr);
389
390   init_scheme_variables_global = "(list " + init_scheme_variables_global + ")";
391   init_scheme_code_global = "(begin " + init_scheme_code_global + ")";
392
393   ly_c_init_guile ();
394   call_constructors ();
395   init_fontconfig ();
396
397   init_freetype ();
398   ly_reset_all_fonts ();
399
400   /* We accept multiple independent music files on the command line to
401      reduce compile time when processing lots of small files.
402      Starting the GUILE engine is very time consuming.  */
403
404   SCM files = SCM_EOL;
405   SCM *tail = &files;
406   while (char const *arg = option_parser->get_next_arg ())
407     {
408       *tail = scm_cons (scm_from_locale_string (arg), SCM_EOL);
409       tail = SCM_CDRLOC (*tail);
410     }
411   
412   delete option_parser;
413   option_parser = 0;
414
415 #if HAVE_CHROOT
416   if (!jail_spec.empty ())
417     do_chroot_jail ();
418 #endif
419
420   SCM result = scm_call_1 (ly_lily_module_constant ("lilypond-main"), files);
421   (void) result;
422
423   /* Unreachable.  */
424   exit (0);
425 }
426
427 static void
428 setup_localisation ()
429 {
430 #if HAVE_GETTEXT
431   /* Enable locales */
432   setlocale (LC_ALL, "");
433
434   /* FIXME: check if this is still true.
435      Disable localisation of float values. */
436   setlocale (LC_NUMERIC, "C");
437
438   string localedir = LOCALEDIR;
439   if (char const *env = getenv ("LILYPOND_LOCALEDIR"))
440     localedir = env;
441
442   bindtextdomain ("lilypond", localedir.c_str ());
443   textdomain ("lilypond");
444 #endif
445 }
446
447 static void
448 add_output_format (string format)
449 {
450   if (output_format_global != "")
451     output_format_global += ",";
452   output_format_global += format;
453 }
454
455 static void
456 parse_argv (int argc, char **argv)
457 {
458   bool show_help = false;
459   option_parser = new Getopt_long (argc, argv, options_static);
460   while (Long_option_init const *opt = (*option_parser) ())
461     {
462       switch (opt->shortname_char_)
463         {
464         case 0:
465           if (string (opt->longname_str0_) == "pdf"
466               || string (opt->longname_str0_) == "png"
467               || string (opt->longname_str0_) == "ps")
468             add_output_format (opt->longname_str0_);
469           else if (string (opt->longname_str0_) == "relocate")
470             relocate_binary = true;
471           break;
472
473         case 'd':
474           {
475             string arg (option_parser->optional_argument_str0_);
476             ssize eq = arg.find ('=');
477
478             string key = arg;
479             string val = "#t";
480
481             if (eq != NPOS)
482               {
483                 key = arg.substr (0, eq);
484                 val = arg.substr (eq + 1, arg.length () - 1);
485               }
486
487             init_scheme_variables_global
488               += "(cons \'" + key + " '" + val + ")\n";
489           }
490           break;
491
492         case 'v':
493           notice ();
494           exit (0);
495           break;
496         case 'o':
497           {
498             string s = option_parser->optional_argument_str0_;
499             File_name file_name (s);
500             output_name_global = file_name.to_string ();
501           }
502           break;
503         case 'j':
504           jail_spec = option_parser->optional_argument_str0_;
505           break;
506           
507         case 'e':
508           init_scheme_code_global += option_parser->optional_argument_str0_ + string (" ");
509           break;
510         case 'w':
511           warranty ();
512           exit (0);
513           break;
514
515         case 'f':
516           {
517             vector<string> components
518               = string_split (option_parser->optional_argument_str0_, ',');
519             for (vsize i = 0; i < components.size (); i++)
520               add_output_format (components[i]);
521           }
522           break;
523
524         case 'H':
525           dump_header_fieldnames_global
526             .push_back (option_parser->optional_argument_str0_);
527           break;
528         case 'I':
529           global_path.append (option_parser->optional_argument_str0_);
530           break;
531         case 'i':
532           init_name_global = option_parser->optional_argument_str0_;
533           break;
534         case 'h':
535           show_help = true;
536           break;
537         case 'V':
538           be_verbose_global = true;
539           break;
540         default:
541           programming_error (to_string ("unhandled short option: %c",
542                                         opt->shortname_char_));
543           assert (false);
544           break;
545         }
546     }
547
548   if (output_format_global == "")
549     output_format_global = "pdf";
550
551   if (show_help)
552     {
553       ly_usage ();
554       if (be_verbose_global)
555         dir_info (stdout);
556       exit (0);
557     }
558 }
559
560 void
561 setup_guile_env ()
562 {
563   const char *yield = getenv ("LILYPOND_GC_YIELD");
564   bool overwrite = true;
565   if (!yield)
566     {
567       yield = "65";
568       overwrite = false;
569     }
570
571   sane_putenv ("GUILE_MIN_YIELD_1", yield, overwrite);
572   sane_putenv ("GUILE_MIN_YIELD_2", yield, overwrite);
573   sane_putenv ("GUILE_MIN_YIELD_MALLOC", yield, overwrite);
574
575
576   sane_putenv ("GUILE_INIT_SEGMENT_SIZE_1",
577                "10485760", overwrite);
578   sane_putenv ("GUILE_MAX_SEGMENT_SIZE",
579                "104857600", overwrite);
580 }
581
582 vector<string> start_environment_global;
583  
584 int
585 main (int argc, char **argv, char **envp)
586 {
587   configure_fpu();
588
589   for (char **p = envp; *p; p++)
590     start_environment_global.push_back(*p);
591   
592   if (getenv ("LILYPOND_VERBOSE"))
593     be_verbose_global = true;
594
595   setup_localisation ();
596   parse_argv (argc, argv);
597   if (isatty (STDIN_FILENO))
598     identify (stderr);
599
600   setup_paths (argv[0]);
601   setup_guile_env ();
602
603 #if 0
604   /* Debugging aid.  */
605   try
606     {
607       scm_boot_guile (argc, argv, main_with_guile, 0);
608     }
609   catch (exception e)
610     {
611       error (_f ("exception caught: %s", e.what ()));
612     };
613 #else
614   scm_boot_guile (argc, argv, main_with_guile, 0);
615 #endif
616         
617   /* Only reachable if GUILE exits.  That is an error.  */
618   return 1;
619 }