]> git.donarmstrong.com Git - lilypond.git/blob - scripts/lilypond-book.py
cf2008deccd71c6b6abb2d9cefdd3cd7cb11d989
[lilypond.git] / scripts / lilypond-book.py
1 #!@PYTHON@
2
3 '''
4 Example usage:
5
6 test:
7      lilypond-book --filter="tr '[a-z]' '[A-Z]'" BOOK
8
9 convert-ly on book:
10      lilypond-book --filter="convert-ly --no-version --from=1.6.11 -" BOOK
11
12 classic lilypond-book:
13      lilypond-book --process="lilypond" BOOK.tely
14
15 TODO:
16
17     *  this script is too complex. Modularize.
18     
19     *  ly-options: intertext?
20     *  --line-width?
21     *  eps in latex / eps by lilypond -b ps?
22     *  check latex parameters, twocolumn, multicolumn?
23     *  use --png --ps --pdf for making images?
24
25     *  Converting from lilypond-book source, substitute:
26        @mbinclude foo.itely -> @include foo.itely
27        \mbinput -> \input
28
29 '''
30
31 import stat
32 import string
33 import tempfile
34 import commands
35 import os
36 import sys
37 import re
38
39 # Users of python modules should include this snippet
40 # and customize variables below.
41
42 # We'll suffer this path initialization stuff as long as we don't install
43 # our python packages in <prefix>/lib/pythonX.Y
44
45 # If set, LILYPONDPREFIX must take prevalence.
46 # if datadir is not set, we're doing a build and LILYPONDPREFIX.
47
48 ################
49 # RELOCATION
50 ################
51
52 datadir = '@local_lilypond_datadir@'
53 if not os.path.isdir (datadir):
54         datadir = '@lilypond_datadir@'
55
56 sys.path.insert (0, os.path.join (datadir, 'python'))
57
58 if os.environ.has_key ('LILYPONDPREFIX'):
59         datadir = os.environ['LILYPONDPREFIX']
60         while datadir[-1] == os.sep:
61                 datadir= datadir[:-1]
62                 
63         datadir = os.path.join (datadir, "share/lilypond/current/")
64 sys.path.insert (0, os.path.join (datadir, 'python'))
65
66 # dynamic relocation, for GUB binaries.
67 bindir = os.path.split (sys.argv[0])[0]
68
69
70 for prefix_component in ['share', 'lib']:
71         datadir = os.path.abspath (bindir + '/../%s/lilypond/current/python/' % prefix_component)
72         sys.path.insert (0, datadir)
73
74
75 import lilylib as ly
76 import fontextract
77 global _;_=ly._
78
79
80 # Lilylib globals.
81 program_version = '@TOPLEVEL_VERSION@'
82 program_name = os.path.basename (sys.argv[0])
83
84 original_dir = os.getcwd ()
85 backend = 'ps'
86
87 help_summary = _ (
88 '''Process LilyPond snippets in hybrid HTML, LaTeX, or texinfo document.
89
90 Example usage:
91
92    lilypond-book --filter="tr '[a-z]' '[A-Z]'" BOOK
93    lilypond-book --filter="convert-ly --no-version --from=2.0.0 -" BOOK
94    lilypond-book --process='lilypond -I include' BOOK
95 ''')
96
97 authors = ('Jan Nieuwenhuizen <janneke@gnu.org>',
98              'Han-Wen Nienhuys <hanwen@cs.uu.nl>')
99
100         
101 ################################################################
102 def exit (i):
103         if global_options.verbose:
104                 raise _ ('Exiting (%d)...') % i
105         else:
106                 sys.exit (i)
107
108 def identify ():
109         sys.stdout.write ('%s (GNU LilyPond) %s\n' % (program_name, program_version))
110
111 progress=sys.stderr.write
112
113 def warning (s):
114         sys.stderr.write (program_name + ": " + _ ("warning: %s") % s + '\n')
115
116 def error (s):
117         sys.stderr.write (program_name + ": " + _ ("error: %s") % s + '\n')
118
119 def warranty ():
120         identify ()
121         sys.stdout.write ('''
122 %s
123
124 %s
125
126 %s
127 %s
128 '''  ( _('Copyright (c) %s by') % '2001--2006',
129        authors,
130        _('Distributed under terms of the GNU General Public License.'),
131        _('It comes with NO WARRANTY.')))
132
133
134 def get_option_parser ():
135         p = ly.get_option_parser (usage='lilypond-book [OPTIONS] FILE',
136                                   version="@TOPLEVEL_VERSION@",
137                                   description=help_summary)
138
139         p.add_option ('-F', '--filter', metavar=_ ("FILTER"),
140                       action="store",
141                       dest="filter_cmd",
142                       help=_ ("pipe snippets through FILTER [convert-ly -n -]"),
143                       default=None)
144         p.add_option ('-f', '--format', help=_('''use output format FORMAT (texi [default], texi-html, latex, html)'''),
145                       action='store')
146         p.add_option ("-I", '--include', help=_('add DIR to include path'),
147                       metavar="DIR",
148                       action='append', dest='include_path',
149                       default=[os.path.abspath (os.getcwd ())])
150         
151         p.add_option ("-o", '--output', help=_('write output to DIR'),
152                       metavar="DIR",
153                       action='store', dest='output_name', default=None)
154         p.add_option ('-P', '--process', metavar=_("COMMAND"),
155                       help = _ ("process ly_files using COMMAND FILE..."),
156                       action='store', 
157                       dest='process_cmd', default='lilypond -b eps')
158         
159         p.add_option ('', '--psfonts', action="store_true", dest="psfonts",
160                       help=_ ('''extract all PostScript fonts into INPUT.psfonts for LaTeX'''
161                               '''must use this with dvips -h INPUT.psfonts'''),
162                       default=None)
163         p.add_option ('-V', '--verbose', help=_("be verbose"), action="store_true",
164                       dest="verbose")
165                       
166         p.add_option ('-w', '--warranty',
167                       help=_("show warranty and copyright"),
168                       action='store_true')
169
170         
171         p.add_option_group  ('bugs',
172                              description='''Report bugs via http://post.gmane.org/post.php'''
173                              '''?group=gmane.comp.gnu.lilypond.bugs\n''')
174         
175         return p
176
177 lilypond_binary = os.path.join ('@bindir@', 'lilypond')
178
179 # Only use installed binary when we are installed too.
180 if '@bindir@' == ('@' + 'bindir@') or not os.path.exists (lilypond_binary):
181         lilypond_binary = 'lilypond'
182
183 global_options = None
184
185
186 default_ly_options = { 'alt': "[image of music]" }
187
188 #
189 # Is this pythonic?  Personally, I find this rather #define-nesque. --hwn
190 #
191 AFTER = 'after'
192 BEFORE = 'before'
193 EXAMPLEINDENT = 'exampleindent'
194 FILTER = 'filter'
195 FRAGMENT = 'fragment'
196 HTML = 'html'
197 INDENT = 'indent'
198 LATEX = 'latex'
199 LAYOUT = 'layout'
200 LINE_WIDTH = 'line-width'
201 NOFRAGMENT = 'nofragment'
202 NOINDENT = 'noindent'
203 NOQUOTE = 'noquote'
204 NOTES = 'body'
205 NOTIME = 'notime'
206 OUTPUT = 'output'
207 OUTPUTIMAGE = 'outputimage'
208 PACKED = 'packed'
209 PAPER = 'paper'
210 PREAMBLE = 'preamble'
211 PRINTFILENAME = 'printfilename'
212 QUOTE = 'quote'
213 RAGGED_RIGHT = 'ragged-right'
214 RELATIVE = 'relative'
215 STAFFSIZE = 'staffsize'
216 TEXIDOC = 'texidoc'
217 TEXINFO = 'texinfo'
218 VERBATIM = 'verbatim'
219 FONTLOAD = 'fontload'
220 FILENAME = 'filename'
221 ALT = 'alt'
222
223
224 # NOTIME has no opposite so it isn't part of this dictionary.
225 # NOQUOTE is used internally only.
226 no_options = {
227         NOFRAGMENT: FRAGMENT,
228         NOINDENT: INDENT,
229 }
230
231
232 # Recognize special sequences in the input.
233 #
234 #   (?P<name>regex) -- Assign result of REGEX to NAME.
235 #   *? -- Match non-greedily.
236 #   (?m) -- Multiline regex: Make ^ and $ match at each line.
237 #   (?s) -- Make the dot match all characters including newline.
238 #   (?x) -- Ignore whitespace in patterns.
239 no_match = 'a\ba'
240 snippet_res = {
241         ##
242         HTML: {
243                 'include':
244                   no_match,
245
246                 'lilypond':
247                   r'''(?mx)
248                     (?P<match>
249                     <lilypond
250                       (\s*(?P<options>.*?)\s*:)?\s*
251                       (?P<code>.*?)
252                     />)''',
253
254                 'lilypond_block':
255                   r'''(?msx)
256                     (?P<match>
257                     <lilypond
258                       \s*(?P<options>.*?)\s*
259                     >
260                     (?P<code>.*?)
261                     </lilypond>)''',
262
263                 'lilypond_file':
264                   r'''(?mx)
265                     (?P<match>
266                     <lilypondfile
267                       \s*(?P<options>.*?)\s*
268                     >
269                     \s*(?P<filename>.*?)\s*
270                     </lilypondfile>)''',
271
272                 'multiline_comment':
273                   r'''(?smx)
274                     (?P<match>
275                     \s*(?!@c\s+)
276                     (?P<code><!--\s.*?!-->)
277                     \s)''',
278
279                 'singleline_comment':
280                   no_match,
281
282                 'verb':
283                   r'''(?x)
284                     (?P<match>
285                       (?P<code><pre>.*?</pre>))''',
286
287                 'verbatim':
288                   r'''(?x)
289                     (?s)
290                     (?P<match>
291                       (?P<code><pre>\s.*?</pre>\s))''',
292         },
293
294         ##
295         LATEX: {
296                 'include':
297                   r'''(?smx)
298                     ^[^%\n]*?
299                     (?P<match>
300                     \\input\s*{
301                       (?P<filename>\S+?)
302                     })''',
303
304                 'lilypond':
305                   r'''(?smx)
306                     ^[^%\n]*?
307                     (?P<match>
308                     \\lilypond\s*(
309                     \[
310                       \s*(?P<options>.*?)\s*
311                     \])?\s*{
312                       (?P<code>.*?)
313                     })''',
314
315                 'lilypond_block':
316                   r'''(?smx)
317                     ^[^%\n]*?
318                     (?P<match>
319                     \\begin\s*(
320                     \[
321                       \s*(?P<options>.*?)\s*
322                     \])?\s*{lilypond}
323                       (?P<code>.*?)
324                     ^[^%\n]*?
325                     \\end\s*{lilypond})''',
326
327                 'lilypond_file':
328                   r'''(?smx)
329                     ^[^%\n]*?
330                     (?P<match>
331                     \\lilypondfile\s*(
332                     \[
333                       \s*(?P<options>.*?)\s*
334                     \])?\s*\{
335                       (?P<filename>\S+?)
336                     })''',
337
338                 'multiline_comment':
339                   no_match,
340
341                 'singleline_comment':
342                   r'''(?mx)
343                     ^.*?
344                     (?P<match>
345                       (?P<code>
346                       %.*$\n+))''',
347
348                 'verb':
349                   r'''(?mx)
350                     ^[^%\n]*?
351                     (?P<match>
352                       (?P<code>
353                       \\verb(?P<del>.)
354                         .*?
355                       (?P=del)))''',
356
357                 'verbatim':
358                   r'''(?msx)
359                     ^[^%\n]*?
360                     (?P<match>
361                       (?P<code>
362                       \\begin\s*{verbatim}
363                         .*?
364                       \\end\s*{verbatim}))''',
365         },
366
367         ##
368         TEXINFO: {
369                 'include':
370                   r'''(?mx)
371                     ^(?P<match>
372                     @include\s+
373                       (?P<filename>\S+))''',
374
375                 'lilypond':
376                   r'''(?smx)
377                     ^[^\n]*?(?!@c\s+)[^\n]*?
378                     (?P<match>
379                     @lilypond\s*(
380                     \[
381                       \s*(?P<options>.*?)\s*
382                     \])?\s*{
383                       (?P<code>.*?)
384                     })''',
385
386                 'lilypond_block':
387                   r'''(?msx)
388                     ^(?P<match>
389                     @lilypond\s*(
390                     \[
391                       \s*(?P<options>.*?)\s*
392                     \])?\s+?
393                     ^(?P<code>.*?)
394                     ^@end\s+lilypond)\s''',
395
396                 'lilypond_file':
397                   r'''(?mx)
398                     ^(?P<match>
399                     @lilypondfile\s*(
400                     \[
401                       \s*(?P<options>.*?)\s*
402                     \])?\s*{
403                       (?P<filename>\S+)
404                     })''',
405
406                 'multiline_comment':
407                   r'''(?smx)
408                     ^(?P<match>
409                       (?P<code>
410                       @ignore\s
411                         .*?
412                       @end\s+ignore))\s''',
413
414                 'singleline_comment':
415                   r'''(?mx)
416                     ^.*
417                     (?P<match>
418                       (?P<code>
419                       @c([ \t][^\n]*|)\n))''',
420
421         # Don't do this: It interferes with @code{@{}.
422         #       'verb': r'''(?P<code>@code{.*?})''',
423
424                 'verbatim':
425                   r'''(?sx)
426                     (?P<match>
427                       (?P<code>
428                       @example
429                         \s.*?
430                       @end\s+example\s))''',
431         },
432 }
433
434
435
436
437 format_res = {
438         HTML: {
439                 'intertext': r',?\s*intertext=\".*?\"',
440                 'option_sep': '\s*',
441         },
442
443         LATEX: {
444                 'intertext': r',?\s*intertext=\".*?\"',
445                 'option_sep': '\s*,\s*',
446         },
447
448         TEXINFO: {
449                 'intertext': r',?\s*intertext=\".*?\"',
450                 'option_sep': '\s*,\s*',
451         },
452 }
453
454 # Options without a pattern in ly_options.
455 simple_options = [
456         EXAMPLEINDENT,
457         FRAGMENT,
458         NOFRAGMENT,
459         NOINDENT,
460         PRINTFILENAME,
461         TEXIDOC,
462         VERBATIM,
463         FONTLOAD,
464         FILENAME,
465         ALT
466 ]
467
468 ly_options = {
469         ##
470         NOTES: {
471                 RELATIVE: r'''\relative c%(relative_quotes)s''',
472         },
473
474         ##
475         PAPER: {
476                 INDENT: r'''indent = %(indent)s''',
477
478                 LINE_WIDTH: r'''line-width = %(line-width)s''',
479
480                 QUOTE: r'''line-width = %(line-width)s - 2.0 * %(exampleindent)s''',
481
482                 RAGGED_RIGHT: r'''ragged-right = ##t''',
483
484                 PACKED: r'''packed = ##t''',
485         },
486
487         ##
488         LAYOUT: {
489                 NOTIME: r'''
490   \context {
491     \Score
492     timing = ##f
493   }
494   \context {
495     \Staff
496     \remove Time_signature_engraver
497   }''',
498         },
499
500         ##
501         PREAMBLE: {
502                 STAFFSIZE: r'''#(set-global-staff-size %(staffsize)s)''',
503         },
504 }
505
506 output = {
507         ##
508         HTML: {
509                 FILTER: r'''<lilypond %(options)s>
510 %(code)s
511 </lilypond>
512 ''',
513
514                 AFTER: r'''
515   </a>
516 </p>''',
517
518                 BEFORE: r'''<p>
519   <a href="%(base)s.ly">''',
520
521                 OUTPUT: r'''
522     <img align="center" valign="center"
523          border="0" src="%(image)s" alt="%(alt)s">''',
524
525                 PRINTFILENAME: '<p><tt><a href="%(base)s.ly">%(filename)s</a></tt></p>',
526
527                 QUOTE: r'''<blockquote>
528 %(str)s
529 </blockquote>
530 ''',
531
532                 VERBATIM: r'''<pre>
533 %(verb)s</pre>''',
534         },
535
536         ##
537         LATEX: {
538                 OUTPUT: r'''{%%
539 \parindent 0pt%%
540 \catcode`\@=12%%
541 \ifx\preLilyPondExample \undefined%%
542   \relax%%
543 \else%%
544   \preLilyPondExample%%
545 \fi%%
546 \def\lilypondbook{}%%
547 \input %(base)s-systems.tex%%
548 \ifx\postLilyPondExample \undefined%%
549   \relax%%
550 \else%%
551   \postLilyPondExample%%
552 \fi%%
553 }''',
554
555                 PRINTFILENAME: '''\\texttt{%(filename)s}
556         ''',
557
558                 QUOTE: r'''\begin{quotation}%(str)s
559 \end{quotation}''',
560
561                 VERBATIM: r'''\noindent
562 \begin{verbatim}%(verb)s\end{verbatim}''',
563
564                 FILTER: r'''\begin{lilypond}[%(options)s]
565 %(code)s
566 \end{lilypond}''',
567         },
568
569         ##
570         TEXINFO: {
571                 FILTER: r'''@lilypond[%(options)s]
572 %(code)s
573 @lilypond''',
574
575                 OUTPUT: r'''
576 @iftex
577 @include %(base)s-systems.texi
578 @end iftex
579 ''',
580
581                 OUTPUTIMAGE: r'''@noindent
582 @ifinfo
583 @image{%(base)s,,,%(alt)s,%(ext)s}
584 @end ifinfo
585 @html
586 <p>
587   <a href="%(base)s.ly">
588     <img align="center" valign="center"
589          border="0" src="%(image)s" alt="%(alt)s">
590   </a>
591 </p>
592 @end html
593 ''',
594
595                 PRINTFILENAME: '''@file{%(filename)s}
596         ''',
597
598                 QUOTE: r'''@quotation
599 %(str)s@end quotation
600 ''',
601
602                 NOQUOTE: r'''@format
603 %(str)s@end format
604 ''',
605
606                 VERBATIM: r'''@exampleindent 0
607 @example
608 %(verb)s@end example
609 ''',
610         },
611 }
612
613 #
614 # Maintain line numbers.
615 #
616
617 ## TODO
618 if 0:
619         for f in [HTML, LATEX]:
620                 for s in (QUOTE, VERBATIM):
621                         output[f][s] = output[f][s].replace("\n"," ")
622
623
624 PREAMBLE_LY = '''%%%% Generated by %(program_name)s
625 %%%% Options: [%(option_string)s]
626
627 #(set! toplevel-score-handler print-score-with-defaults)
628 #(set! toplevel-music-handler
629   (lambda (p m)
630    (if (not (eq? (ly:music-property m \'void) #t))
631         (print-score-with-defaults
632          p (scorify-music m p)))))
633
634 #(ly:set-option (quote no-point-and-click))
635 #(define inside-lilypond-book #t)
636 #(define version-seen? #t)
637 %(preamble_string)s
638
639
640
641
642
643
644 %% ****************************************************************
645 %% Start cut-&-pastable-section 
646 %% ****************************************************************
647
648 \paper {
649   #(define dump-extents #t)
650   %(font_dump_setting)s
651   %(paper_string)s
652 }
653
654 \layout {
655   %(layout_string)s
656 }
657 '''
658
659 FRAGMENT_LY = r'''
660 %(notes_string)s
661 {
662
663
664 %% ****************************************************************
665 %% ly snippet contents follows:
666 %% ****************************************************************
667 %(code)s
668
669
670 %% ****************************************************************
671 %% end ly snippet
672 %% ****************************************************************
673 }
674 '''
675
676 FULL_LY = '''
677
678
679 %% ****************************************************************
680 %% ly snippet:
681 %% ****************************************************************
682 %(code)s
683
684
685 %% ****************************************************************
686 %% end ly snippet
687 %% ****************************************************************
688 '''
689
690 texinfo_line_widths = {
691         '@afourpaper': '160\\mm',
692         '@afourwide': '6.5\\in',
693         '@afourlatex': '150\\mm',
694         '@smallbook': '5\\in',
695         '@letterpaper': '6\\in',
696 }
697
698 def classic_lilypond_book_compatibility (key, value):
699         if key == 'singleline' and value == None:
700                 return (RAGGED_RIGHT, None)
701
702         m = re.search ('relative\s*([-0-9])', key)
703         if m:
704                 return ('relative', m.group (1))
705
706         m = re.match ('([0-9]+)pt', key)
707         if m:
708                 return ('staffsize', m.group (1))
709
710         if key == 'indent' or key == 'line-width':
711                 m = re.match ('([-.0-9]+)(cm|in|mm|pt|staffspace)', value)
712                 if m:
713                         f = float (m.group (1))
714                         return (key, '%f\\%s' % (f, m.group (2)))
715
716         return (None, None)
717
718 def find_file (name):
719         for i in global_options.include_path:
720                 full = os.path.join (i, name)
721                 if os.path.exists (full):
722                         return full
723                 
724         error (_ ("file not found: %s") % name + '\n')
725         exit (1)
726         return ''
727
728 def verbatim_html (s):
729         return re.sub ('>', '&gt;',
730                        re.sub ('<', '&lt;',
731                                re.sub ('&', '&amp;', s)))
732
733 def verbatim_texinfo (s):
734         return re.sub ('{', '@{',
735                        re.sub ('}', '@}',
736                                re.sub ('@', '@@', s)))
737
738 def split_options (option_string):
739         if option_string:
740                 if global_options.format == HTML:
741                         options = re.findall('[\w\.-:]+(?:\s*=\s*(?:"[^"]*"|\'[^\']*\'|\S+))?',option_string)
742                         for i in range(len(options)):
743                                 options[i] = re.sub('^([^=]+=\s*)(?P<q>["\'])(.*)(?P=q)','\g<1>\g<3>',options[i])
744                         return options
745                 else:
746                         return re.split (format_res[global_options.format]['option_sep'],
747                                          option_string)
748         return []
749
750 def set_default_options (source):
751         global default_ly_options
752         if not default_ly_options.has_key (LINE_WIDTH):
753                 if global_options.format == LATEX:
754                         textwidth = get_latex_textwidth (source)
755                         default_ly_options[LINE_WIDTH] = \
756                           '''%.0f\\pt''' % textwidth
757                 elif global_options.format == TEXINFO:
758                         for (k, v) in texinfo_line_widths.items ():
759                                 # FIXME: @layout is usually not in
760                                 # chunk #0:
761                                 #
762                                 #  \input texinfo @c -*-texinfo-*-
763                                 #
764                                 # Bluntly search first K items of
765                                 # source.
766                                 # s = chunks[0].replacement_text ()
767                                 if re.search (k, source[:1024]):
768                                         default_ly_options[LINE_WIDTH] = v
769                                         break
770
771 class Chunk:
772         def replacement_text (self):
773                 return ''
774
775         def filter_text (self):
776                 return self.replacement_text ()
777
778         def ly_is_outdated (self):
779                 return 0
780
781         def png_is_outdated (self):
782                 return 0
783
784         def is_plain (self):
785                 return False
786         
787 class Substring (Chunk):
788         def __init__ (self, source, start, end, line_number):
789                 self.source = source
790                 self.start = start
791                 self.end = end
792                 self.line_number = line_number
793                 self.override_text = None
794                 
795         def is_plain (self):
796                 return True
797
798         def replacement_text (self):
799                 if self.override_text:
800                         return self.override_text
801                 else:
802                         return self.source[self.start:self.end]
803
804 class Snippet (Chunk):
805         def __init__ (self, type, match, format, line_number):
806                 self.type = type
807                 self.match = match
808                 self.hash = 0
809                 self.option_dict = {}
810                 self.format = format
811                 self.line_number = line_number
812
813         def replacement_text (self):
814                 return self.match.group ('match')
815
816         def substring (self, s):
817                 return self.match.group (s)
818
819         def __repr__ (self):
820                 return `self.__class__` + ' type = ' + self.type
821
822 class Include_snippet (Snippet):
823         def processed_filename (self):
824                 f = self.substring ('filename')
825                 return os.path.splitext (f)[0] + format2ext[global_options.format]
826
827         def replacement_text (self):
828                 s = self.match.group ('match')
829                 f = self.substring ('filename')
830
831                 return re.sub (f, self.processed_filename (), s)
832
833 class Lilypond_snippet (Snippet):
834         def __init__ (self, type, match, format, line_number):
835                 Snippet.__init__ (self, type, match, format, line_number)
836                 os = match.group ('options')
837                 self.do_options (os, self.type)
838
839         def ly (self):
840                 return self.substring ('code')
841
842         def full_ly (self):
843                 s = self.ly ()
844                 if s:
845                         return self.compose_ly (s)
846                 return ''
847
848         def do_options (self, option_string, type):
849                 self.option_dict = {}
850
851                 options = split_options (option_string)
852
853                 for i in options:
854                         if string.find (i, '=') > 0:
855                                 (key, value) = re.split ('\s*=\s*', i)
856                                 self.option_dict[key] = value
857                         else:
858                                 if i in no_options.keys ():
859                                         if no_options[i] in self.option_dict.keys ():
860                                                 del self.option_dict[no_options[i]]
861                                 else:
862                                         self.option_dict[i] = None
863
864                 has_line_width = self.option_dict.has_key (LINE_WIDTH)
865                 no_line_width_value = 0
866
867                 # If LINE_WIDTH is used without parameter, set it to default.
868                 if has_line_width and self.option_dict[LINE_WIDTH] == None:
869                         no_line_width_value = 1
870                         del self.option_dict[LINE_WIDTH]
871
872                 for i in default_ly_options.keys ():
873                         if i not in self.option_dict.keys ():
874                                 self.option_dict[i] = default_ly_options[i]
875
876                 if not has_line_width:
877                         if type == 'lilypond' or FRAGMENT in self.option_dict.keys ():
878                                 self.option_dict[RAGGED_RIGHT] = None
879
880                         if type == 'lilypond':
881                                 if LINE_WIDTH in self.option_dict.keys ():
882                                         del self.option_dict[LINE_WIDTH]
883                         else:
884                                 if RAGGED_RIGHT in self.option_dict.keys ():
885                                         if LINE_WIDTH in self.option_dict.keys ():
886                                                 del self.option_dict[LINE_WIDTH]
887
888                         if QUOTE in self.option_dict.keys () or type == 'lilypond':
889                                 if LINE_WIDTH in self.option_dict.keys ():
890                                         del self.option_dict[LINE_WIDTH]
891
892                 if not INDENT in self.option_dict.keys ():
893                         self.option_dict[INDENT] = '0\\mm'
894
895                 # The QUOTE pattern from ly_options only emits the `line-width'
896                 # keyword.
897                 if has_line_width and QUOTE in self.option_dict.keys ():
898                         if no_line_width_value:
899                                 del self.option_dict[LINE_WIDTH]
900                         else:
901                                 del self.option_dict[QUOTE]
902
903         def compose_ly (self, code):
904                 if FRAGMENT in self.option_dict.keys ():
905                         body = FRAGMENT_LY
906                 else:
907                         body = FULL_LY
908
909                 # Defaults.
910                 relative = 1
911                 override = {}
912                 # The original concept of the `exampleindent' option is broken.
913                 # It is not possible to get a sane value for @exampleindent at all
914                 # without processing the document itself.  Saying
915                 #
916                 #   @exampleindent 0
917                 #   @example
918                 #   ...
919                 #   @end example
920                 #   @exampleindent 5
921                 #
922                 # causes ugly results with the DVI backend of texinfo since the
923                 # default value for @exampleindent isn't 5em but 0.4in (or a smaller
924                 # value).  Executing the above code changes the environment
925                 # indentation to an unknown value because we don't know the amount
926                 # of 1em in advance since it is font-dependent.  Modifying
927                 # @exampleindent in the middle of a document is simply not
928                 # supported within texinfo.
929                 #
930                 # As a consequence, the only function of @exampleindent is now to
931                 # specify the amount of indentation for the `quote' option.
932                 #
933                 # To set @exampleindent locally to zero, we use the @format
934                 # environment for non-quoted snippets.
935                 override[EXAMPLEINDENT] = r'0.4\in'
936                 override[LINE_WIDTH] = texinfo_line_widths['@smallbook']
937                 override.update (default_ly_options)
938
939                 option_list = []
940                 for (key, value) in self.option_dict.items ():
941                         if value == None:
942                                 option_list.append (key)
943                         else:
944                                 option_list.append (key + '=' + value)
945                 option_string = string.join (option_list, ',')
946
947                 compose_dict = {}
948                 compose_types = [NOTES, PREAMBLE, LAYOUT, PAPER]
949                 for a in compose_types:
950                         compose_dict[a] = []
951
952                 for (key, value) in self.option_dict.items ():
953                         (c_key, c_value) = \
954                           classic_lilypond_book_compatibility (key, value)
955                         if c_key:
956                                 if c_value:
957                                         warning \
958                                           (_ ("deprecated ly-option used: %s=%s" \
959                                             % (key, value)))
960                                         warning \
961                                           (_ ("compatibility mode translation: %s=%s" \
962                                             % (c_key, c_value)))
963                                 else:
964                                         warning \
965                                           (_ ("deprecated ly-option used: %s" \
966                                             % key))
967                                         warning \
968                                           (_ ("compatibility mode translation: %s" \
969                                             % c_key))
970
971                                 (key, value) = (c_key, c_value)
972
973                         if value:
974                                 override[key] = value
975                         else:
976                                 if not override.has_key (key):
977                                         override[key] = None
978
979                         found = 0
980                         for type in compose_types:
981                                 if ly_options[type].has_key (key):
982                                         compose_dict[type].append (ly_options[type][key])
983                                         found = 1
984                                         break
985
986                         if not found and key not in simple_options:
987                                 warning (_ ("ignoring unknown ly option: %s") % key)
988
989                 # URGS
990                 if RELATIVE in override.keys () and override[RELATIVE]:
991                         relative = int (override[RELATIVE])
992
993                 relative_quotes = ''
994
995                 # 1 = central C
996                 if relative < 0:
997                         relative_quotes += ',' * (- relative)
998                 elif relative > 0:
999                         relative_quotes += "'" * relative
1000
1001                 paper_string = string.join (compose_dict[PAPER],
1002                                             '\n  ') % override
1003                 layout_string = string.join (compose_dict[LAYOUT],
1004                                              '\n  ') % override
1005                 notes_string = string.join (compose_dict[NOTES],
1006                                             '\n  ') % vars ()
1007                 preamble_string = string.join (compose_dict[PREAMBLE],
1008                                                '\n  ') % override
1009                 
1010                 font_dump_setting = ''
1011                 if FONTLOAD in self.option_dict:
1012                         font_dump_setting = '#(define-public force-eps-font-include #t)\n'
1013
1014                 d = globals().copy()
1015                 d.update (locals())
1016                 return (PREAMBLE_LY + body) % d
1017
1018         # TODO: Use md5?
1019         def get_hash (self):
1020                 if not self.hash:
1021                         self.hash = abs (hash (self.full_ly ()))
1022                 return self.hash
1023
1024         def basename (self):
1025                 if FILENAME in self.option_dict:
1026                         return self.option_dict[FILENAME]
1027                 if global_options.use_hash:
1028                         return 'lily-%d' % self.get_hash ()
1029                 raise 'to be done'
1030
1031         def write_ly (self):
1032                 outf = open (self.basename () + '.ly', 'w')
1033                 outf.write (self.full_ly ())
1034
1035                 open (self.basename () + '.txt', 'w').write ('image of music')
1036
1037         def ly_is_outdated (self):
1038                 base = self.basename ()
1039
1040                 tex_file = '%s.tex' % base
1041                 eps_file = '%s.eps' % base
1042                 system_file = '%s-systems.tex' % base
1043                 ly_file = '%s.ly' % base
1044                 ok = os.path.exists (ly_file) \
1045                      and os.path.exists (system_file)\
1046                      and os.stat (system_file)[stat.ST_SIZE] \
1047                      and re.match ('% eof', open (system_file).readlines ()[-1])
1048                 if ok and (not global_options.use_hash or FILENAME in self.option_dict):
1049                         ok = (self.full_ly () == open (ly_file).read ())
1050                 if ok:
1051                         # TODO: Do something smart with target formats
1052                         #       (ps, png) and m/ctimes.
1053                         return None
1054                 return self
1055
1056         def png_is_outdated (self):
1057                 base = self.basename ()
1058                 ok = self.ly_is_outdated ()
1059                 if global_options.format in (HTML, TEXINFO):
1060                         ok = ok and os.path.exists (base + '.eps')
1061
1062                         page_count = 0
1063                         if ok:
1064                                 page_count = ly.ps_page_count (base + '.eps')
1065                         
1066                         if page_count == 1:
1067                                 ok = ok and os.path.exists (base + '.png')
1068                         elif page_count > 1:
1069                                 for a in range (1, page_count + 1):
1070                                                 ok = ok and os.path.exists (base + '-page%d.png' % a)
1071                                 
1072                 return not ok
1073         
1074         def texstr_is_outdated (self):
1075                 if backend == 'ps':
1076                         return 0
1077
1078                 base = self.basename ()
1079                 ok = self.ly_is_outdated ()
1080                 ok = ok and (os.path.exists (base + '.texstr'))
1081                 return not ok
1082
1083         def filter_text (self):
1084                 code = self.substring ('code')
1085                 s = run_filter (code)
1086                 d = {
1087                         'code': s,
1088                         'options': self.match.group ('options')
1089                 }
1090                 # TODO
1091                 return output[self.format][FILTER] % d
1092
1093         def replacement_text (self):
1094                 func = Lilypond_snippet.__dict__['output_' + self.format]
1095                 return func (self)
1096
1097         def get_images (self):
1098                 base = self.basename ()
1099                 # URGUGHUGHUGUGH
1100                 single = '%(base)s.png' % vars ()
1101                 multiple = '%(base)s-page1.png' % vars ()
1102                 images = (single,)
1103                 if os.path.exists (multiple) \
1104                    and (not os.path.exists (single) \
1105                         or (os.stat (multiple)[stat.ST_MTIME] \
1106                             > os.stat (single)[stat.ST_MTIME])):
1107                         count = ly.ps_page_count ('%(base)s.eps' % vars ())
1108                         images = ['%s-page%d.png' % (base, a) for a in range (1, count+1)]
1109                         images = tuple (images)
1110                 return images
1111
1112         def output_html (self):
1113                 str = ''
1114                 base = self.basename ()
1115                 if global_options.format == HTML:
1116                         str += self.output_print_filename (HTML)
1117                         if VERBATIM in self.option_dict:
1118                                 verb = verbatim_html (self.substring ('code'))
1119                                 str += output[HTML][VERBATIM] % vars ()
1120                         if QUOTE in self.option_dict:
1121                                 str = output[HTML][QUOTE] % vars ()
1122
1123                 str += output[HTML][BEFORE] % vars ()
1124                 for image in self.get_images ():
1125                         (base, ext) = os.path.splitext (image)
1126                         alt = self.option_dict[ALT]
1127                         str += output[HTML][OUTPUT] % vars ()
1128                 str += output[HTML][AFTER] % vars ()
1129                 return str
1130
1131         def output_info (self):
1132                 str = ''
1133                 for image in self.get_images ():
1134                         (base, ext) = os.path.splitext (image)
1135
1136                         # URG, makeinfo implicitly prepends dot to extension.
1137                         # Specifying no extension is most robust.
1138                         ext = ''
1139                         alt = self.option_dict[ALT]
1140                         str += output[TEXINFO][OUTPUTIMAGE] % vars ()
1141
1142                 base = self.basename ()
1143                 str += output[global_options.format][OUTPUT] % vars ()
1144                 return str
1145
1146         def output_latex (self):
1147                 str = ''
1148                 base = self.basename ()
1149                 if global_options.format == LATEX:
1150                         str += self.output_print_filename (LATEX)
1151                         if VERBATIM in self.option_dict:
1152                                 verb = self.substring ('code')
1153                                 str += (output[LATEX][VERBATIM] % vars ())
1154                 
1155                 str += (output[LATEX][OUTPUT] % vars ())
1156
1157                 ## todo: maintain breaks
1158                 if 0:
1159                         breaks = self.ly ().count ("\n")
1160                         str += "".ljust (breaks, "\n").replace ("\n","%\n")
1161                 
1162                 if QUOTE in self.option_dict:
1163                         str = output[LATEX][QUOTE] % vars ()
1164                 return str
1165
1166         def output_print_filename (self, format):
1167                 str = ''
1168                 if PRINTFILENAME in self.option_dict:
1169                         base = self.basename ()
1170                         filename = self.substring ('filename')
1171                         str = output[global_options.format][PRINTFILENAME] % vars ()
1172
1173                 return str
1174
1175         def output_texinfo (self):
1176                 str = ''
1177                 if self.output_print_filename (TEXINFO):
1178                         str += ('@html\n'
1179                                 + self.output_print_filename (HTML)
1180                                 + '\n@end html\n')
1181                         str += ('@tex\n'
1182                                 + self.output_print_filename (LATEX)
1183                                 + '\n@end tex\n')
1184                 base = self.basename ()
1185                 if TEXIDOC in self.option_dict:
1186                         texidoc = base + '.texidoc'
1187                         if os.path.exists (texidoc):
1188                                 str += '@include %(texidoc)s\n\n' % vars ()
1189
1190                 if VERBATIM in self.option_dict:
1191                         verb = verbatim_texinfo (self.substring ('code'))
1192                         str += (output[TEXINFO][VERBATIM] % vars ())
1193                         if not QUOTE in self.option_dict:
1194                                 str = output[TEXINFO][NOQUOTE] % vars ()
1195
1196                 str += self.output_info ()
1197
1198 #               str += ('@ifinfo\n' + self.output_info () + '\n@end ifinfo\n')
1199 #               str += ('@tex\n' + self.output_latex () + '\n@end tex\n')
1200 #               str += ('@html\n' + self.output_html () + '\n@end html\n')
1201
1202                 if QUOTE in self.option_dict:
1203                         str = output[TEXINFO][QUOTE] % vars ()
1204
1205                 # need par after image
1206                 str += '\n'
1207
1208                 return str
1209
1210 class Lilypond_file_snippet (Lilypond_snippet):
1211         def ly (self):
1212                 name = self.substring ('filename')
1213                 return '\\sourcefilename \"%s\"\n%s' \
1214                          % (name, open (find_file (name)).read ())
1215
1216 snippet_type_to_class = {
1217         'lilypond_file': Lilypond_file_snippet,
1218         'lilypond_block': Lilypond_snippet,
1219         'lilypond': Lilypond_snippet,
1220         'include': Include_snippet,
1221 }
1222
1223 def find_linestarts (s):
1224         nls = [0]
1225         start = 0
1226         end = len (s)
1227         while 1:
1228                 i = s.find ('\n', start)
1229                 if i < 0:
1230                         break
1231
1232                 i = i + 1
1233                 nls.append (i)
1234                 start = i
1235
1236         nls.append (len (s))
1237         return nls
1238
1239 def find_toplevel_snippets (s, types):
1240         res = {}
1241         for i in types:
1242                 res[i] = ly.re.compile (snippet_res[global_options.format][i])
1243
1244         snippets = []
1245         index = 0
1246         ## found = dict (map (lambda x: (x, None),
1247         ##                    types))
1248         ## urg python2.1
1249         found = {}
1250         map (lambda x, f = found: f.setdefault (x, None),
1251              types)
1252
1253         line_starts = find_linestarts (s)
1254         line_start_idx = 0
1255         # We want to search for multiple regexes, without searching
1256         # the string multiple times for one regex.
1257         # Hence, we use earlier results to limit the string portion
1258         # where we search.
1259         # Since every part of the string is traversed at most once for
1260         # every type of snippet, this is linear.
1261
1262         while 1:
1263                 first = None
1264                 endex = 1 << 30
1265                 for type in types:
1266                         if not found[type] or found[type][0] < index:
1267                                 found[type] = None
1268                                 
1269                                 m = res[type].search (s[index:endex])
1270                                 if not m:
1271                                         continue
1272
1273                                 cl = Snippet
1274                                 if snippet_type_to_class.has_key (type):
1275                                         cl = snippet_type_to_class[type]
1276
1277
1278                                 start = index + m.start ('match')
1279                                 line_number = line_start_idx
1280                                 while (line_starts[line_number] < start):
1281                                         line_number += 1
1282
1283                                 line_number += 1
1284                                 snip = cl (type, m, global_options.format, line_number)
1285
1286                                 found[type] = (start, snip)
1287
1288                         if found[type] \
1289                            and (not first \
1290                                 or found[type][0] < found[first][0]):
1291                                 first = type
1292
1293                                 # FIXME.
1294
1295                                 # Limiting the search space is a cute
1296                                 # idea, but this *requires* to search
1297                                 # for possible containing blocks
1298                                 # first, at least as long as we do not
1299                                 # search for the start of blocks, but
1300                                 # always/directly for the entire
1301                                 # @block ... @end block.
1302
1303                                 endex = found[first][0]
1304
1305                 if not first:
1306                         snippets.append (Substring (s, index, len (s), line_start_idx))
1307                         break
1308
1309                 while (start > line_starts[line_start_idx+1]):
1310                         line_start_idx += 1
1311
1312                 (start, snip) = found[first]
1313                 snippets.append (Substring (s, index, start, line_start_idx + 1))
1314                 snippets.append (snip)
1315                 found[first] = None
1316                 index = start + len (snip.match.group ('match'))
1317
1318         return snippets
1319
1320 def filter_pipe (input, cmd):
1321         if global_options.verbose:
1322                 progress (_ ("Opening filter `%s'") % cmd)
1323
1324         (stdin, stdout, stderr) = os.popen3 (cmd)
1325         stdin.write (input)
1326         status = stdin.close ()
1327
1328         if not status:
1329                 status = 0
1330                 output = stdout.read ()
1331                 status = stdout.close ()
1332                 error = stderr.read ()
1333
1334         if not status:
1335                 status = 0
1336         signal = 0x0f & status
1337         if status or (not output and error):
1338                 exit_status = status >> 8
1339                 error (_ ("`%s' failed (%d)") % (cmd, exit_status))
1340                 error (_ ("The error log is as follows:"))
1341                 sys.stderr.write (error)
1342                 sys.stderr.write (stderr.read ())
1343                 exit (status)
1344
1345         if global_options.verbose:
1346                 progress ('\n')
1347
1348         return output
1349
1350 def run_filter (s):
1351         return filter_pipe (s, global_options.filter_cmd)
1352
1353 def is_derived_class (cl, baseclass):
1354         if cl == baseclass:
1355                 return 1
1356         for b in cl.__bases__:
1357                 if is_derived_class (b, baseclass):
1358                         return 1
1359         return 0
1360
1361 def process_snippets (cmd, ly_snippets, texstr_snippets, png_snippets):
1362         ly_names = filter (lambda x: x,
1363                            map (Lilypond_snippet.basename, ly_snippets))
1364         texstr_names = filter (lambda x: x,
1365                            map (Lilypond_snippet.basename, texstr_snippets))
1366         png_names = filter (lambda x: x,
1367                             map (Lilypond_snippet.basename, png_snippets))
1368
1369         status = 0
1370         def my_system (cmd):
1371                 status = ly.system (cmd,
1372                                     ignore_error = 1, progress_p = 1)
1373
1374                 if status:
1375                         error ('Process %s exited unsuccessfully.' % cmd)
1376                         raise Compile_error
1377
1378         # UGH
1379         # the --process=CMD switch is a bad idea
1380         # it is too generic for lilypond-book.
1381         if texstr_names:
1382                 my_system (string.join ([cmd, '--backend texstr',
1383                                          'snippet-map.ly'] + texstr_names))
1384                 for l in texstr_names:
1385                         my_system ('latex %s.texstr' % l)
1386
1387         if ly_names:
1388                 my_system (string.join ([cmd, 'snippet-map.ly'] + ly_names))
1389
1390 LATEX_INSPECTION_DOCUMENT = r'''
1391 \nonstopmode
1392 %(preamble)s
1393 \begin{document}
1394 \typeout{textwidth=\the\textwidth}
1395 \typeout{columnsep=\the\columnsep}
1396 \makeatletter\if@twocolumn\typeout{columns=2}\fi\makeatother
1397 \end{document}
1398 '''
1399
1400 # Do we need anything else besides `textwidth'?
1401 def get_latex_textwidth (source):
1402         m = re.search (r'''(?P<preamble>\\begin\s*{document})''', source)
1403         preamble = source[:m.start (0)]
1404         latex_document = LATEX_INSPECTION_DOCUMENT % vars ()
1405         
1406         (handle, tmpfile) = tempfile.mkstemp('.tex')
1407         logfile = os.path.splitext (tmpfile)[0] + '.log'
1408         open (tmpfile,'w').write (latex_document)
1409         ly.system ('latex %s' % tmpfile)
1410         parameter_string = open (logfile).read()
1411         
1412         os.unlink (tmpfile)
1413         os.unlink (logfile)
1414
1415         columns = 0
1416         m = re.search ('columns=([0-9.]*)', parameter_string)
1417         if m:
1418                 columns = int (m.group (1))
1419
1420         columnsep = 0
1421         m = re.search ('columnsep=([0-9.]*)pt', parameter_string)
1422         if m:
1423                 columnsep = float (m.group (1))
1424
1425         textwidth = 0
1426         m = re.search ('textwidth=([0-9.]*)pt', parameter_string)
1427         if m:
1428                 textwidth = float (m.group (1))
1429                 if columns:
1430                         textwidth = (textwidth - columnsep) / columns
1431
1432         return textwidth
1433
1434 def modify_preamble (chunk):
1435         str = chunk.replacement_text ()
1436         if (re.search (r"\\begin *{document}", str)
1437             and not re.search ("{graphic[sx]", str)):
1438                 str = re.sub (r"\\begin{document}",
1439                               r"\\usepackage{graphics}" + '\n'
1440                               + r"\\begin{document}",
1441                               str)
1442                 chunk.override_text = str 
1443                 
1444         
1445
1446 ext2format = {
1447         '.html': HTML,
1448         '.itely': TEXINFO,
1449         '.latex': LATEX,
1450         '.lytex': LATEX,
1451         '.tely': TEXINFO,
1452         '.tex': LATEX,
1453         '.texi': TEXINFO,
1454         '.texinfo': TEXINFO,
1455         '.xml': HTML,
1456 }
1457
1458 format2ext = {
1459         HTML: '.html',
1460         # TEXINFO: '.texinfo',
1461         TEXINFO: '.texi',
1462         LATEX: '.tex',
1463 }
1464
1465 class Compile_error:
1466         pass
1467
1468 def write_file_map (lys, name):
1469         snippet_map = open ('snippet-map.ly', 'w')
1470         snippet_map.write ("""
1471 #(define version-seen? #t)
1472 #(ly:add-file-name-alist '(
1473 """)
1474         for ly in lys:
1475                 snippet_map.write ('("%s.ly" . "%s:%d (%s.ly)")\n'
1476                                    % (ly.basename (),
1477                                       name,
1478                                       ly.line_number,
1479                                       ly.basename ()))
1480
1481         snippet_map.write ('))\n')
1482
1483 def do_process_cmd (chunks, input_name):
1484         all_lys = filter (lambda x: is_derived_class (x.__class__,
1485                                                       Lilypond_snippet),
1486                           chunks)
1487
1488         write_file_map (all_lys, input_name)
1489         ly_outdated = \
1490           filter (lambda x: is_derived_class (x.__class__,
1491                                               Lilypond_snippet)
1492                             and x.ly_is_outdated (),
1493                   chunks)
1494         texstr_outdated = \
1495           filter (lambda x: is_derived_class (x.__class__,
1496                                               Lilypond_snippet)
1497                             and x.texstr_is_outdated (),
1498                   chunks)
1499         png_outdated = \
1500           filter (lambda x: is_derived_class (x.__class__,
1501                                               Lilypond_snippet)
1502                             and x.png_is_outdated (),
1503                   chunks)
1504
1505         progress (_ ("Writing snippets..."))
1506         map (Lilypond_snippet.write_ly, ly_outdated)
1507         progress ('\n')
1508
1509         if ly_outdated:
1510                 progress (_ ("Processing..."))
1511                 progress ('\n')
1512                 process_snippets (global_options.process_cmd, ly_outdated, texstr_outdated, png_outdated)
1513         else:
1514                 progress (_ ("All snippets are up to date..."))
1515         progress ('\n')
1516
1517 def guess_format (input_filename):
1518         format = None
1519         e = os.path.splitext (input_filename)[1]
1520         if e in ext2format.keys ():
1521                 # FIXME
1522                 format = ext2format[e]
1523         else:
1524                 error (_ ("can't determine format for: %s" \
1525                              % input_filename))
1526                 exit (1)
1527         return format
1528
1529 def write_if_updated (file_name, lines):
1530         try:
1531                 f = open (file_name)
1532                 oldstr = f.read ()
1533                 new_str = string.join (lines, '')
1534                 if oldstr == new_str:
1535                         progress (_ ("%s is up to date.") % file_name)
1536                         progress ('\n')
1537                         return
1538         except:
1539                 pass
1540
1541         progress (_ ("Writing `%s'...") % file_name)
1542         open (file_name, 'w').writelines (lines)
1543         progress ('\n')
1544
1545 def note_input_file (name, inputs=[]):
1546         ## hack: inputs is mutable!
1547         inputs.append (name)
1548         return inputs
1549
1550 def do_file (input_filename):
1551         # Ugh.
1552         if not input_filename or input_filename == '-':
1553                 in_handle = sys.stdin
1554                 input_fullname = '<stdin>'
1555         else:
1556                 if os.path.exists (input_filename):
1557                         input_fullname = input_filename
1558                 elif global_options.format == LATEX and ly.search_exe_path ('kpsewhich'): 
1559                         input_fullname = os.read_pipe ('kpsewhich ' + input_filename).read()[:-1]
1560                 else:
1561                         input_fullname = find_file (input_filename)
1562
1563                 note_input_file (input_fullname)
1564                 in_handle = open (input_fullname)
1565
1566         if input_filename == '-':
1567                 input_base = 'stdin'
1568         else:
1569                 input_base = os.path.basename \
1570                              (os.path.splitext (input_filename)[0])
1571
1572         # Only default to stdout when filtering.
1573         if global_options.output_name == '-' or (not global_options.output_name and global_options.filter_cmd):
1574                 output_filename = '-'
1575                 output_file = sys.stdout
1576         else:
1577                 # don't complain when global_options.output_name is existing
1578                 output_filename = input_base + format2ext[global_options.format]
1579                 if global_options.output_name:
1580                         if not os.path.isdir (global_options.output_name):
1581                                 os.mkdir (global_options.output_name, 0777)
1582                         os.chdir (global_options.output_name)
1583                 else: 
1584                         if os.path.exists (input_filename) \
1585                            and os.path.exists (output_filename) \
1586                            and os.path.samefile (output_filename, input_fullname):
1587                            error (
1588                            _ ("Output would overwrite input file; use --output."))
1589                            exit (2)
1590
1591         try:
1592                 progress (_ ("Reading %s...") % input_fullname)
1593                 source = in_handle.read ()
1594                 progress ('\n')
1595
1596                 set_default_options (source)
1597
1598
1599                 # FIXME: Containing blocks must be first, see
1600                 #        find_toplevel_snippets.
1601                 snippet_types = (
1602                         'multiline_comment',
1603                         'verbatim',
1604                         'lilypond_block',
1605         #               'verb',
1606                         'singleline_comment',
1607                         'lilypond_file',
1608                         'include',
1609                         'lilypond',
1610                 )
1611                 progress (_ ("Dissecting..."))
1612                 chunks = find_toplevel_snippets (source, snippet_types)
1613
1614                 if global_options.format == LATEX:
1615                         for c in chunks:
1616                                 if (c.is_plain () and
1617                                     re.search (r"\\begin *{document}", c.replacement_text())):
1618                                         modify_preamble (c)
1619                                         break
1620                 progress ('\n')
1621
1622                 if global_options.filter_cmd:
1623                         write_if_updated (output_filename,
1624                                           [c.filter_text () for c in chunks])
1625                 elif global_options.process_cmd:
1626                         do_process_cmd (chunks, input_fullname)
1627                         progress (_ ("Compiling %s...") % output_filename)
1628                         progress ('\n')
1629                         write_if_updated (output_filename,
1630                                           [s.replacement_text ()
1631                                            for s in chunks])
1632                 
1633                 def process_include (snippet):
1634                         os.chdir (original_dir)
1635                         name = snippet.substring ('filename')
1636                         progress (_ ("Processing include: %s") % name)
1637                         progress ('\n')
1638                         return do_file (name)
1639
1640                 include_chunks = map (process_include,
1641                                       filter (lambda x: is_derived_class (x.__class__,
1642                                                                           Include_snippet),
1643                                               chunks))
1644
1645
1646                 return chunks + reduce (lambda x,y: x + y, include_chunks, [])
1647                 
1648         except Compile_error:
1649                 os.chdir (original_dir)
1650                 progress (_ ("Removing `%s'") % output_filename)
1651                 progress ('\n')
1652                 raise Compile_error
1653
1654 def do_options ():
1655         opt_parser = get_option_parser()
1656         (options, args) = opt_parser.parse_args ()
1657
1658         if options.format in ('texi-html', 'texi'):
1659                 options.format = TEXINFO
1660         options.use_hash = True
1661
1662         options.include_path =  map (os.path.abspath, options.include_path)
1663
1664         global global_options
1665         global_options = options
1666         return args
1667
1668 def main ():
1669         files = do_options ()
1670         if not files or len (files) > 1:
1671                 ly.help ()
1672                 exit (2)
1673
1674         file = files[0]
1675
1676         basename = os.path.splitext (file)[0]
1677         basename = os.path.split (basename)[1]
1678         
1679         if not global_options.format:
1680                 global_options.format = guess_format (files[0])
1681
1682         formats = 'ps'
1683         if global_options.format in (TEXINFO, HTML):
1684                 formats += ',png'
1685         if global_options.process_cmd == '':
1686                 global_options.process_cmd = lilypond_binary \
1687                               + ' --formats=%s --backend eps ' % formats
1688
1689         if global_options.process_cmd:
1690                 global_options.process_cmd += string.join ([(' -I %s' % commands.mkarg (p))
1691                                                             for p in global_options.include_path])
1692
1693         ly.identify (sys.stderr)
1694
1695         try:
1696                 chunks = do_file (file)
1697                 if global_options.psfonts:
1698                         fontextract.verbose = global_options.verbose
1699                         snippet_chunks = filter (lambda x: is_derived_class (x.__class__,
1700                                                                               Lilypond_snippet),
1701                                                  chunks)
1702
1703                         psfonts_file = basename + '.psfonts' 
1704                         if not global_options.verbose:
1705                                 progress (_ ("Writing fonts to %s...") % psfonts_file)
1706                         fontextract.extract_fonts (psfonts_file,
1707                                                    [x.basename() + '.eps'
1708                                                     for x in snippet_chunks])
1709                         if not global_options.verbose:
1710                                 progress ('\n')
1711                         
1712         except Compile_error:
1713                 exit (1)
1714
1715         if global_options.format in (TEXINFO, LATEX):
1716                 if not global_options.psfonts:
1717                         warning (_ ("option --psfonts not used"))
1718                         warning (_ ("processing with dvips will have no fonts"))
1719
1720                 psfonts_file = os.path.join (global_options.output_name, basename + '.psfonts')
1721                 output = os.path.join (global_options.output_name, basename +  '.dvi' )
1722                 
1723                 progress ('\n')
1724                 progress (_ ("DVIPS usage:"))
1725                 progress ('\n')
1726                 progress ("    dvips -h %(psfonts_file)s %(output)s" % vars ())
1727                 progress ('\n')
1728
1729         inputs = note_input_file ('')
1730         inputs.pop ()
1731
1732         base_file_name = os.path.splitext (os.path.basename (file))[0]
1733         dep_file = os.path.join (global_options.output_name, base_file_name + '.dep')
1734         final_output_file = os.path.join (global_options.output_name,
1735                                           base_file_name
1736                                           + '.%s' % global_options.format)
1737         
1738         os.chdir (original_dir)
1739         open (dep_file, 'w').write ('%s: %s' % (final_output_file, ' '.join (inputs)))
1740
1741 if __name__ == '__main__':
1742         main ()