]> git.donarmstrong.com Git - lilypond.git/blob - scripts/lilypond-book.py
5aa91fc0aecdd3c38b5233b545cbd0430eeb0408
[lilypond.git] / scripts / lilypond-book.py
1 #!@PYTHON@
2 # vim: set noexpandtab:
3 # TODO:
4 # * junk --outdir for --output
5 # * Figure out clean set of options.
6 # *
7 # * EndLilyPondOutput is def'd as vfil. Causes large white gaps.
8 # * texinfo: add support for @pagesize
9
10 # todo: dimension handling (all the x2y) is clumsy. (tca: Thats
11 #       because the values are taken directly from texinfo.tex,
12 #       geometry.sty and article.cls. Give me a hint, and I'll
13 #       fix it.)
14
15 #
16 # TODO: magnification support should also work for texinfo -> html: eg. add as option to dvips.
17 #
18
19 # This is was the idea for handling of comments:
20 #       Multiline comments, @ignore .. @end ignore is scanned for
21 #       in read_doc_file, and the chunks are marked as 'ignore', so
22 #       lilypond-book will not touch them any more. The content of the
23 #       chunks are written to the output file. Also 'include' and 'input'
24 #       regex has to check if they are commented out.
25 #
26 #       Then it is scanned for 'lilypond', 'lilypond-file' and 'lilypond-block'.
27 #       These three regex's has to check if they are on a commented line,
28 #       % for latex, @c for texinfo.
29 #
30 #       Then lines that are commented out with % (latex) and @c (Texinfo)
31 #       are put into chunks marked 'ignore'. This cannot be done before
32 #       searching for the lilypond-blocks because % is also the comment character
33 #       for lilypond.
34 #
35 #       The the rest of the rexeces are searched for. They don't have to test
36 #       if they are on a commented out line.
37
38
39
40 import os
41 import stat
42 import string
43 import getopt
44 import sys
45 import __main__
46
47 # Handle bug in Python 1.6-2.1
48 #
49 # there are recursion limits for some patterns in Python 1.6 til 2.1.
50 # fix this by importing the 1.5.2 implementation pre instead. Fix by Mats.
51
52
53 ## We would like to do this for python 2.2 as well, unfortunately
54 ## python 2.2 has another bug, see Sf.net bugtracker
55 ##
56 ## https://sourceforge.net/tracker/?func=detail&aid=604803&group_id=5470&atid=105470
57 ##
58
59 if float (sys.version[0:3]) <= 2.1 or sys.version[0:5] == '2.2.1':
60         try:
61                 import pre
62                 re = pre
63                 del pre
64         except ImportError:
65                 import re
66 else:
67         import re
68
69 # Attempt to fix problems with limited stack size set by Python!
70 # Sets unlimited stack size. Note that the resource module only
71 # is available on UNIX.
72 try:
73        import resource
74        resource.setrlimit (resource.RLIMIT_STACK, (-1, -1))
75 except:
76        pass
77
78 errorport = sys.stderr
79 verbose_p = 0
80
81
82
83 try:
84         import gettext
85         gettext.bindtextdomain ('lilypond', localedir)
86         gettext.textdomain ('lilypond')
87         _ = gettext.gettext
88 except:
89         def _ (s):
90                 return s
91
92 def progress (s):
93         errorport.write (s + '\n')
94
95
96 program_version = '@TOPLEVEL_VERSION@'
97 if program_version == '@' + 'TOPLEVEL_VERSION' + '@':
98         program_version = '1.6.0'
99
100 # if set, LILYPONDPREFIX must take prevalence
101 # if datadir is not set, we're doing a build and LILYPONDPREFIX
102 datadir = '@local_lilypond_datadir@'
103
104 if os.environ.has_key ('LILYPONDPREFIX') :
105         datadir = os.environ['LILYPONDPREFIX']
106 else:
107         datadir = '@local_lilypond_datadir@'
108
109 while datadir[-1] == os.sep:
110         datadir= datadir[:-1]
111
112 kpse = os.popen ('kpsexpand \$TEXMF').read()
113 kpse = re.sub('[ \t\n]+$','', kpse)
114 type1_paths = os.popen ('kpsewhich -expand-path=\$T1FONTS').read ()
115
116 binary = 'lilypond'
117 #binary = 'valgrind --suppressions=/home/hanwen/usr/src/guile-1.6.supp  --num-callers=10 /home/hanwen/usr/src/lilypond/lily/out/lilypond'
118 environment = {
119         # TODO: * prevent multiple addition.
120         #       * clean TEXINPUTS, MFINPUTS, TFMFONTS,
121         #         as these take prevalence over $TEXMF
122         #         and thus may break tex run?
123         'TEXMF' : "{%s,%s}" % (datadir, kpse) ,
124         'GS_FONTPATH' : type1_paths,
125         'GS_LIB' : datadir + '/ps',
126 }
127
128 # tex needs lots of memory, more than it gets by default on Debian
129 non_path_environment = {
130         'extra_mem_top' : '1000000',
131         'extra_mem_bottom' : '1000000',
132         'pool_size' : '250000',
133 }
134
135 def setup_environment ():
136         # $TEXMF is special, previous value is already taken care of
137         if os.environ.has_key ('TEXMF'):
138                 del os.environ['TEXMF']
139
140         for key in environment.keys ():
141                 val = environment[key]
142                 if os.environ.has_key (key):
143                         val = val + os.pathsep + os.environ[key]
144                 os.environ[key] = val
145
146         for key in non_path_environment.keys ():
147                 val = non_path_environment[key]
148                 os.environ[key] = val
149
150 include_path = [os.getcwd()]
151
152
153 # g_ is for global (?)
154 g_extra_opts = ''
155 g_here_dir = os.getcwd ()
156 g_dep_prefix = ''
157 g_outdir = ''
158 g_force_music_fontsize = 0
159 g_read_lys = 0
160 g_do_pictures = 1
161 g_do_music = 1
162
163 format = ''
164 g_run_lilypond = 1
165 no_match = 'a\ba'
166
167 default_music_fontsize = 16
168 default_text_fontsize = 12
169 paperguru = None
170
171 class LatexPaper:
172         def __init__(self):
173                 self.m_document_preamble = []
174                 self.m_num_cols = 1
175                 self.m_multicols = 1
176
177         def find_latex_dims(self):
178                 if g_outdir:
179                         fname = os.path.join(g_outdir, "lily-tmp.tex")
180                 else:
181                         fname = "lily-tmp.tex"
182                 try:
183                         f = open(fname, "w")
184                 except IOError:
185                         error ("Error creating temporary file '%s'" % fname)
186
187                 for s in self.m_document_preamble:
188                         f.write(s)
189                 f.write(r"""
190 \begin{document}
191 \typeout{---}
192 \typeout{\columnsep \the\columnsep}
193 \typeout{\textwidth \the\textwidth}
194 \typeout{---}
195 \end{document}
196                 """)
197                 f.close()
198                 re_dim = re.compile(r"\\(\w+)\s+(\d+\.\d+)")
199
200                 cmd = "latex '\\nonstopmode \input %s'" % fname
201                 if verbose_p:
202                         sys.stderr.write ("Invoking `%s' as pipe" % cmd)
203                 try:
204                         status = quiet_system (cmd, "Latex for finding dimensions")
205                 except:
206                         sys.stderr.write (_("Invoking LaTeX failed.") + '\n' )
207                         sys.stderr.write (_("This is the error log:\n") + '\n')
208
209                         lns = open ('lily-tmp.log').readlines()
210
211                         countdown = -3
212                         for ln in lns:
213                                 sys.stderr.write (ln)
214                                 if re.match('^!', ln):
215                                         countdown = 3
216
217                                 if countdown == 0:
218                                         break
219
220                                 if countdown > 0:
221                                         countdown = countdown -1
222
223                         sys.stderr.write ("  ... (further messages elided)...\n")
224                         sys.exit (1)
225
226                 lns = open ('lily-tmp.log').readlines()
227                 for ln in lns:
228                         ln = string.strip(ln)
229                         m = re_dim.match(ln)
230                         if m:
231                                 if m.groups()[0] in ('textwidth', 'columnsep'):
232                                         self.__dict__['m_%s' % m.groups()[0]] = float(m.groups()[1])
233
234                 try:
235                         os.remove (fname)
236                         os.remove (os.path.splitext(fname)[0]+".aux")
237                         os.remove (os.path.splitext(fname)[0]+".log")
238                 except:
239                         pass
240
241                 if not self.__dict__.has_key ('m_textwidth'):
242                         raise 'foo!'
243
244         def get_linewidth(self):
245                 if self.m_num_cols == 1:
246                         w = self.m_textwidth
247                 else:
248                         w = (self.m_textwidth - self.m_columnsep)/2
249                 if self.m_multicols > 1:
250                         return (w - self.m_columnsep*(self.m_multicols-1)) \
251                            / self.m_multicols
252                 return w
253
254
255 class HtmlPaper:
256         def __init__(self):
257                 self.m_papersize = 'letterpaper'
258                 self.m_fontsize = 12
259         def get_linewidth(self):
260                 return html_linewidths[self.m_papersize][self.m_fontsize]
261
262 class TexiPaper:
263         def __init__(self):
264                 self.m_papersize = 'letterpaper'
265                 self.m_fontsize = 12
266         def get_linewidth(self):
267                 return texi_linewidths[self.m_papersize][self.m_fontsize]
268
269 def mm2pt(x):
270         return x * 2.8452756
271 def in2pt(x):
272         return x * 72.26999
273 def em2pt(x, fontsize = 10):
274         return {10: 10.00002, 11: 10.8448, 12: 11.74988}[fontsize] * x
275 def ex2pt(x, fontsize = 10):
276         return {10: 4.30554, 11: 4.7146, 12: 5.16667}[fontsize] * x
277
278 def pt2pt(x):
279         return x
280
281 dimension_conversion_dict ={
282         'mm': mm2pt,
283         'cm': lambda x: mm2pt(10*x),
284         'in': in2pt,
285         'em': em2pt,
286         'ex': ex2pt,
287         'pt': pt2pt
288         }
289
290 # Convert numeric values, with or without specific dimension, to floats.
291 # Keep other strings
292 def conv_dimen_to_float(value):
293         if type(value) == type(""):
294                 m = re.match ("([0-9.]+)(cm|in|pt|mm|em|ex)",value)
295                 if m:
296                         unit = m.group (2)
297                         num = string.atof(m.group (1))
298                         conv =  dimension_conversion_dict[m.group(2)]
299
300                         value = conv(num)
301
302                 elif re.match ("^[0-9.]+$",value):
303                         value = float(value)
304
305         return value
306
307 texi_linewidths = {
308         'afourpaper': {12: mm2pt(160)},
309         'afourwide': {12: in2pt(6.5)},
310         'afourlatex': {12: mm2pt(150)},
311         'smallbook': {12: in2pt(5)},
312         'letterpaper': {12: in2pt(6)}}
313
314 html_linewidths = {
315         'afourpaper': {12: mm2pt(160)},
316         'afourwide': {12: in2pt(6.5)},
317         'afourlatex': {12: mm2pt(150)},
318         'smallbook': {12: in2pt(5)},
319         'letterpaper': {12: in2pt(6)}}
320
321 option_definitions = [
322         ('EXT', 'f', 'format', 'use output format EXT (texi [default], latex, html)'),
323         ('DIM',  '', 'default-music-fontsize', 'default fontsize for music.  DIM is assumed to be in points'),
324         ('DIM',  '', 'default-lilypond-fontsize', 'deprecated, use --default-music-fontsize'),
325         ('OPT', '', 'extra-options' , 'pass OPT quoted to the lilypond command line'),
326         ('DIM', '', 'force-music-fontsize', 'force fontsize for all inline lilypond. DIM is assumed be to in points'),
327         ('DIM', '', 'force-lilypond-fontsize', 'deprecated, use --force-music-fontsize'),
328         ('', 'h', 'help', 'this help'),
329         ('DIR', 'I', 'include', 'include path'),
330         ('', 'M', 'dependencies', 'write dependencies'),
331         ('PREF', '',  'dep-prefix', 'prepend PREF before each -M dependency'),
332         ('', 'n', 'no-lily', 'don\'t run lilypond'),
333         ('', '', 'no-pictures', "don\'t generate pictures"),
334         ('', '', 'no-music', "strip all lilypond blocks from output"),
335         ('', '', 'read-lys', "don't write ly files."),
336         ('FILE', 'o', 'outname', 'filename main output file'),
337         ('FILE', '', 'outdir', "where to place generated files"),
338         ('', 'V', 'verbose', 'verbose' ),
339         ('', 'v', 'version', 'print version information' ),
340         ]
341
342 # format specific strings, ie. regex-es for input, and % strings for output
343 output_dict= {
344
345
346         'html' : {
347
348                 'output-lilypond': '''<lilypond%s>
349 %s
350 </lilypond>''',
351                 'output-filename' : r'''
352 <!-- %s >
353 <a href="%s">
354 <pre>%s</pre></a>:''',
355                 'output-lilypond-fragment': '''<lilypond%s>
356 \context Staff\context Voice{ %s }
357 </lilypond>''',
358                 'output-noinline': r'''
359 <!-- generated: %(fn)s.png !-->
360 ''',
361                 ## maybe <hr> ?
362                 'pagebreak': None,
363                 # Verbatim text is always finished with \n.  FIXME: For HTML,
364                 # this newline should be removed.
365                 'output-verbatim': r'''<pre>
366 %s</pre>''',
367                 # Verbatim text is always finished with \n.  FIXME: For HTML,
368                 # this newline should be removed.
369                 'output-small-verbatim': r'''<font size=-1><pre>
370 %s</pre></font>''',
371                 ## Ugh we need to differentiate on origin:
372                 ## lilypond-block origin wants an extra <p>, but
373                 ## inline music doesn't.
374                 ## possibly other center options?
375                 'output-html': r'''
376 <a href="%(fn)s.png">
377 <img align="center" valign="center" border="0" src="%(fn)s.png" alt="[picture of music]"></a>
378 ''',
379                 },
380
381
382         'latex': {
383
384                 'output-lilypond-fragment' : r'''\begin[eps,singleline,%s]{lilypond}
385   \context Staff <
386     \context Voice{
387       %s
388     }
389   >
390 \end{lilypond}''',
391                 'output-filename' : r'''\verb+%s+:\\
392 %% %s
393 %% %s
394 ''',
395                 'output-lilypond': r'''\begin[%s]{lilypond}
396 %s
397 \end{lilypond}
398 ''',
399                 # verbatim text is always finished with \n
400                 'output-verbatim': r'''\begin{verbatim}
401 %s\end{verbatim}
402 ''',
403                 # verbatim text is always finished with \n
404                 'output-small-verbatim': r'''{\small\begin{verbatim}
405 %s\end{verbatim}}
406 ''',
407                 'output-default-post': "\\def\postLilypondExample{}\n",
408                 'output-default-pre': "\\def\preLilypondExample{}\n",
409                 'usepackage-graphics': '\\usepackage{graphics}\n',
410                 'output-eps': '\\noindent\\parbox{\\lilypondepswidth{%(fn)s.eps}}{\includegraphics{%(fn)s}}',
411                 'output-noinline': r'''
412 %% generated: %(fn)s.eps
413 ''',
414                 'output-tex': '{\\preLilypondExample \\input %(fn)s.tex \\postLilypondExample\n}',
415                 'pagebreak': r'\pagebreak',
416                 },
417
418
419         'texi' : {
420
421                 'output-lilypond': '''@lilypond[%s]
422 %s
423 @end lilypond
424 ''',
425                 'output-filename' : r'''@ifnothtml
426 @file{%s}:@*
427 @end ifnothtml
428 @ifhtml
429 @uref{%s,@file{%s}}
430 @end ifhtml
431 ''',
432                 'output-lilypond-fragment': '''@lilypond[%s]
433 \context Staff\context Voice{ %s }
434 @end lilypond ''',
435                 'output-noinline': r'''
436 @c generated: %(fn)s.png
437 ''',
438                 'pagebreak': None,
439                 # verbatim text is always finished with \n
440                 'output-small-verbatim': r'''@smallexample
441 %s@end smallexample
442 ''',
443                 # verbatim text is always finished with \n
444                 'output-verbatim': r'''@example
445 %s@end example
446 ''',
447                 # do some tweaking: @ is needed in some ps stuff.
448                 # override EndLilyPondOutput, since @tex is done
449                 # in a sandbox, you can't do \input lilyponddefs at the
450                 # top of the document.
451                 #
452                 # ugh, the <p> below breaks inline images...
453                 'output-texi-noquote': r'''@tex
454 \catcode`\@=12
455 \parindent 0pt
456 \input lilyponddefs
457 \def\EndLilyPondOutput{}
458 \input %(fn)s.tex
459 \catcode`\@=0
460 @end tex
461 @html
462 <p><a href="%(fn)s.png">
463 <img border=0 src="%(fn)s.png" alt="[picture of music]">
464 </a><p>
465 @end html
466 ''',
467                 'output-texi-quoted': r'''@quotation
468 @tex
469 \catcode`\@=12
470 \input lilyponddefs
471 \def\EndLilyPondOutput{}
472 \input %(fn)s.tex
473 \catcode`\@=0
474 @end tex
475 @html
476 <a href="%(fn)s.png">
477 <img border=0 src="%(fn)s.png" alt="[picture of music]">
478 </a>
479 @end html
480 @end quotation
481 ''',
482                 }
483
484         }
485
486 def output_verbatim (body, small):
487         if __main__.format == 'html':
488                 body = re.sub ('&', '&amp;', body)
489                 body = re.sub ('>', '&gt;', body)
490                 body = re.sub ('<', '&lt;', body)
491         elif __main__.format == 'texi':
492                 # clumsy workaround for python 2.2 pre bug.
493                 body = re.sub ('@', '@@', body)
494                 body = re.sub ('{', '@{', body)
495                 body = re.sub ('}', '@}', body)
496
497         if small:
498                 key = 'output-small-verbatim'
499         else:
500                 key = 'output-verbatim'
501         return get_output (key) % body
502
503
504 # Warning: This uses extended regular expressions.  Treat with care.
505 #
506 # legenda
507 #
508 # (?P<name>regex) -- assign result of REGEX to NAME
509 # *? -- match non-greedily.
510 # (?m) -- multiline regex: make ^ and $ match at each line
511 # (?s) -- make the dot match all characters including newline
512 re_dict = {
513         'html': {
514                 'include':  no_match,
515                 'input': no_match,
516                 'header': no_match,
517                 'preamble-end': no_match,
518                 'landscape': no_match,
519                 'verbatim': r'''(?s)(?P<code><pre>\s.*?</pre>\s)''',
520                 'verb': r'''(?P<code><pre>.*?</pre>)''',
521                 'lilypond-file': r'(?m)(?P<match><lilypondfile(?P<options>[^>]+)?>\s*(?P<filename>[^<]+)\s*</lilypondfile>)',
522                 'lilypond' : '(?m)(?P<match><lilypond((?P<options>[^:]*):)(?P<code>.*?)/>)',
523                 'lilypond-block': r'''(?ms)(?P<match><lilypond(?P<options>[^>]+)?>(?P<code>.*?)</lilypond>)''',
524                 'option-sep' : '\s*',
525                 'intertext': r',?\s*intertext=\".*?\"',
526                 'multiline-comment': r"(?sm)\s*(?!@c\s+)(?P<code><!--\s.*?!-->)\s",
527                 'singleline-comment': no_match,
528                 'numcols': no_match,
529                 'multicols': no_match,
530                 },
531
532         'latex': {
533                 'input': r'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
534                 'include': r'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
535                 'option-sep' : ',\s*',
536                 'header': r"\n*\\documentclass\s*(\[.*?\])?",
537                 'preamble-end': r'(?P<code>\\begin\s*{document})',
538                 'verbatim': r"(?s)(?P<code>\\begin\s*{verbatim}.*?\\end{verbatim})",
539                 'verb': r"(?P<code>\\verb(?P<del>.).*?(?P=del))",
540                 'lilypond-file': r'(?m)^[^%\n]*?(?P<match>\\lilypondfile\s*(\[(?P<options>.*?)\])?\s*\{(?P<filename>.+)})',
541                 'lilypond' : r'(?m)^[^%\n]*?(?P<match>\\lilypond\s*(\[(?P<options>.*?)\])?\s*{(?P<code>.*?)})',
542                 'lilypond-block': r"(?sm)^[^%\n]*?(?P<match>\\begin\s*(\[(?P<options>.*?)\])?\s*{lilypond}(?P<code>.*?)\\end{lilypond})",
543                 'def-post-re': r"\\def\\postLilypondExample",
544                 'def-pre-re': r"\\def\\preLilypondExample",
545                 'usepackage-graphics': r"\usepackage\s*{graphics}",
546                 'intertext': r',?\s*intertext=\".*?\"',
547                 'multiline-comment': no_match,
548                 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
549                 'numcols': r"(?P<code>\\(?P<num>one|two)column)",
550                 'multicols': r"(?P<code>\\(?P<be>begin|end)\s*{multicols}({(?P<num>\d+)?})?)",
551                 },
552
553         # why do we have distinction between @mbinclude and @include?
554
555         'texi': {
556                 'include':  '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
557                 'input': no_match,
558                 'header': no_match,
559                 'preamble-end': no_match,
560                 'landscape': no_match,
561                 'verbatim': r'''(?s)(?P<code>@example\s.*?@end example\s)''',
562                 'verb': r'''(?P<code>@code{.*?})''',
563                 'lilypond-file': '(?m)^(?P<match>@lilypondfile(\[(?P<options>[^]]*)\])?{(?P<filename>[^}]+)})',
564                 'lilypond' : '(?m)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?{(?P<code>.*?)})',
565                 'lilypond-block': r'''(?ms)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?\s(?P<code>.*?)@end +lilypond)\s''',
566                 'option-sep' : ',\s*',
567                 'intertext': r',?\s*intertext=\".*?\"',
568                 'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
569                 'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
570                 'numcols': no_match,
571                 'multicols': no_match,
572                 }
573         }
574
575
576 for r in re_dict.keys ():
577         olddict = re_dict[r]
578         newdict = {}
579         for k in olddict.keys ():
580                 try:
581                         newdict[k] = re.compile (olddict[k])
582                 except:
583                         print 'invalid regexp: %s' % olddict[k]
584
585                         # we'd like to catch and reraise a more detailed  error, but
586                         # alas, the exceptions changed across the 1.5/2.1 boundary.
587                         raise "Invalid re"
588         re_dict[r] = newdict
589
590
591 def uniq (list):
592         list.sort ()
593         s = list
594         list = []
595         for x in s:
596                 if x not in list:
597                         list.append (x)
598         return list
599
600
601 def get_output (name):
602         return  output_dict[format][name]
603
604 def get_re (name):
605         return  re_dict[format][name]
606
607 def bounding_box_dimensions(fname):
608         if g_outdir:
609                 fname = os.path.join(g_outdir, fname)
610         try:
611                 fd = open(fname)
612         except IOError:
613                 error ("Error opening `%s'" % fname)
614         str = fd.read ()
615         s = re.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
616         if s:
617
618                 gs = map (lambda x: string.atoi (x), s.groups ())
619                 return (int (gs[2] - gs[0] + 0.5),
620                         int (gs[3] - gs[1] + 0.5))
621         else:
622                 return (0,0)
623
624 def error (str):
625         sys.stderr.write ("\n\n" + str + "\nExiting ... \n\n")
626         raise 'Exiting.'
627
628
629 def compose_full_body (body, opts):
630         '''Construct the lilypond code to send to Lilypond.
631         Add stuff to BODY using OPTS as options.'''
632         music_size = default_music_fontsize
633         if g_force_music_fontsize:
634                 music_size = g_force_music_fontsize
635         indent = ''
636         linewidth = ''
637         notime = ''
638         for o in opts:
639                 if not g_force_music_fontsize:
640                         m = re.match ('([0-9]+)pt', o)
641                         if m:
642                                 music_size = string.atoi(m.group (1))
643
644                 m = re.match ('indent=([-.0-9]+)(cm|in|mm|pt)', o)
645                 if m:
646                         f = float (m.group (1))
647                         indent = 'indent = %f\\%s' % (f, m.group (2))
648
649                 m = re.match ('linewidth=([-.0-9]+)(cm|in|mm|pt)', o)
650                 if m:
651                         f = float (m.group (1))
652                         linewidth = 'linewidth = %f\\%s' % (f, m.group (2))
653
654         if re.search ('\\\\score', body):
655                 is_fragment = 0
656         else:
657                 is_fragment = 1
658         if 'fragment' in opts:
659                 is_fragment = 1
660         if 'nofragment' in opts:
661                 is_fragment = 0
662
663         if is_fragment and not 'multiline' in opts:
664                 opts.append('singleline')
665
666         if 'singleline' in opts:
667                 if not linewidth:
668                         linewidth = 'linewidth = -1.0'
669                 if not indent:
670                         indent = 'indent = 0.0\mm'
671         elif not linewidth:
672                 l = __main__.paperguru.get_linewidth ()
673                 linewidth = 'linewidth = %f\pt' % l
674
675         if 'noindent' in opts:
676                 indent = 'indent = 0.0\mm'
677
678         if 'notime' in opts:
679                 notime = r'''
680 \translator {
681   \StaffContext
682   \remove Time_signature_engraver
683 }
684 '''
685
686         for o in opts:
687                 m= re.search ('relative(.*)', o)
688                 v = 0
689                 if m:
690                         try:
691                                 v = string.atoi (m.group (1))
692                         except ValueError:
693                                 pass
694
695                         v = v + 1
696                         pitch = 'c'
697                         if v < 0:
698                                 pitch = pitch + '\,' * v
699                         elif v > 0:
700                                 pitch = pitch + '\'' * v
701
702                         body = '\\relative %s { %s }' %(pitch, body)
703
704         if is_fragment:
705                 body = r'''
706 \score {
707   \notes {
708 %s
709   }
710 }
711 ''' % body
712
713         opts = uniq (opts)
714         optstring = string.join (opts, ' ')
715         optstring = re.sub ('\n', ' ', optstring)
716         body = r'''
717 %% Generated automatically by: lilypond-book.py
718 %% options are %s
719 \include "paper%d.ly"
720 \paper  {
721   %s
722   %s
723   %s
724 }
725 ''' % (optstring, music_size, linewidth, indent, notime) + body
726
727         # ughUGH not original options
728         return body
729
730 def scan_html_preamble (chunks):
731         return
732
733 def scan_latex_preamble(chunks):
734         # First we want to scan the \documentclass line
735         # it should be the first non-comment line.
736         # The only thing we really need to know about the \documentclass line
737         # is if there are one or two columns to begin with.
738         idx = 0
739         while 1:
740                 if chunks[idx][0] == 'ignore':
741                         idx = idx + 1
742                         continue
743                 m = get_re ('header').match(chunks[idx][1])
744                 if not m:
745                         error ("Latex documents must start with a \documentclass command")
746                 if m.group (1):
747                         options = re.split (',[\n \t]*', m.group(1)[1:-1])
748                 else:
749                         options = []
750                 if 'twocolumn' in options:
751                         paperguru.m_num_cols = 2
752                 break
753
754
755         # Then we add everything before \begin{document} to
756         # paperguru.m_document_preamble so that we can later write this header
757         # to a temporary file in find_latex_dims() to find textwidth.
758         while idx < len(chunks) and chunks[idx][0] != 'preamble-end':
759                 if chunks[idx] == 'ignore':
760                         idx = idx + 1
761                         continue
762                 paperguru.m_document_preamble.append(chunks[idx][1])
763                 idx = idx + 1
764
765         if len(chunks) == idx:
766                 error ("Didn't find end of preamble (\\begin{document})")
767
768         paperguru.find_latex_dims()
769
770 def scan_texi_preamble (chunks):
771         # this is not bulletproof..., it checks the first 10 chunks
772         for c in chunks[:10]:
773                 if c[0] == 'input':
774                         for s in ('afourpaper', 'afourwide', 'letterpaper',
775                                   'afourlatex', 'smallbook'):
776                                 if string.find(c[1], "@%s" % s) != -1:
777                                         paperguru.m_papersize = s
778
779
780 def scan_preamble (chunks):
781         if __main__.format == 'html':
782                 scan_html_preamble (chunks)
783         elif __main__.format == 'latex':
784                 scan_latex_preamble (chunks)
785         elif __main__.format == 'texi':
786                 scan_texi_preamble (chunks)
787
788
789 def completize_preamble (chunks):
790         if __main__.format != 'latex':
791                 return chunks
792         pre_b = post_b = graphics_b = None
793         for chunk in chunks:
794                 if chunk[0] == 'preamble-end':
795                         break
796                 if chunk[0] == 'input':
797                         m = get_re('def-pre-re').search(chunk[1])
798                         if m:
799                                 pre_b = 1
800                 if chunk[0] == 'input':
801                         m = get_re('def-post-re').search(chunk[1])
802                         if m:
803                                 post_b = 1
804
805                 if chunk[0] == 'input':
806                         m = get_re('usepackage-graphics').search(chunk[1])
807                         if m:
808                                 graphics_b = 1
809         x = 0
810         while x < len (chunks) and   chunks[x][0] != 'preamble-end':
811                 x = x + 1
812
813         if x == len(chunks):
814                 return chunks
815
816         if not pre_b:
817                 chunks.insert(x, ('input', get_output ('output-default-pre')))
818         if not post_b:
819                 chunks.insert(x, ('input', get_output ('output-default-post')))
820         if not graphics_b:
821                 chunks.insert(x, ('input', get_output ('usepackage-graphics')))
822
823         return chunks
824
825
826 read_files = []
827 def find_file (name):
828         '''
829         Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
830         '''
831
832         if name == '-':
833                 return (sys.stdin.read (), '<stdin>')
834         f = None
835         nm = ''
836         for a in include_path:
837                 try:
838                         nm = os.path.join (a, name)
839                         f = open (nm)
840                         __main__.read_files.append (nm)
841                         break
842                 except IOError:
843                         pass
844         if f:
845                 sys.stderr.write ("Reading `%s'\n" % nm)
846                 return (f.read (), nm)
847         else:
848                 error ("File not found `%s'\n" % name)
849                 return ('', '')
850
851 def do_ignore(match_object):
852         return [('ignore', match_object.group('code'))]
853 def do_preamble_end(match_object):
854         return [('preamble-end', match_object.group('code'))]
855
856 def make_verbatim(match_object):
857         return [('verbatim', match_object.group('code'))]
858
859 def make_verb(match_object):
860         return [('verb', match_object.group('code'))]
861
862 def do_include_file(m):
863         "m: MatchObject"
864         return [('input', get_output ('pagebreak'))] \
865              + read_doc_file(m.group('filename')) \
866              + [('input', get_output ('pagebreak'))]
867
868 def do_input_file(m):
869         return read_doc_file(m.group('filename'))
870
871 def make_lilypond(m):
872         if m.group('options'):
873                 options = m.group('options')
874         else:
875                 options = ''
876         return [('input', get_output('output-lilypond-fragment') %
877                         (options, m.group('code')))]
878
879 def make_lilypond_file(m):
880         '''
881
882         Find @lilypondfile{bla.ly} occurences and substitute bla.ly
883         into a @lilypond .. @end lilypond block.
884
885         '''
886
887         if m.group('options'):
888                 options = m.group('options')
889         else:
890                 options = ''
891         (content, nm) = find_file(m.group('filename'))
892         options = "filename=%s," % nm + options
893
894         return [('input', get_output('output-lilypond') %
895                         (options, content))]
896
897 def make_lilypond_block(m):
898         if not g_do_music:
899                 return []
900
901         if m.group('options'):
902                 options = get_re('option-sep').split (m.group('options'))
903         else:
904                 options = []
905         options = filter(lambda s: s != '', options)
906         return [('lilypond', m.group('code'), options)]
907
908 def do_columns(m):
909         if __main__.format != 'latex':
910                 return []
911         if m.group('num') == 'one':
912                 return [('numcols', m.group('code'), 1)]
913         if m.group('num') == 'two':
914                 return [('numcols', m.group('code'), 2)]
915
916 def do_multicols(m):
917         if __main__.format != 'latex':
918                 return []
919         if m.group('be') == 'begin':
920                 return [('multicols', m.group('code'), int(m.group('num')))]
921         else:
922                 return [('multicols', m.group('code'), 1)]
923         return []
924
925 def chop_chunks(chunks, re_name, func, use_match=0):
926         newchunks = []
927         for c in chunks:
928                 if c[0] == 'input':
929                         str = c[1]
930                         while str:
931                                 m = get_re (re_name).search (str)
932                                 if m == None:
933                                         newchunks.append (('input', str))
934                                         str = ''
935                                 else:
936                                         if use_match:
937                                                 newchunks.append (('input', str[:m.start ('match')]))
938                                         else:
939                                                 newchunks.append (('input', str[:m.start (0)]))
940                                         #newchunks.extend(func(m))
941                                         # python 1.5 compatible:
942                                         newchunks = newchunks + func(m)
943                                         str = str [m.end(0):]
944                 else:
945                         newchunks.append(c)
946         return newchunks
947
948 def determine_format (str):
949         if __main__.format == '':
950
951                 html = re.search ('(?i)<[dh]tml', str[:200])
952                 latex = re.search (r'''\\document''', str[:200])
953                 texi = re.search ('@node|@setfilename', str[:200])
954
955                 f = ''
956                 g = None
957
958                 if html and not latex and not texi:
959                         f = 'html'
960                 elif latex and not html and not texi:
961                         f = 'latex'
962                 elif texi and not html and not latex:
963                         f = 'texi'
964                 else:
965                         error ("can't determine format, please specify")
966                 __main__.format = f
967
968         if __main__.paperguru == None:
969                 if __main__.format == 'html':
970                         g = HtmlPaper ()
971                 elif __main__.format == 'latex':
972                         g = LatexPaper ()
973                 elif __main__.format == 'texi':
974                         g = TexiPaper ()
975
976                 __main__.paperguru = g
977
978
979 def read_doc_file (filename):
980         '''Read the input file, find verbatim chunks and do \input and \include
981         '''
982         (str, path) = find_file(filename)
983         determine_format (str)
984
985         chunks = [('input', str)]
986
987         # we have to check for verbatim before doing include,
988         # because we don't want to include files that are mentioned
989         # inside a verbatim environment
990         chunks = chop_chunks(chunks, 'verbatim', make_verbatim)
991
992         chunks = chop_chunks(chunks, 'verb', make_verb)
993         chunks = chop_chunks(chunks, 'multiline-comment', do_ignore)
994         #ugh fix input
995         chunks = chop_chunks(chunks, 'include', do_include_file, 1)
996         chunks = chop_chunks(chunks, 'input', do_input_file, 1)
997         return chunks
998
999
1000 taken_file_names = {}
1001 def schedule_lilypond_block (chunk):
1002         '''Take the body and options from CHUNK, figure out how the
1003         real .ly should look, and what should be left MAIN_STR (meant
1004         for the main file).  The .ly is written, and scheduled in
1005         TODO.
1006
1007         Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
1008
1009         TODO has format [basename, extension, extension, ... ]
1010         '''
1011         (type, body, opts) = chunk
1012         assert type == 'lilypond'
1013         file_body = compose_full_body (body, opts)
1014         ## Hmm, we should hash only lilypond source, and skip the
1015         ## %options are ...
1016         ## comment line
1017         basename = 'lily-' + `abs(hash (file_body))`
1018         for o in opts:
1019                 m = re.search ('filename="(.*?)"', o)
1020                 if m:
1021                         basename = m.group (1)
1022                         if not taken_file_names.has_key(basename):
1023                                 taken_file_names[basename] = 0
1024                         else:
1025                                 taken_file_names[basename] = taken_file_names[basename] + 1
1026                                 basename = basename + "-%i" % taken_file_names[basename]
1027         if not g_read_lys:
1028                 update_file(file_body, os.path.join(g_outdir, basename) + '.ly')
1029         needed_filetypes = ['tex']
1030
1031         if format == 'html' or format == 'texi':
1032                 needed_filetypes.append ('eps')
1033                 needed_filetypes.append ('png')
1034         if 'eps' in opts and not ('eps' in needed_filetypes):
1035                 needed_filetypes.append('eps')
1036         pathbase = os.path.join (g_outdir, basename)
1037         def f (base, ext1, ext2):
1038                 a = os.path.isfile(base + ext2)
1039                 if (os.path.isfile(base + ext1) and
1040                     os.path.isfile(base + ext2) and
1041                                 os.stat(base+ext1)[stat.ST_MTIME] >
1042                                 os.stat(base+ext2)[stat.ST_MTIME]) or \
1043                                 not os.path.isfile(base + ext2):
1044                         return 1
1045         todo = []
1046         if 'tex' in needed_filetypes and f(pathbase, '.ly', '.tex'):
1047                 todo.append('tex')
1048         if 'eps' in needed_filetypes and f(pathbase, '.tex', '.eps'):
1049                 todo.append('eps')
1050         if 'png' in needed_filetypes and f(pathbase, '.eps', '.png'):
1051                 todo.append('png')
1052         newbody = ''
1053
1054         if 'printfilename' in opts:
1055                 for o in opts:
1056                         m= re.match ("filename=(.*)", o)
1057                         if m:
1058                                 newbody = newbody + get_output ("output-filename") % (m.group(1), basename + '.ly', m.group(1))
1059                                 break
1060
1061
1062         if 'smallverbatim' in opts:
1063                 newbody = newbody + output_verbatim (body, 1)
1064         elif 'verbatim' in opts:
1065                 newbody = newbody + output_verbatim (body, 0)
1066
1067         for o in opts:
1068                 m = re.search ('intertext="(.*?)"', o)
1069                 if m:
1070                         newbody = newbody + "\n"
1071                         if format == 'texi':
1072                                 newbody = newbody + "@noindent\n"
1073                         elif format == 'latex':
1074                                 newbody = newbody + "\\noindent\n"
1075                         newbody = newbody + m.group (1) + "\n"
1076
1077         if 'noinline' in opts:
1078                 s = 'output-noinline'
1079         elif format == 'latex':
1080                 if 'eps' in opts:
1081                         s = 'output-eps'
1082                 else:
1083                         s = 'output-tex'
1084         elif format == 'texi':
1085                 if 'noquote' in opts:
1086                         s = 'output-texi-noquote'
1087                 else:
1088                         s = 'output-texi-quoted'
1089         else: # format == 'html'
1090                 s = 'output-html'
1091         newbody = newbody + get_output (s) % {'fn': basename }
1092         return ('lilypond', newbody, opts, todo, basename)
1093
1094 def process_lilypond_blocks(chunks):#ugh rename
1095         newchunks = []
1096         # Count sections/chapters.
1097         for c in chunks:
1098                 if c[0] == 'lilypond':
1099                         c = schedule_lilypond_block (c)
1100                 elif c[0] == 'numcols':
1101                         paperguru.m_num_cols = c[2]
1102                 elif c[0] == 'multicols':
1103                         paperguru.m_multicols = c[2]
1104                 newchunks.append (c)
1105         return newchunks
1106
1107
1108
1109 def system (cmd):
1110         sys.stderr.write ("invoking `%s'\n" % cmd)
1111         st = os.system (cmd)
1112         if st:
1113                 error ('Error command exited with value %d\n' % st)
1114         return st
1115
1116 def quiet_system (cmd, name):
1117         if not verbose_p:
1118                 progress ( _("Running %s...") % name)
1119                 cmd = cmd + ' 1> /dev/null 2> /dev/null'
1120
1121         return system (cmd)
1122
1123 def get_bbox (filename):
1124
1125         # gs bbox device is ugh, it always prints of stderr.
1126         system ('gs -sDEVICE=bbox -q  -sOutputFile=- -dNOPAUSE %s -c quit > %s.bbox 2>&1' % (filename, filename))
1127
1128         box = open (filename + '.bbox').read()
1129         m = re.match ('^%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', box)
1130         gr = []
1131         if m:
1132                 gr = map (string.atoi, m.groups ())
1133
1134         return gr
1135
1136 def make_pixmap (name):
1137         bbox = get_bbox (name + '.eps')
1138
1139
1140         margin = 0
1141         fo = open (name + '.trans.eps' , 'w')
1142         fo.write ('%d %d translate\n' % (-bbox[0]+margin, -bbox[1]+margin))
1143         fo.close ()
1144
1145         res = 90
1146
1147         x = (2* margin + bbox[2] - bbox[0]) * res / 72.
1148         y = (2* margin + bbox[3] - bbox[1]) * res / 72.
1149         if x == 0:
1150                 x = 1
1151         if y == 0:
1152                 y = 1
1153
1154         cmd = r'''gs -g%dx%d -sDEVICE=pnggray  -dTextAlphaBits=4 -dGraphicsAlphaBits=4  -q -sOutputFile=%s -r%d -dNOPAUSE %s %s -c quit '''
1155
1156         cmd = cmd % (x, y, name + '.png', res, name + '.trans.eps', name + '.eps')
1157         status = 0
1158         try:
1159                 status = system (cmd)
1160         except:
1161                 status = -1
1162
1163         if status:
1164                 os.unlink (name + '.png')
1165                 error ("Removing output file")
1166
1167 def compile_all_files (chunks):
1168         global foutn
1169         eps = []
1170         tex = []
1171         png = []
1172
1173         for c in chunks:
1174                 if c[0] != 'lilypond':
1175                         continue
1176                 base  = c[4]
1177                 exts = c[3]
1178                 for e in exts:
1179                         if e == 'eps':
1180                                 eps.append (base)
1181                         elif e == 'tex':
1182                                 #ugh
1183                                 if base + '.ly' not in tex:
1184                                         tex.append (base + '.ly')
1185                         elif e == 'png' and g_do_pictures:
1186                                 png.append (base)
1187         d = os.getcwd()
1188         if g_outdir:
1189                 os.chdir(g_outdir)
1190         if tex:
1191                 # fixme: be sys-independent.
1192                 def incl_opt (x):
1193                         if g_outdir and x[0] != '/' :
1194                                 x = os.path.join (g_here_dir, x)
1195                         return ' -I %s' % x
1196
1197                 incs = map (incl_opt, include_path)
1198                 lilyopts = string.join (incs, ' ' )
1199                 if do_deps:
1200                         lilyopts = lilyopts + ' --dependencies '
1201                         if g_outdir:
1202                                 lilyopts = lilyopts + '--dep-prefix=' + g_outdir + '/'
1203                 texfiles = string.join (tex, ' ')
1204                 cmd = '%s --header=texidoc %s %s %s' \
1205                       % (binary, lilyopts, g_extra_opts, texfiles)
1206
1207                 system (cmd)
1208
1209                 #
1210                 # Ugh, fixing up dependencies for .tex generation
1211                 #
1212                 if do_deps:
1213                         depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep', x), tex)
1214                         for i in depfiles:
1215                                 f =open (i)
1216                                 text=f.read ()
1217                                 f.close ()
1218                                 text=re.sub ('\n([^:\n]*):', '\n' + foutn + ':', text)
1219                                 f = open (i, 'w')
1220                                 f.write (text)
1221                                 f.close ()
1222
1223         for e in eps:
1224                 cmd = r"echo $TEXMF; latex '\nonstopmode \input %s'" % e
1225                 quiet_system (cmd, 'LaTeX')
1226
1227                 cmd = r"dvips -E -o %s %s" % (e + '.eps', e)
1228                 quiet_system (cmd, 'dvips')
1229
1230         for g in png:
1231                 make_pixmap (g)
1232
1233         os.chdir (d)
1234
1235
1236 def update_file (body, name):
1237         '''
1238         write the body if it has changed
1239         '''
1240         same = 0
1241         try:
1242                 f = open (name)
1243                 fs = f.read (-1)
1244                 same = (fs == body)
1245         except:
1246                 pass
1247
1248         if not same:
1249                 f = open (name , 'w')
1250                 f.write (body)
1251                 f.close ()
1252
1253         return not same
1254
1255
1256 def getopt_args (opts):
1257         "Construct arguments (LONG, SHORT) for getopt from  list of options."
1258         short = ''
1259         long = []
1260         for o in opts:
1261                 if o[1]:
1262                         short = short + o[1]
1263                         if o[0]:
1264                                 short = short + ':'
1265                 if o[2]:
1266                         l = o[2]
1267                         if o[0]:
1268                                 l = l + '='
1269                         long.append (l)
1270         return (short, long)
1271
1272 def option_help_str (o):
1273         "Transform one option description (4-tuple ) into neatly formatted string"
1274         sh = '  '
1275         if o[1]:
1276                 sh = '-%s' % o[1]
1277
1278         sep = ' '
1279         if o[1] and o[2]:
1280                 sep = ','
1281
1282         long = ''
1283         if o[2]:
1284                 long= '--%s' % o[2]
1285
1286         arg = ''
1287         if o[0]:
1288                 if o[2]:
1289                         arg = '='
1290                 arg = arg + o[0]
1291         return '  ' + sh + sep + long + arg
1292
1293 def options_help_str (opts):
1294         "Convert a list of options into a neatly formatted string"
1295         w = 0
1296         strs =[]
1297         helps = []
1298
1299         for o in opts:
1300                 s = option_help_str (o)
1301                 strs.append ((s, o[3]))
1302                 if len (s) > w:
1303                         w = len (s)
1304
1305         str = ''
1306         for s in strs:
1307                 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0])  + 3), s[1])
1308         return str
1309
1310 def help():
1311         sys.stdout.write('''Usage: lilypond-book [options] FILE\n
1312 Generate hybrid LaTeX input from latex + lilypond.\n
1313 Options:
1314 ''')
1315         sys.stdout.write (options_help_str (option_definitions))
1316         sys.stdout.write (r'''
1317 Warning: All output is written in the CURRENT directory.
1318
1319
1320 Report bugs to bug-lilypond@gnu.org.
1321
1322 Written by Tom Cato Amundsen <tca@gnu.org> and
1323 Han-Wen Nienhuys <hanwen@cs.uu.nl>
1324 ''')
1325
1326         sys.exit (0)
1327
1328
1329 def write_deps (fn, target, chunks):
1330         global read_files
1331         sys.stderr.write('Writing `%s\'\n' % os.path.join(g_outdir, fn))
1332         f = open (os.path.join(g_outdir, fn), 'w')
1333         f.write ('%s%s: ' % (g_dep_prefix, target))
1334         for d in read_files:
1335                 f.write ('%s ' %  d)
1336         basenames=[]
1337         for c in chunks:
1338                 if c[0] == 'lilypond':
1339                         (type, body, opts, todo, basename) = c;
1340                         basenames.append (basename)
1341         for d in basenames:
1342                 if g_outdir:
1343                         d=g_outdir + '/' + d
1344                 if g_dep_prefix:
1345                         #if not os.isfile (d): # thinko?
1346                         if not re.search ('/', d):
1347                                 d = g_dep_prefix + d
1348                 f.write ('%s.tex ' %  d)
1349         f.write ('\n')
1350         #if len (basenames):
1351         #       for d in basenames:
1352         #               f.write ('%s.ly ' %  d)
1353         #       f.write (' : %s' % target)
1354         f.write ('\n')
1355         f.close ()
1356         read_files = []
1357
1358 def identify (stream):
1359         stream.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
1360
1361 def print_version ():
1362         identify (sys.stdout)
1363         sys.stdout.write (r'''Copyright 1998--1999
1364 Distributed under terms of the GNU General Public License. It comes with
1365 NO WARRANTY.
1366 ''')
1367
1368
1369 def check_texidoc (chunks):
1370         n = []
1371         for c in chunks:
1372                 if c[0] == 'lilypond':
1373                         (type, body, opts, todo, basename) = c;
1374                         pathbase = os.path.join (g_outdir, basename)
1375                         if os.path.isfile (pathbase + '.texidoc'):
1376                                 body = '\n@include %s.texidoc\n' % basename + body
1377                                 c = (type, body, opts, todo, basename)
1378                 n.append (c)
1379         return n
1380
1381
1382 ## what's this? Docme --hwn
1383 ##
1384 def fix_epswidth (chunks):
1385         newchunks = []
1386         for c in chunks:
1387                 if c[0] != 'lilypond' or 'eps' not in c[2]:
1388                         newchunks.append (c)
1389                         continue
1390
1391                 mag = 1.0
1392                 for o in c[2]:
1393                         m  = re.match ('magnification=([0-9.]+)', o)
1394                         if m:
1395                                 mag = string.atof (m.group (1))
1396
1397                 def replace_eps_dim (match, lmag = mag):
1398                         filename = match.group (1)
1399                         dims = bounding_box_dimensions (filename)
1400
1401                         return '%fpt' % (dims[0] *lmag)
1402
1403                 body = re.sub (r'''\\lilypondepswidth{(.*?)}''', replace_eps_dim, c[1])
1404                 newchunks.append(('lilypond', body, c[2], c[3], c[4]))
1405
1406         return newchunks
1407
1408
1409 ##docme: why global?
1410 foutn=""
1411
1412 def do_file(input_filename):
1413         chunks = read_doc_file(input_filename)
1414         chunks = chop_chunks(chunks, 'lilypond', make_lilypond, 1)
1415         chunks = chop_chunks(chunks, 'lilypond-file', make_lilypond_file, 1)
1416         chunks = chop_chunks(chunks, 'lilypond-block', make_lilypond_block, 1)
1417         chunks = chop_chunks(chunks, 'singleline-comment', do_ignore, 1)
1418         chunks = chop_chunks(chunks, 'preamble-end', do_preamble_end)
1419         chunks = chop_chunks(chunks, 'numcols', do_columns)
1420         chunks = chop_chunks(chunks, 'multicols', do_multicols)
1421         #print "-" * 50
1422         #for c in chunks: print "c:", c;
1423         #sys.exit()
1424         scan_preamble(chunks)
1425         chunks = process_lilypond_blocks(chunks)
1426
1427         # Do It.
1428         if __main__.g_run_lilypond:
1429                 compile_all_files (chunks)
1430                 chunks = fix_epswidth (chunks)
1431
1432         if __main__.format == 'texi':
1433                 chunks = check_texidoc (chunks)
1434
1435         x = 0
1436         chunks = completize_preamble (chunks)
1437
1438         global foutn
1439
1440         if outname:
1441                 my_outname = outname
1442         elif input_filename == '-' or input_filename == "/dev/stdin":
1443                 my_outname = '-'
1444         else:
1445                 my_outname = os.path.basename (os.path.splitext(input_filename)[0]) + '.' + format
1446         my_depname = my_outname + '.dep'
1447
1448         if my_outname == '-' or my_outname == '/dev/stdout':
1449                 fout = sys.stdout
1450                 foutn = "<stdout>"
1451                 __main__.do_deps = 0
1452         else:
1453                 foutn = os.path.join (g_outdir, my_outname)
1454                 sys.stderr.write ("Writing `%s'\n" % foutn)
1455                 fout = open (foutn, 'w')
1456         for c in chunks:
1457                 fout.write (c[1])
1458         fout.close ()
1459         # should chmod -w
1460
1461         if do_deps:
1462                 write_deps (my_depname, foutn, chunks)
1463
1464 outname = ''
1465 try:
1466         (sh, long) = getopt_args (__main__.option_definitions)
1467         (options, files) = getopt.getopt(sys.argv[1:], sh, long)
1468 except getopt.error, msg:
1469         sys.stderr.write("error: %s" % msg)
1470         sys.exit(1)
1471
1472 do_deps = 0
1473 for opt in options:
1474         o = opt[0]
1475         a = opt[1]
1476
1477         if o == '--include' or o == '-I':
1478                 include_path.append (a)
1479         elif o == '--version' or o == '-v':
1480                 print_version ()
1481                 sys.exit  (0)
1482         elif o == '--verbose' or o == '-V':
1483                 __main__.verbose_p = 1
1484         elif o == '--format' or o == '-f':
1485                 __main__.format = a
1486         elif o == '--outname' or o == '-o':
1487                 if len(files) > 1:
1488                         #HACK
1489                         sys.stderr.write("Lilypond-book is confused by --outname on multiple files")
1490                         sys.exit(1)
1491                 outname = a
1492         elif o == '--help' or o == '-h':
1493                 help ()
1494         elif o == '--no-lily' or o == '-n':
1495                 __main__.g_run_lilypond = 0
1496         elif o == '--dependencies' or o == '-M':
1497                 do_deps = 1
1498         elif o == '--default-music-fontsize':
1499                 default_music_fontsize = string.atoi (a)
1500         elif o == '--default-lilypond-fontsize':
1501                 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1502                 default_music_fontsize = string.atoi (a)
1503         elif o == '--extra-options':
1504                 g_extra_opts = a
1505         elif o == '--force-music-fontsize':
1506                 g_force_music_fontsize = string.atoi(a)
1507         elif o == '--force-lilypond-fontsize':
1508                 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1509                 g_force_music_fontsize = string.atoi(a)
1510         elif o == '--dep-prefix':
1511                 g_dep_prefix = a
1512         elif o == '--no-pictures':
1513                 g_do_pictures = 0
1514         elif o == '--no-music':
1515                 g_do_music = 0
1516         elif o == '--read-lys':
1517                 g_read_lys = 1
1518         elif o == '--outdir':
1519                 g_outdir = a
1520
1521 identify (sys.stderr)
1522 if g_outdir:
1523         if os.path.isfile(g_outdir):
1524                 error ("outdir is a file: %s" % g_outdir)
1525         if not os.path.exists(g_outdir):
1526                 os.mkdir(g_outdir)
1527 setup_environment ()
1528 for input_filename in files:
1529         do_file(input_filename)
1530
1531 #
1532 # Petr, ik zou willen dat ik iets zinvoller deed,
1533 # maar wat ik kan ik doen, het verandert toch niets?
1534 #   --hwn 20/aug/99