]> git.donarmstrong.com Git - lilypond.git/blob - lily/main.cc
* lily/include/debug.hh: deprecate.
[lilypond.git] / lily / main.cc
1 /*
2   main.cc -- implement main: entrypoints
3
4   source file of the GNU LilyPond music typesetter
5
6   (c)  1997--2002 Han-Wen Nienhuys <hanwen@cs.uu.nl>
7 */
8
9 #include <stdlib.h>
10 #include <stdio.h>
11 #include <assert.h>
12 #include <locale.h>
13 #include <stdio.h>
14
15 #include <iostream>
16
17 #include "config.h"
18
19 #if HAVE_GETTEXT
20 #include <libintl.h>
21 #endif
22
23 #include "lily-guile.hh"
24 #include "lily-version.hh"
25 #include "all-font-metrics.hh"
26 #include "getopt-long.hh"
27 #include "misc.hh"
28 #include "string.hh"
29 #include "main.hh"
30 #include "file-path.hh"
31 #include "file-results.hh"
32 #include "warn.hh"
33 #include "lily-guile.hh"
34 #include "paper-def.hh"
35 #include "midi-def.hh"
36 #include "global-ctor.hh"
37 #include "kpath.hh"
38
39 static int sane_putenv (char const* key, char const* value, bool overwrite = false);
40
41 /*
42   Global options that can be overridden through command line.
43 */
44
45 /* Write dependencies file? */
46 bool dependency_global_b = false;
47
48 /* Prepend to dependencies */
49 String dependency_prefix_global;
50
51 /* Names of header fields to be dumped to a separate file. */
52 Array<String> dump_header_fieldnames_global;
53
54 /* Name of initialisation file. */
55 String init_name_global;
56
57 /* Do not calculate and write paper output? */
58 bool no_paper_global_b = false;
59
60 /* Selected output format.
61    One of tex, ps, scm, as. */
62 String output_format_global = "tex";
63
64 /* Current output name. */
65 String output_name_global;
66
67 /* Run in safe mode? -- FIXME: should be re-analised */
68 bool safe_global_b = false;
69
70 /* Verbose progress indication? */
71 bool verbose_global_b = false;
72
73 /* Scheme code to execute before parsing, after .scm init */
74 String init_scheme_code_string = "(begin #t ";
75
76
77 /*
78   Misc. global stuff.
79  */
80
81
82 All_font_metrics *all_fonts_global_p;
83 int exit_status_global;
84 File_path global_path;
85
86 /* Number of current score output block.  If there's more than one
87    score block, this counter will be added to the output filename. */
88 int score_count_global;
89
90
91
92 /*
93   File globals.
94  */
95
96 /*  The option parser */
97 static Getopt_long *oparser_p_static = 0;
98
99 /*
100  Internationalisation kludge in two steps:
101    * use _i () to get entry in POT file
102    * call gettext () explicitely for actual "translation"
103
104  Note: these messages all start with lower case (ie, don't
105        follow regular localisation guidelines).
106  */
107 static Long_option_init options_static[] = {
108   /* print example usage:  lilypond -e "" ? */
109   {_i ("EXPR"), "evaluate", 'e',_i ("Scheme options: try -e \"(set-lily-option 'help 0)\" for more help.")},
110   /* another bug in option parser: --output=foe is taken as an abbreviation
111      for --output-format */
112   {_i ("EXT"), "format", 'f',  _i ("use output format EXT (tex [default], pdftex, ps, scm or as)")},
113   {0, "help", 'h',  _i ("this help")},
114   {_i ("FIELD"), "header", 'H',  _i ("write header field to BASENAME.FIELD")},
115   {_i ("DIR"), "include", 'I',  _i ("add DIR to search path")},
116   {_i ("FILE"), "init", 'i',  _i ("use FILE as init file")},
117   {0, "dependencies", 'M',  _i ("write Makefile dependencies for every input file")},
118   {0, "no-paper", 'm',  _i ("produce MIDI output only")},
119   {_i ("FILE"), "output", 'o',  _i ("write output to FILE")},
120   {_i ("DIR"), "dep-prefix", 'P',  _i ("prepend DIR to dependencies")},
121 #if 0
122   /*
123     should audit again.
124    */
125   {0, "safe", 's',  _i ("inhibit file output naming and exporting")},
126 #endif
127   {0, "version", 'v',  _i ("print version number")},
128   {0, "verbose", 'V', _i ("verbose")},
129   {0, "warranty", 'w',  _i ("show warranty and copyright")},
130   {0,0,0,0}
131 };
132
133 void
134 identify (FILE* os)
135 {
136   fputs(gnu_lilypond_version_str ().ch_C(), os);
137 }
138
139 void
140 usage ()
141 {
142   
143   /*
144     No version number or newline here. It confuses help2man
145    */
146   std::cout << _f ("Usage: %s [OPTION]... FILE...", "lilypond").ch_C();
147   std::cout << "\n\n";
148   std::cout << _ ("Typeset music and or play MIDI from FILE").ch_C();
149   std::cout << "\n\n";
150   std::cout << 
151 _ (
152 "LilyPond is a music typesetter.  It produces beautiful sheet music\n"
153 "using a high level description file as input.  LilyPond is part of \n"
154 "the GNU Project.\n"
155 ).ch_C();
156
157   std::cout << '\n';
158   std::cout << _ ("Options:").ch_C();
159   std::cout << '\n';
160   std::cout << Long_option_init::table_str (options_static).ch_C();
161   std::cout << '\n';
162   std::cout << _ ("This binary was compiled with the following options:") .ch_C()
163     << " " <<
164 #ifdef NDEBUG
165     "NDEBUG "
166 #endif
167     "\n"
168     "datadir: `" DIR_DATADIR "'\n"
169     "localedir: `" DIR_LOCALEDIR "'\n"
170     "\n";
171
172
173   std::cout << std::endl;
174
175   std::cout << _f ("Report bugs to %s", "bug-lilypond@gnu.org").ch_C() << std::endl;
176 }
177
178 void
179 version ()
180 {
181   identify (stdout);
182   std::cout << '\n';
183   std::cout << _f (""
184   "This is free software.  It is covered by the GNU General Public License,\n"
185   "and you are welcome to change it and/or distribute copies of it under\n"
186   "certain conditions.  Invoke as `%s --warranty' for more information.\n",
187     "lilypond").ch_C();
188   std::cout << std::endl;
189
190   std::cout << _f ("Copyright (c) %s by", "1996--2002").ch_C();
191   std::cout << '\n';
192   std::cout << "  Han-Wen Nienhuys <hanwen@cs.uu.nl>\n";
193   std::cout << "  Jan Nieuwenhuizen <janneke@gnu.org>\n";
194 }
195
196 void
197 notice ()
198 {
199   std::cout << '\n';
200   std::cout << _ ("GNU LilyPond -- The music typesetter").ch_C();
201   std::cout << '\n';
202   std::cout << _f ("Copyright (c) %s by", "1996--2002").ch_C();
203   std::cout << '\n';
204   std::cout << "  Han-Wen Nienhuys <hanwen@cs.uu.nl>\n";
205   std::cout << "  Jan Nieuwenhuizen <janneke@gnu.org>\n";
206   std::cout << '\n';
207   std::cout << _ (
208              "    This program is free software; you can redistribute it and/or\n"
209              "modify it under the terms of the GNU General Public License version 2\n"
210              "as published by the Free Software Foundation.\n"
211              "\n"
212              "    This program is distributed in the hope that it will be useful,\n"
213              "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
214              "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n"
215              "General Public License for more details.\n"
216              "\n"
217              "    You should have received a copy (refer to the file COPYING) of the\n"
218              "GNU General Public License along with this program; if not, write to\n"
219              "the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,\n"
220              "USA.\n").ch_C();
221 }
222
223 String prefix_directory;
224
225 void
226 setup_paths ()
227 {
228   // facilitate binary distributions
229   char const *env_lily = getenv ("LILYPONDPREFIX");
230
231   if (env_lily)
232     prefix_directory = env_lily;
233
234 #if HAVE_GETTEXT
235   setlocale (LC_ALL, ""); /* enable locales */
236   setlocale (LC_NUMERIC, "C"); /* musn't have comma's in TeX output... */
237   String lily_locale_dir;
238   String name (PACKAGE);
239   name.to_lower ();
240
241   /*
242     urg; what *do* we want with $LILYPONDPREFIX, DIR_DATADIR and $prefix/share
243     handy for multiple source-dir runs, though...
244    */
245   if (!prefix_directory.empty_b ())
246     {
247       lily_locale_dir = prefix_directory + "/share/locale";
248       bindtextdomain (name.ch_C (), lily_locale_dir.ch_C ());
249     }
250   else
251     bindtextdomain (name.ch_C (), DIR_LOCALEDIR);
252   textdomain (name.ch_C ());
253 #endif
254
255   global_path.add ("");
256
257
258   /* Adding mf/out make lilypond unchanged source directory, when setting
259      LILYPONDPREFIX to lilypond-x.y.z */
260   char *suffixes[] = {"ly", "afm", "mf/out", "scm", "tfm", "ps", 0};
261
262   if (prefix_directory.empty_b ())
263     prefix_directory =  DIR_DATADIR;
264   for (char **s = suffixes; *s; s++)
265     {
266       String p =  prefix_directory + to_str ('/') + String (*s);
267       global_path.add (p);
268
269 #if !KPATHSEA
270       /* Urg: GNU make's $ (word) index starts at 1 */
271       int i  = 1;
272       while (global_path.try_add (p + to_str (".") + to_str (i)))
273         i++;
274 #endif
275     }
276 }
277
278 /**
279   Make input file name from command argument.
280
281   Path describes file name with added default extension,
282   ".ly" if none.  "-" is stdin.
283  */
284 Path
285 distill_inname (String str)
286 {
287   Path p = split_path (str);
288   if (str.empty_b () || str == "-")
289     p.base = "-";
290   else
291     {
292       String orig_ext = p.ext;
293       char const *extensions[] = {"ly", "fly", "sly", "", 0};
294       for (int i = 0; extensions[i]; i++)
295         {
296           p.ext = orig_ext;
297           if (*extensions[i] && !p.ext.empty_b ())
298             p.ext += ".";
299           p.ext += extensions[i];
300           if (!global_path.find (p.str ()).empty_b ())
301               break;
302         }
303       /* Reshuffle extension */
304       p = split_path (p.str ());
305     }
306   return p;
307 }
308
309 String
310 format_to_ext (String format)
311 {
312   if (format == "tex")
313     /* .lytex change put off */
314     return "tex"; // "lytex";
315   return format;
316 }
317
318 void
319 main_prog (void * , int, char**)
320 {
321   /*
322     need to do this first. Engravers use lily.scm contents.
323    */
324   
325   /*
326     prepend onto GUILE  loadpath.
327
328     Very ugh.
329    */
330
331   init_lily_guile (prefix_directory);
332   std::cout << std::endl;
333
334   call_constructors ();
335   all_fonts_global_p = new All_font_metrics (global_path.str ());
336
337   init_scheme_code_string += ")";
338   gh_eval_str ((char *)init_scheme_code_string.ch_C());
339   
340   int p=0;
341   const char *arg  = oparser_p_static->get_next_arg ();
342
343   if (!arg)
344     {
345       usage ();
346       /* No FILE arguments is now a usage error */
347       exit (2);
348     }
349   else
350     do 
351     {
352       String infile (arg);
353         
354       /* What/when was this supposed to do?
355        It looks like it reset the outname_str_global for every new
356        file, but only if user didn't specify a outname?  Huh?
357
358        // if (outname_str_global == "")
359
360       */
361       {
362         Midi_def::reset_score_count ();
363         Paper_def::reset_score_count ();
364       }
365
366       Path inpath = distill_inname (infile);
367
368       /* By default, use base name of input file for output file name */
369       Path outpath = inpath;
370       if (inpath.str () != "-")
371         outpath.ext = format_to_ext (output_format_global);
372
373       /* By default, write output to cwd; do not copy directory part
374          of input file name */
375       outpath.root = "";
376       outpath.dir = "";
377       
378       if (!output_name_global.empty_b ())
379         outpath = split_path (output_name_global);
380       
381       String init;
382       if (!init_name_global.empty_b ())
383         init = init_name_global;
384       else if (!inpath.ext.empty_b ())
385         init = "init." + inpath.ext;
386       else
387         init = "init.ly";
388         
389       /* Burp: output name communication goes through _global */
390       String save_output_name_global = output_name_global;
391       output_name_global = outpath.str ();
392       do_one_file (init, inpath.str ());
393       output_name_global = save_output_name_global;
394       
395       p++;
396     } while ((arg  = oparser_p_static->get_next_arg ()));
397   delete oparser_p_static;
398   oparser_p_static = 0;
399   exit (exit_status_global);
400 }
401
402
403 static int
404 sane_putenv (char const* key, char const* value, bool overwrite)
405 {
406   if (overwrite || !getenv (key))
407     return putenv ((char*)((String (key) + "=" + value).ch_C ()));
408   return -1;
409 }
410
411 int
412 main (int argc, char **argv)
413 {
414   setup_paths ();
415
416   /* Prepare GUILE for heavy memory usage.  If you have plenty memory,
417      this may speed up GUILE a bit.  If you're short on memory, these
418      settings
419     
420          export GUILE_INIT_SEGMENT_SIZE_1=36000
421          export GUILE_MAX_SEGMENT_SIZE=576000
422
423      may considerably decrease memory footprint (~*0.85), with a small
424      execution time penalty (~*1.10).  However, if this 15% gain in memory
425      usage prevents swapping, the execution time falls drastically. */
426   
427   sane_putenv ("GUILE_INIT_SEGMENT_SIZE_1", "4194304", false);
428   sane_putenv ("GUILE_MAX_SEGMENT_SIZE", "8388608", false);
429
430   ly_init_kpath (argv[0]);
431   
432   oparser_p_static = new Getopt_long (argc, argv, options_static);
433   while (Long_option_init const * opt = (*oparser_p_static) ())
434     {
435       switch (opt->shortname_ch_)
436         {
437         case 'v':
438           version ();
439           exit (0);             // we print a version anyway.
440           break;
441         case 'o':
442           {
443             String s = oparser_p_static->optional_argument_ch_C_;
444             Path p = split_path (s);
445             if (s != "-" && p.ext.empty_b ())
446               p.ext = format_to_ext (output_format_global);
447             output_name_global = p.str ();
448           }
449           break;
450         case 'e':
451           init_scheme_code_string +=
452             oparser_p_static->optional_argument_ch_C_;
453           break;
454         case 'w':
455           notice ();
456           exit (0);
457           break;
458         case 'f':
459             output_format_global = oparser_p_static->optional_argument_ch_C_;
460           break;
461         case 'P':
462             dependency_prefix_global = oparser_p_static->optional_argument_ch_C_;
463           break;
464         case 'H':
465           dump_header_fieldnames_global.push (oparser_p_static->optional_argument_ch_C_);
466           break;
467         case 'I':
468           global_path.push (oparser_p_static->optional_argument_ch_C_);
469           break;
470         case 'i':
471           init_name_global = oparser_p_static->optional_argument_ch_C_;
472           break;
473         case 'h':
474           usage ();
475           exit (0);
476           break;
477         case 'V':
478           verbose_global_b = true;
479           break;
480         case 's':
481           safe_global_b = true;
482           break;
483         case 'M':
484           dependency_global_b = true;
485           break; 
486         case 'm':
487           no_paper_global_b = true;
488           break;
489         default:
490           assert (false);
491           break;
492         }
493     }
494   identify (stderr);
495
496 #ifdef WINNT
497   scm_boot_guile (argc, argv, main_prog, 0);
498 #else
499   scm_boot_guile (argc, argv, (void (*) (void*, int, char**))main_prog, 0);
500 #endif
501
502   return 0;                     // unreachable
503 }
504
505