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