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