]> git.donarmstrong.com Git - lilypond.git/blob - lily/main.cc
Merge branch 'lilypond/translation' of ssh://git.sv.gnu.org/srv/git/lilypond into...
[lilypond.git] / lily / main.cc
1 /*
2   This file is part of LilyPond, the GNU music typesetter.
3
4   Copyright (C) 1997--2010 Han-Wen Nienhuys <hanwen@xs4all.nl>
5
6   LilyPond is free software: you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation, either version 3 of the License, or
9   (at your option) any later version.
10
11   LilyPond is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "main.hh"
21
22 #include <cassert>
23 #include <clocale>
24 #include <cstring>
25 #include <cerrno>
26 #include <cstdio>
27 using namespace std;
28
29 #include <unistd.h>
30 #include <sys/types.h>
31 #include "config.hh"
32
33 #if HAVE_GRP_H
34 #include <grp.h>
35 #endif
36 #if HAVE_PWD_H
37 #include <pwd.h>
38 #endif
39 #if HAVE_GETTEXT
40 #include <libintl.h>
41 #endif
42
43 #include "all-font-metrics.hh"
44 #include "file-name.hh"
45 #include "freetype.hh"
46 #include "getopt-long.hh"
47 #include "global-ctor.hh"
48 #include "international.hh"
49 #include "lily-version.hh"
50 #include "misc.hh"
51 #include "output-def.hh"
52 #include "program-option.hh"
53 #include "relocate.hh"
54 #include "string-convert.hh"
55 #include "version.hh"
56 #include "warn.hh"
57
58 /*
59  * Global options that can be overridden through command line.
60  */
61
62 /* Names of header fields to be dumped to a separate file. */
63 vector<string> dump_header_fieldnames_global;
64
65 /* Name of initialisation file. */
66 string init_name_global;
67
68
69 /* Output formats to generate.  */
70 string output_format_global = "";
71
72 /* Current output name. */
73 string output_name_global;
74
75 /* Run in safe mode? */
76 bool be_safe_global = false;
77
78 /* Provide URI links to the original file */
79 bool point_and_click_global = true;
80
81 /* Verbose progress indication? */
82 bool be_verbose_global = false;
83
84 /* Scheme code to execute before parsing, after .scm init.
85    This is where -e arguments are appended to.  */
86 string init_scheme_code_global;
87 string init_scheme_variables_global;
88
89 bool relocate_binary = true;
90
91
92 /*
93  * Miscellaneous global stuff.
94  */
95 File_path global_path;
96
97 /*
98  * File globals.
99  */
100
101 static char const *AUTHORS
102 = "  Han-Wen Nienhuys <hanwen@xs4all.nl>\n"
103   "  Jan Nieuwenhuizen <janneke@gnu.org>\n";
104
105 static char const *PROGRAM_NAME = "lilypond";
106 static char const *PROGRAM_URL = "http://lilypond.org";
107
108 static char const *NOTICE
109 = _i ("This program is free software.  It is covered by the GNU General Public\n"
110       "License and you are welcome to change it and/or distribute copies of it\n"
111       "under certain conditions.  Invoke as `%s --warranty' for more\n"
112       "information.\n");
113
114 static char const *WARRANTY
115 = _i ("    This program is free software; you can redistribute it and/or\n"
116       "modify it under the terms of the GNU General Public License version 2\n"
117       "as published by the Free Software Foundation.\n"
118       "\n"
119       "    This program is distributed in the hope that it will be useful,\n"
120       "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
121       "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n"
122       "General Public License for more details.\n"
123       "\n"
124       "    You should have received a copy of the\n"
125       "GNU General Public License along with this program; if not, write to\n"
126       "the Free Software Foundation, Inc., 59 Temple Place - Suite 330,\n"
127       "Boston, MA 02111-1307, USA.\n");
128
129 /* Where the init files live.  Typically:
130    LILYPOND_DATADIR = /usr/share/lilypond
131 */
132 string lilypond_datadir;
133
134 /* The jail specification: USER, GROUP, JAIL, DIR. */
135 string jail_spec;
136
137 /*  The option parser */
138 static Getopt_long *option_parser = 0;
139
140 /* Internationalisation kludge in two steps:
141  * use _i () to get entry in POT file
142  * call gettext () explicitly for actual "translation"  */
143
144 static Long_option_init options_static[]
145 = {
146   {_i ("SYM[=VAL]"), "define-default", 'd',
147    _i ("set Scheme option SYM to VAL (default: #t).\n"
148        "Use -dhelp for help.")},
149
150   {_i ("EXPR"), "evaluate", 'e', _i ("evaluate scheme code")},
151   /* Bug in option parser: --output =foe is taken as an abbreviation
152      for --output-format.  */
153   {_i ("FORMATs"), "formats", 'f', _i ("dump FORMAT,...  Also as separate options:")},
154   {0, "pdf", 0, _i ("generate PDF (default)")},
155   {0, "png", 0, _i ("generate PNG")},
156   {0, "ps", 0, _i ("generate PostScript")},
157   {0, "help", 'h',  _i ("show this help and exit")},
158   {_i ("FIELD"), "header", 'H',  _i ("dump header field FIELD to file\n"
159                                      "named BASENAME.FIELD")},
160   {_i ("DIR"), "include", 'I',  _i ("add DIR to search path")},
161   {_i ("FILE"), "init", 'i',  _i ("use FILE as init file")},
162 #if HAVE_CHROOT
163   {_i ("USER, GROUP, JAIL, DIR"), "jail", 'j', _i ("chroot to JAIL, become USER:GROUP\n"
164                                                 "and cd into DIR")},
165 #endif
166   {_i ("FILE"), "output", 'o',  _i ("write output to FILE (suffix will be added)")},
167   {0, "relocate", 0, _i ("relocate using directory of lilypond program")},
168   {0, "version", 'v',  _i ("show version number and exit")},
169   {0, "verbose", 'V', _i ("be verbose")},
170   {0, "warranty", 'w',  _i ("show warranty and copyright")},
171   {0, 0, 0, 0}
172 };
173
174 char const *LILYPOND_DATADIR = PACKAGE_DATADIR "/" TOPLEVEL_VERSION;
175
176
177 /* x86 defaults to using 80-bit extended precision arithmetic. This can cause
178    problems because the truncation from 80 bits to 64 bits can occur in
179    unpredictable places. To get around this, we tell the x87 FPU to use only
180    double precision. Note that this is not needed for x86_64 because that uses
181    the SSE unit by default instead of the x87 FPU. */
182 #if ((defined(__x86__) || defined(__i386__)) \
183   && defined(HAVE_FPU_CONTROL_H) && (HAVE_FPU_CONTROL_H == 1))
184
185 #include <fpu_control.h>
186 static void
187 configure_fpu ()
188 {
189   fpu_control_t fpu_control = 0x027f;
190   _FPU_SETCW (fpu_control);
191 }
192
193 #else
194
195 static void
196 configure_fpu ()
197 {
198 }
199
200 #endif /* defined(__x86__) || defined(__i386__) */
201
202
203 static void
204 env_var_info (FILE *out, char const *key)
205 {
206   if (char const *value = getenv (key))
207     fprintf (out, "%s=\"%s\"\n", key, value);
208 }
209
210 static void
211 dir_info (FILE *out)
212 {
213   fputs ("\n", out);
214   fprintf (out, "LILYPOND_DATADIR=\"%s\"\n", LILYPOND_DATADIR);
215   env_var_info (out, "LILYPONDPREFIX");
216   env_var_info (out, "LILYPOND_DATADIR");
217   fprintf (out, "LOCALEDIR=\"%s\"\n", LOCALEDIR);
218
219   fprintf (out, "\nEffective prefix: \"%s\"\n", lilypond_datadir.c_str ());
220
221   if (relocate_binary)
222     {
223       env_var_info (out, "FONTCONFIG_FILE");
224       env_var_info (out, "FONTCONFIG_PATH");
225       env_var_info (out, "GS_FONTPATH");
226       env_var_info (out, "GS_LIB");
227       env_var_info (out, "GUILE_LOAD_PATH");
228       env_var_info (out, "PANGO_RC_FILE");
229       env_var_info (out, "PANGO_PREFIX");
230       env_var_info (out, "PATH");
231     }
232 }
233
234 static void
235 copyright ()
236 {
237   /* Do not update the copyright years here, run `make grand-replace'  */
238   printf ("%s", (_f ("Copyright (c) %s by\n%s  and others.", "1996--2010",
239                      AUTHORS).c_str ()));
240   printf ("\n");
241 }
242
243 static void
244 identify (FILE *out)
245 {
246   fputs (gnu_lilypond_version_string ().c_str (), out);
247   fputs ("\n", out);
248 }
249
250 static void
251 notice ()
252 {
253   identify (stdout);
254   printf ("\n");
255   copyright ();
256   printf ("\n");
257   puts (_f (NOTICE, PROGRAM_NAME).c_str ());
258 }
259
260 LY_DEFINE (ly_usage, "ly:usage",
261            0, 0, 0, (),
262            "Print usage message.")
263 {
264   /* No version number or newline here.  It confuses help2man.  */
265   printf ("%s", (_f ("Usage: %s [OPTION]... FILE...", PROGRAM_NAME).c_str ()));
266   printf ("\n\n");
267   printf ("%s", (_ ("Typeset music and/or produce MIDI from FILE.").c_str ()));
268   printf ("\n\n");
269   printf ("%s", (_ ("LilyPond produces beautiful music notation.").c_str ()));
270   printf ("\n");
271   printf ("%s", (_f ("For more information, see %s", PROGRAM_URL).c_str ()));
272   printf ("\n\n");
273   printf ("%s", (_ ("Options:").c_str ()));
274   printf ("\n");
275   printf ("%s", Long_option_init::table_string (options_static).c_str ());
276   printf ("\n");
277   /* Translators, please translate this string as
278      "Report bugs in English via %s",
279      or if there is a LilyPond users list or forum in your language
280      "Report bugs in English via %s or in YOUR_LANG via URI"  */
281   printf ("%s", (_f ("Report bugs via %s",
282                      "http://post.gmane.org/post.php?group=gmane.comp.gnu.lilypond.bugs"
283                      ).c_str ()));
284   printf ("\n");
285   printf ("\n");
286   return SCM_UNSPECIFIED;
287 }
288
289 static void
290 warranty ()
291 {
292   identify (stdout);
293   printf ("\n");
294   copyright ();
295   printf ("\n");
296   printf ("%s", (_ (WARRANTY).c_str ()));
297 }
298
299 static void
300 prepend_load_path (string dir)
301 {
302   string s = "(set! %load-path (cons \"" + dir + "\" %load-path))";
303   scm_c_eval_string (s.c_str ());
304 }
305
306 void init_global_tweak_registry ();
307 void init_fontconfig ();
308
309 #if HAVE_CHROOT
310 static void
311 do_chroot_jail ()
312 {
313   /* Now we chroot, setuid/setgrp and chdir.  If something goes wrong,
314      we exit (this is a security-sensitive area).  First we split
315      jail_spec into its components, then we retrieve the user/group id
316      (necessarily *before* chroot'ing) and finally we perform the
317      actual actions.  */
318
319   enum Jail
320     {
321       USER_NAME, GROUP_NAME, JAIL, DIR, JAIL_MAX
322     };
323
324   vector<string> components = string_split (jail_spec, ',');
325   if (components.size () != JAIL_MAX)
326     {
327       error (_f ("expected %d arguments with jail, found: %u", JAIL_MAX,
328                  (unsigned) components.size ()));
329       exit (2);
330     }
331
332   /* Hmm.  */
333   errno = 0;
334
335   int uid;
336   if (passwd * passwd = getpwnam (components[USER_NAME].c_str ()))
337     uid = passwd->pw_uid;
338   else
339     {
340       if (errno == 0)
341         error (_f ("no such user: %s", components[USER_NAME]));
342       else
343         error (_f ("cannot get user id from user name: %s: %s",
344                    components[USER_NAME],
345                    strerror (errno)));
346       exit (3);
347     }
348
349   /* Hmm.  */
350   errno = 0;
351
352   int gid;
353   if (group * group = getgrnam (components[GROUP_NAME].c_str ()))
354     gid = group->gr_gid;
355   else
356     {
357       if (errno == 0)
358         error (_f ("no such group: %s", components[GROUP_NAME]));
359       else
360         error (_f ("cannot get group id from group name: %s: %s",
361                    components[GROUP_NAME],
362                    strerror (errno)));
363       exit (3);
364     }
365
366   if (chroot (components[JAIL].c_str ()))
367     {
368       error (_f ("cannot chroot to: %s: %s", components[JAIL],
369                  strerror (errno)));
370       exit (3);
371     }
372
373   if (setgid (gid))
374     {
375       error (_f ("cannot change group id to: %d: %s", gid, strerror (errno)));
376       exit (3);
377     }
378
379   if (setuid (uid))
380     {
381       error (_f ("cannot change user id to: %d: %s", uid, strerror (errno)));
382       exit (3);
383     }
384
385   if (chdir (components[DIR].c_str ()))
386     {
387       error (_f ("cannot change working directory to: %s: %s", components[DIR],
388                  strerror (errno)));
389       exit (3);
390     }
391 }
392 #endif
393
394 static void
395 main_with_guile (void *, int, char **)
396 {
397   /* Engravers use lily.scm contents, need to make Guile find it.
398      Prepend onto GUILE %load-path, very ugh. */
399
400   prepend_load_path (lilypond_datadir);
401   prepend_load_path (lilypond_datadir + "/scm");
402
403   if (be_verbose_global)
404     dir_info (stderr);
405
406   init_scheme_variables_global = "(list " + init_scheme_variables_global + ")";
407   init_scheme_code_global = "(begin " + init_scheme_code_global + ")";
408
409   ly_c_init_guile ();
410   call_constructors ();
411   init_fontconfig ();
412
413   init_freetype ();
414   ly_reset_all_fonts ();
415
416   /* We accept multiple independent music files on the command line to
417      reduce compile time when processing lots of small files.
418      Starting the GUILE engine is very time consuming.  */
419
420   SCM files = SCM_EOL;
421   SCM *tail = &files;
422   while (char const *arg = option_parser->get_next_arg ())
423     {
424       *tail = scm_cons (scm_from_locale_string (arg), SCM_EOL);
425       tail = SCM_CDRLOC (*tail);
426     }
427
428   delete option_parser;
429   option_parser = 0;
430
431 #if HAVE_CHROOT
432   if (!jail_spec.empty ())
433     do_chroot_jail ();
434 #endif
435
436   SCM result = scm_call_1 (ly_lily_module_constant ("lilypond-main"), files);
437   (void) result;
438
439   /* Unreachable.  */
440   exit (0);
441 }
442
443 static void
444 setup_localisation ()
445 {
446 #if HAVE_GETTEXT
447   /* Enable locales */
448   setlocale (LC_ALL, "");
449
450   /* FIXME: check if this is still true.
451      Disable localisation of float values. */
452   setlocale (LC_NUMERIC, "C");
453
454   string localedir = LOCALEDIR;
455   if (char const *env = getenv ("LILYPOND_LOCALEDIR"))
456     localedir = env;
457
458   bindtextdomain ("lilypond", localedir.c_str ());
459   textdomain ("lilypond");
460 #endif
461 }
462
463 static void
464 add_output_format (string format)
465 {
466   if (output_format_global != "")
467     output_format_global += ",";
468   output_format_global += format;
469 }
470
471 static void
472 parse_argv (int argc, char **argv)
473 {
474   bool show_help = false;
475   option_parser = new Getopt_long (argc, argv, options_static);
476   while (Long_option_init const *opt = (*option_parser) ())
477     {
478       switch (opt->shortname_char_)
479         {
480         case 0:
481           if (string (opt->longname_str0_) == "pdf"
482               || string (opt->longname_str0_) == "png"
483               || string (opt->longname_str0_) == "ps")
484             add_output_format (opt->longname_str0_);
485           else if (string (opt->longname_str0_) == "relocate")
486             relocate_binary = true;
487           break;
488
489         case 'd':
490           {
491             string arg (option_parser->optional_argument_str0_);
492             ssize eq = arg.find ('=');
493
494             string key = arg;
495             string val = "#t";
496
497             if (eq != NPOS)
498               {
499                 key = arg.substr (0, eq);
500                 val = arg.substr (eq + 1, arg.length () - 1);
501               }
502
503             init_scheme_variables_global
504               += "(cons \'" + key + " '" + val + ")\n";
505           }
506           break;
507
508         case 'v':
509           notice ();
510           exit (0);
511           break;
512         case 'o':
513           {
514             string s = option_parser->optional_argument_str0_;
515             File_name file_name (s);
516             output_name_global = file_name.to_string ();
517           }
518           break;
519         case 'j':
520           jail_spec = option_parser->optional_argument_str0_;
521           break;
522
523         case 'e':
524           init_scheme_code_global
525             += option_parser->optional_argument_str0_ + string (" ");
526           break;
527         case 'w':
528           warranty ();
529           exit (0);
530           break;
531
532         case 'f':
533           {
534             vector<string> components
535               = string_split (option_parser->optional_argument_str0_, ',');
536             for (vsize i = 0; i < components.size (); i++)
537               add_output_format (components[i]);
538           }
539           break;
540
541         case 'H':
542           dump_header_fieldnames_global
543             .push_back (option_parser->optional_argument_str0_);
544           break;
545         case 'I':
546           global_path.append (option_parser->optional_argument_str0_);
547           break;
548         case 'i':
549           init_name_global = option_parser->optional_argument_str0_;
550           break;
551         case 'h':
552           show_help = true;
553           break;
554         case 'V':
555           be_verbose_global = true;
556           break;
557         default:
558           programming_error (to_string ("unhandled short option: %c",
559                                         opt->shortname_char_));
560           assert (false);
561           break;
562         }
563     }
564
565   if (output_format_global == "")
566     output_format_global = "pdf";
567
568   if (show_help)
569     {
570       ly_usage ();
571       if (be_verbose_global)
572         dir_info (stdout);
573       exit (0);
574     }
575 }
576
577 void
578 setup_guile_env ()
579 {
580   char const *yield = getenv ("LILYPOND_GC_YIELD");
581   bool overwrite = true;
582   if (!yield)
583     {
584       yield = "65";
585       overwrite = false;
586     }
587
588   sane_putenv ("GUILE_MIN_YIELD_1", yield, overwrite);
589   sane_putenv ("GUILE_MIN_YIELD_2", yield, overwrite);
590   sane_putenv ("GUILE_MIN_YIELD_MALLOC", yield, overwrite);
591
592
593   sane_putenv ("GUILE_INIT_SEGMENT_SIZE_1",
594                "10485760", overwrite);
595   sane_putenv ("GUILE_MAX_SEGMENT_SIZE",
596                "104857600", overwrite);
597 }
598
599 vector<string> start_environment_global;
600
601 int
602 main (int argc, char **argv, char **envp)
603 {
604   configure_fpu();
605
606   for (char **p = envp; *p; p++)
607     start_environment_global.push_back(*p);
608
609   if (getenv ("LILYPOND_VERBOSE"))
610     be_verbose_global = true;
611
612   setup_localisation ();
613   parse_argv (argc, argv);
614   if (isatty (STDIN_FILENO))
615     identify (stderr);
616
617   setup_paths (argv[0]);
618   setup_guile_env ();
619
620 #if 0
621   /* Debugging aid.  */
622   try
623     {
624       scm_boot_guile (argc, argv, main_with_guile, 0);
625     }
626   catch (exception e)
627     {
628       error (_f ("exception caught: %s", e.what ()));
629     };
630 #else
631   scm_boot_guile (argc, argv, main_with_guile, 0);
632 #endif
633
634   /* Only reachable if GUILE exits.  That is an error.  */
635   return 1;
636 }