]> git.donarmstrong.com Git - lilypond.git/blob - lily/main.cc
Merge branch 'master' into dev/texi2html
[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--2007 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 bool is_pango_format_global;
61 bool is_TeX_format_global;
62
63 /* Current output name. */
64 string output_name_global;
65
66 /* Run in safe mode? */
67 bool be_safe_global = false;
68
69 /* Provide URI links to the original file */
70 bool point_and_click_global = true;
71
72 /* Verbose progress indication? */
73 bool be_verbose_global = false;
74
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;
79
80 bool relocate_binary = true;
81
82
83 /*
84  * Miscellaneous global stuff.
85  */
86 File_path global_path;
87
88 /*
89  * File globals.
90  */
91
92 static char const *AUTHORS
93 = "  Han-Wen Nienhuys <hanwen@xs4all.nl>\n"
94   "  Jan Nieuwenhuizen <janneke@gnu.org>\n";
95
96 static char const *PROGRAM_NAME = "lilypond";
97 static char const *PROGRAM_URL = "http://lilypond.org";
98
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");
104
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");
119
120 /* Where the init files live.  Typically:
121    LILYPOND_DATADIR = /usr/share/lilypond
122 */
123 string lilypond_datadir;
124
125 /* The jail specification: USER, GROUP, JAIL, DIR. */
126 string jail_spec;
127
128 /*  The option parser */
129 static Getopt_long *option_parser = 0;
130
131 /* Internationalisation kludge in two steps:
132  * use _i () to get entry in POT file
133  * call gettext () explicitely for actual "translation"  */
134
135 static Long_option_init options_static[]
136 = {
137   {_i ("SYM[=VAL]"), "define-default", 'd',
138    _i ("set Scheme option SYM to VAL (default: #t).\n"
139        "Use -dhelp for help.")},
140
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}
165 };
166
167 char const *LILYPOND_DATADIR = PACKAGE_DATADIR "/" TOPLEVEL_VERSION;
168
169
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))
177
178 #include <fpu_control.h>
179 static void configure_fpu() {
180   fpu_control_t fpu_control = 0x027f;
181   _FPU_SETCW (fpu_control);
182 }
183
184 #else
185
186 static void configure_fpu() {
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   printf (_f ("Copyright (c) %s by\n%s  and others.",
227               "1996--2007",
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   puts (_f (NOTICE, PROGRAM_NAME).c_str ());
244   printf ("\n");
245   copyright ();
246 }
247
248 LY_DEFINE (ly_usage, "ly:usage",
249            0, 0, 0, (),
250            "Print usage message.")
251 {
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;
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   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 + ")";
393
394   ly_c_init_guile ();
395   call_constructors ();
396   init_fontconfig ();
397
398   init_freetype ();
399   ly_reset_all_fonts ();
400
401   is_TeX_format_global = (get_output_backend_name () == "tex"
402                           || get_output_backend_name () == "texstr");
403   
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.  This breaks TeX output.  */
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_) == "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;
479
480         case 'd':
481           {
482             string arg (option_parser->optional_argument_str0_);
483             ssize eq = arg.find ('=');
484
485             string key = arg;
486             string val = "#t";
487
488             if (eq != NPOS)
489               {
490                 key = arg.substr (0, eq);
491                 val = arg.substr (eq + 1, arg.length () - 1);
492               }
493
494             init_scheme_variables_global
495               += "(cons \'" + key + " '" + val + ")\n";
496           }
497           break;
498
499         case 'v':
500           notice ();
501           exit (0);
502           break;
503         case 'o':
504           {
505             string s = option_parser->optional_argument_str0_;
506             File_name file_name (s);
507             output_name_global = file_name.to_string ();
508           }
509           break;
510         case 'j':
511           jail_spec = option_parser->optional_argument_str0_;
512           break;
513           
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;
521
522         case 'f':
523           {
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]);
528           }
529           break;
530
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;
552         }
553     }
554
555   if (output_format_global == "")
556     output_format_global = "pdf";
557
558   if (show_help)
559     {
560       identify (stdout);
561       ly_usage ();
562       if (be_verbose_global)
563         dir_info (stdout);
564       exit (0);
565     }
566 }
567
568 void
569 setup_guile_env ()
570 {
571   const char *yield = getenv ("LILYPOND_GC_YIELD");
572   bool overwrite = true;
573   if (!yield)
574     {
575       yield = "65";
576       overwrite = false;
577     }
578
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);
582
583
584   sane_putenv ("GUILE_INIT_SEGMENT_SIZE_1",
585                "10485760", overwrite);
586   sane_putenv ("GUILE_MAX_SEGMENT_SIZE",
587                "104857600", overwrite);
588 }
589
590 vector<string> start_environment_global;
591  
592 int
593 main (int argc, char **argv, char **envp)
594 {
595   configure_fpu();
596
597   for (char **p = envp; *p; p++)
598     start_environment_global.push_back(*p);
599   
600   if (getenv ("LILYPOND_VERBOSE"))
601     be_verbose_global = true;
602
603   setup_localisation ();
604   parse_argv (argc, argv);
605   if (isatty (STDIN_FILENO))
606     identify (stderr);
607
608   setup_paths (argv[0]);
609   setup_guile_env ();
610
611 #if 0
612   /* Debugging aid.  */
613   try
614     {
615       scm_boot_guile (argc, argv, main_with_guile, 0);
616     }
617   catch (exception e)
618     {
619       error (_f ("exception caught: %s", e.what ()));
620     };
621 #else
622   scm_boot_guile (argc, argv, main_with_guile, 0);
623 #endif
624         
625   /* Only reachable if GUILE exits.  That is an error.  */
626   return 1;
627 }