]> git.donarmstrong.com Git - lilypond.git/blob - lily/main.cc
ba8c3acbbc7d21c7003493ac3f335c578b440c06
[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 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--2008",
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   printf ("\n");
244   copyright ();
245   printf ("\n");
246   puts (_f (NOTICE, PROGRAM_NAME).c_str ());
247 }
248
249 LY_DEFINE (ly_usage, "ly:usage",
250            0, 0, 0, (),
251            "Print usage message.")
252 {
253   /* No version number or newline here.  It confuses help2man.  */
254   printf (_f ("Usage: %s [OPTION]... FILE...", PROGRAM_NAME).c_str ());
255   printf ("\n\n");
256   printf (_ ("Typeset music and/or produce MIDI from FILE.").c_str ());
257   printf ("\n\n");
258   printf (_ ("LilyPond produces beautiful music notation.").c_str ());
259   printf ("\n");
260   printf (_f ("For more information, see %s", PROGRAM_URL).c_str ());
261   printf ("\n\n");
262   printf (_ ("Options:").c_str ());
263   printf ("\n");
264   printf (Long_option_init::table_string (options_static).c_str ());
265   printf ("\n");
266   printf (_f ("Report bugs via %s",
267               "http://post.gmane.org/post.php?group=gmane.comp.gnu.lilypond.bugs"
268               ).c_str ());
269   printf ("\n");
270   printf ("\n");
271   return SCM_UNSPECIFIED;
272 }
273
274 static void
275 warranty ()
276 {
277   identify (stdout);
278   printf ("\n");
279   copyright ();
280   printf ("\n");
281   printf (_ (WARRANTY).c_str ());
282 }
283
284 static void
285 prepend_load_path (string dir)
286 {
287   string s = "(set! %load-path (cons \"" + dir + "\" %load-path))";
288   scm_c_eval_string (s.c_str ());
289 }
290
291 void init_global_tweak_registry ();
292 void init_fontconfig ();
293
294 #if HAVE_CHROOT
295 static void
296 do_chroot_jail ()
297 {
298   /* Now we chroot, setuid/setgrp and chdir.  If something goes wrong,
299      we exit (this is a security-sensitive area).  First we split
300      jail_spec into its components, then we retrieve the user/group id
301      (necessarily *before* chroot'ing) and finally we perform the
302      actual actions.  */
303
304   enum Jail
305     {
306       USER_NAME, GROUP_NAME, JAIL, DIR, JAIL_MAX
307     };
308
309   vector<string> components = string_split (jail_spec, ',');
310   if (components.size () != JAIL_MAX)
311     {
312       error (_f ("expected %d arguments with jail, found: %u", JAIL_MAX,
313                  (unsigned) components.size ()));
314       exit (2);
315     }
316
317   /* Hmm.  */
318   errno = 0;
319
320   int uid;
321   if (passwd * passwd = getpwnam (components[USER_NAME].c_str ()))
322     uid = passwd->pw_uid;
323   else
324     {
325       if (errno == 0)
326         error (_f ("no such user: %s", components[USER_NAME]));
327       else
328         error (_f ("cannot get user id from user name: %s: %s",
329                    components[USER_NAME],
330                    strerror (errno)));
331       exit (3);
332     }
333
334   /* Hmm.  */
335   errno = 0;
336
337   int gid;
338   if (group * group = getgrnam (components[GROUP_NAME].c_str ()))
339     gid = group->gr_gid;
340   else
341     {
342       if (errno == 0)
343         error (_f ("no such group: %s", components[GROUP_NAME]));
344       else
345         error (_f ("cannot get group id from group name: %s: %s",
346                    components[GROUP_NAME],
347                    strerror (errno)));
348       exit (3);
349     }
350
351   if (chroot (components[JAIL].c_str ()))
352     {
353       error (_f ("cannot chroot to: %s: %s", components[JAIL],
354                  strerror (errno)));
355       exit (3);
356     }
357
358   if (setgid (gid))
359     {
360       error (_f ("cannot change group id to: %d: %s", gid, strerror (errno)));
361       exit (3);
362     }
363
364   if (setuid (uid))
365     {
366       error (_f ("cannot change user id to: %d: %s", uid, strerror (errno)));
367       exit (3);
368     }
369
370   if (chdir (components[DIR].c_str ()))
371     {
372       error (_f ("cannot change working directory to: %s: %s", components[DIR],
373                  strerror (errno)));
374       exit (3);
375     }
376 }
377 #endif
378
379 static void
380 main_with_guile (void *, int, char **)
381 {
382   /* Engravers use lily.scm contents, need to make Guile find it.
383      Prepend onto GUILE %load-path, very ugh. */
384
385   prepend_load_path (lilypond_datadir);
386   prepend_load_path (lilypond_datadir + "/scm");
387
388   if (be_verbose_global)
389     dir_info (stderr);
390
391   is_pango_format_global = !is_TeX_format_global;
392   init_scheme_variables_global = "(list " + init_scheme_variables_global + ")";
393   init_scheme_code_global = "(begin " + init_scheme_code_global + ")";
394
395   ly_c_init_guile ();
396   call_constructors ();
397   init_fontconfig ();
398
399   init_freetype ();
400   ly_reset_all_fonts ();
401
402   is_TeX_format_global = (get_output_backend_name () == "tex"
403                           || get_output_backend_name () == "texstr");
404   
405
406   /* We accept multiple independent music files on the command line to
407      reduce compile time when processing lots of small files.
408      Starting the GUILE engine is very time consuming.  */
409
410   SCM files = SCM_EOL;
411   SCM *tail = &files;
412   while (char const *arg = option_parser->get_next_arg ())
413     {
414       *tail = scm_cons (scm_from_locale_string (arg), SCM_EOL);
415       tail = SCM_CDRLOC (*tail);
416     }
417   
418   delete option_parser;
419   option_parser = 0;
420
421 #if HAVE_CHROOT
422   if (!jail_spec.empty ())
423     do_chroot_jail ();
424 #endif
425
426   SCM result = scm_call_1 (ly_lily_module_constant ("lilypond-main"), files);
427   (void) result;
428
429   /* Unreachable.  */
430   exit (0);
431 }
432
433 static void
434 setup_localisation ()
435 {
436 #if HAVE_GETTEXT
437   /* Enable locales */
438   setlocale (LC_ALL, "");
439
440   /* FIXME: check if this is still true.
441      Disable localisation of float values.  This breaks TeX output.  */
442   setlocale (LC_NUMERIC, "C");
443
444   string localedir = LOCALEDIR;
445   if (char const *env = getenv ("LILYPOND_LOCALEDIR"))
446     localedir = env;
447
448   bindtextdomain ("lilypond", localedir.c_str ());
449   textdomain ("lilypond");
450 #endif
451 }
452
453 static void
454 add_output_format (string format)
455 {
456   if (output_format_global != "")
457     output_format_global += ",";
458   output_format_global += format;
459 }
460
461 static void
462 parse_argv (int argc, char **argv)
463 {
464   bool show_help = false;
465   option_parser = new Getopt_long (argc, argv, options_static);
466   while (Long_option_init const *opt = (*option_parser) ())
467     {
468       switch (opt->shortname_char_)
469         {
470         case 0:
471           if (string (opt->longname_str0_) == "dvi"
472               || string (opt->longname_str0_) == "pdf"
473               || string (opt->longname_str0_) == "png"
474               || string (opt->longname_str0_) == "ps"
475               || string (opt->longname_str0_) == "tex")
476             add_output_format (opt->longname_str0_);
477           else if (string (opt->longname_str0_) == "relocate")
478             relocate_binary = true;
479           break;
480
481         case 'd':
482           {
483             string arg (option_parser->optional_argument_str0_);
484             ssize eq = arg.find ('=');
485
486             string key = arg;
487             string val = "#t";
488
489             if (eq != NPOS)
490               {
491                 key = arg.substr (0, eq);
492                 val = arg.substr (eq + 1, arg.length () - 1);
493               }
494
495             init_scheme_variables_global
496               += "(cons \'" + key + " '" + val + ")\n";
497           }
498           break;
499
500         case 'v':
501           notice ();
502           exit (0);
503           break;
504         case 'o':
505           {
506             string s = option_parser->optional_argument_str0_;
507             File_name file_name (s);
508             output_name_global = file_name.to_string ();
509           }
510           break;
511         case 'j':
512           jail_spec = option_parser->optional_argument_str0_;
513           break;
514           
515         case 'e':
516           init_scheme_code_global += option_parser->optional_argument_str0_ + string (" ");
517           break;
518         case 'w':
519           warranty ();
520           exit (0);
521           break;
522
523         case 'f':
524           {
525             vector<string> components
526               = string_split (option_parser->optional_argument_str0_, ',');
527             for (vsize i = 0; i < components.size (); i++)
528               add_output_format (components[i]);
529           }
530           break;
531
532         case 'H':
533           dump_header_fieldnames_global
534             .push_back (option_parser->optional_argument_str0_);
535           break;
536         case 'I':
537           global_path.append (option_parser->optional_argument_str0_);
538           break;
539         case 'i':
540           init_name_global = option_parser->optional_argument_str0_;
541           break;
542         case 'h':
543           show_help = true;
544           break;
545         case 'V':
546           be_verbose_global = true;
547           break;
548         default:
549           programming_error (to_string ("unhandled short option: %c",
550                                         opt->shortname_char_));
551           assert (false);
552           break;
553         }
554     }
555
556   if (output_format_global == "")
557     output_format_global = "pdf";
558
559   if (show_help)
560     {
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 }