]> git.donarmstrong.com Git - lilypond.git/blob - scripts/lilypond-book.py
* Documentation/user/lilypond-book.itely:
[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': "\\begin{verbatim}%s\\end{verbatim}",
430                 'output-default-post': "\\def\postLilypondExample{}\n",
431                 'output-default-pre': "\\def\preLilypondExample{}\n",
432                 'usepackage-graphics': '\\usepackage{graphics}\n',
433                 'output-eps': '\\noindent\\parbox{\\lilypondepswidth{%(fn)s.eps}}{\includegraphics{%(fn)s.eps}}',
434                 'output-noinline': r'''
435 %% generated: %(fn)s.eps
436 ''',
437                 'output-tex': '{\\preLilypondExample \\input %(fn)s.tex \\postLilypondExample\n}',
438                 'pagebreak': r'\pagebreak',
439                 },
440         
441         'texi' : {'output-lilypond': """@lilypond[%s]
442 %s
443 @end lilypond 
444 """,
445                 'output-filename' : r'''
446
447 @file{%s}:''',    
448                   'output-lilypond-fragment': """@lilypond[%s]
449 \context Staff\context Voice{ %s }
450 @end lilypond """,
451                   'output-noinline': r'''
452 @c generated: %(fn)s.png                  
453 ''',
454                   'pagebreak': None,
455                   'output-verbatim': r"""@example
456 %s
457 @end example
458 """,
459
460 # do some tweaking: @ is needed in some ps stuff.
461 # override EndLilyPondOutput, since @tex is done
462 # in a sandbox, you can't do \input lilyponddefs at the
463 # top of the document.
464
465 # should also support fragment in
466                   
467                   'output-all': r"""
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 <p>
477 <a href="%(fn)s.png">
478 <img border=0 src="%(fn)s.png" alt="[picture of music]">
479 </a>
480 @end html
481 """,
482                 }
483         }
484
485 def output_verbatim (body):
486         if __main__.format == 'texi':
487                 body = re.sub ('([@{}])', '@\\1', body)
488         return get_output ('output-verbatim') % body
489
490
491 #warning: this uses extended regular expressions. Tread with care.
492
493 # legenda (?P  name parameter
494 # *? match non-greedily.
495
496 re_dict = {
497         'latex': {'input': r'(?m)^[^%\n]*?(?P<match>\\mbinput{?([^}\t \n}]*))',
498                   'include': r'(?m)^[^%\n]*?(?P<match>\\mbinclude{(?P<filename>[^}]+)})',
499                   'option-sep' : ',\s*',
500                   'header': r"\\documentclass\s*(\[.*?\])?",
501                   'geometry': r"^(?m)[^%\n]*?\\usepackage\s*(\[(?P<options>.*)\])?\s*{geometry}",
502                   'preamble-end': r'(?P<code>\\begin{document})',
503                   'verbatim': r"(?s)(?P<code>\\begin{verbatim}.*?\\end{verbatim})",
504                   'verb': r"(?P<code>\\verb(?P<del>.).*?(?P=del))",
505                   'lilypond-file': r'(?m)^[^%\n]*?(?P<match>\\lilypondfile\s*(\[(?P<options>.*?)\])?\s*\{(?P<filename>.+)})',
506                   'lilypond' : r'(?m)^[^%\n]*?(?P<match>\\lilypond\s*(\[(?P<options>.*?)\])?\s*{(?P<code>.*?)})',
507                   'lilypond-block': r"(?sm)^[^%\n]*?(?P<match>\\begin\s*(\[(?P<options>.*?)\])?\s*{lilypond}(?P<code>.*?)\\end{lilypond})",
508                   'def-post-re': r"\\def\\postLilypondExample",
509                   'def-pre-re': r"\\def\\preLilypondExample",             
510                   'usepackage-graphics': r"\usepackage{graphics}",
511                   'intertext': r',?\s*intertext=\".*?\"',
512                   'multiline-comment': no_match,
513                   'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>^%.*$\n+))",
514                   'numcols': r"(?P<code>\\(?P<num>one|two)column)",
515                   },
516
517
518         # why do we have distinction between @mbinclude and @include?
519
520         
521         'texi': {
522                  'include':  '(?m)^[^%\n]*?(?P<match>@mbinclude[ \n\t]+(?P<filename>[^\t \n]*))',
523                  'input': no_match,
524                  'header': no_match,
525                  'preamble-end': no_match,
526                  'landscape': no_match,
527                  'verbatim': r"""(?s)(?P<code>@example\s.*?@end example\s)""",
528                  'verb': r"""(?P<code>@code{.*?})""",
529                  'lilypond-file': '(?m)^(?P<match>@lilypondfile(\[(?P<options>[^]]*)\])?{(?P<filename>[^}]+)})',
530                  'lilypond' : '(?m)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?{(?P<code>.*?)})',
531                  'lilypond-block': r"""(?ms)^(?P<match>@lilypond(\[(?P<options>[^]]*)\])?\s(?P<code>.*?)@end lilypond)\s""",
532                   'option-sep' : ',\s*',
533                   'intertext': r',?\s*intertext=\".*?\"',
534                   'multiline-comment': r"(?sm)^\s*(?!@c\s+)(?P<code>@ignore\s.*?@end ignore)\s",
535                   'singleline-comment': r"(?m)^.*?(?P<match>(?P<code>@c.*$\n+))",
536                   'numcols': no_match,
537                  }
538         }
539
540
541 for r in re_dict.keys ():
542         olddict = re_dict[r]
543         newdict = {}
544         for k in olddict.keys ():
545                 try:
546                         newdict[k] = re.compile (olddict[k])
547                 except:
548                         print 'invalid regexp: %s' % olddict[k]
549
550                         # we'd like to catch and reraise a more detailed  error, but
551                         # alas, the exceptions changed across the 1.5/2.1 boundary.
552                         raise "Invalid re"
553         re_dict[r] = newdict
554
555         
556 def uniq (list):
557         list.sort ()
558         s = list
559         list = []
560         for x in s:
561                 if x not in list:
562                         list.append (x)
563         return list
564                 
565
566 def get_output (name):
567         return  output_dict[format][name]
568
569 def get_re (name):
570         return  re_dict[format][name]
571
572 def bounding_box_dimensions(fname):
573         if g_outdir:
574                 fname = os.path.join(g_outdir, fname)
575         try:
576                 fd = open(fname)
577         except IOError:
578                 error ("Error opening `%s'" % fname)
579         str = fd.read ()
580         s = re.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
581         if s:
582                 
583                 gs = map (lambda x: string.atoi (x), s.groups ())
584                 return (int (gs[2] - gs[0] + 0.5),
585                         int (gs[3] - gs[1] + 0.5))
586         else:
587                 return (0,0)
588
589 def error (str):
590         sys.stderr.write (str + "\n  Exiting ... \n\n")
591         raise 'Exiting.'
592
593
594 def compose_full_body (body, opts):
595         """Construct the lilypond code to send to Lilypond.
596         Add stuff to BODY using OPTS as options."""
597         music_size = default_music_fontsize
598         latex_size = default_text_fontsize
599         indent = ''
600         linewidth = ''
601         for o in opts:
602                 if g_force_lilypond_fontsize:
603                         music_size = g_force_lilypond_fontsize
604                 else:
605                         m = re.match ('([0-9]+)pt', o)
606                         if m:
607                                 music_size = string.atoi(m.group (1))
608
609                 m = re.match ('latexfontsize=([0-9]+)pt', o)
610                 if m:
611                         latex_size = string.atoi (m.group (1))
612                         
613                 m = re.match ('indent=([-.0-9]+)(cm|in|mm|pt)', o)
614                 if m:
615                         f = float (m.group (1))
616                         indent = 'indent = %f\\%s' % (f, m.group (2))
617                         
618                 m = re.match ('linewidth=([-.0-9]+)(cm|in|mm|pt)', o)
619                 if m:
620                         f = float (m.group (1))
621                         linewidth = 'linewidth = %f\\%s' % (f, m.group (2))
622
623         if re.search ('\\\\score', body):
624                 is_fragment = 0
625         else:
626                 is_fragment = 1
627         if 'fragment' in opts:
628                 is_fragment = 1
629         if 'nofragment' in opts:
630                 is_fragment = 0
631
632         if is_fragment and not 'multiline' in opts:
633                 opts.append('singleline')
634                 
635         if 'singleline' in opts:
636                 linewidth = 'linewidth = -1.0'
637         elif not linewidth:
638                 l = __main__.paperguru.get_linewidth ()
639                 linewidth = 'linewidth = %f\pt' % l
640
641         if 'noindent' in opts:
642                 indent = 'indent = 0.0\mm'
643
644         for o in opts:
645                 m= re.search ('relative(.*)', o)
646                 v = 0
647                 if m:
648                         try:
649                                 v = string.atoi (m.group (1))
650                         except ValueError:
651                                 pass
652
653                         v = v + 1
654                         pitch = 'c'
655                         if v < 0:
656                                 pitch = pitch + '\,' * v
657                         elif v > 0:
658                                 pitch = pitch + '\'' * v
659
660                         body = '\\relative %s { %s }' %(pitch, body)
661         
662         if is_fragment:
663                 body = r'''\score { 
664  \notes { %s }
665   \paper { }  
666 }''' % body
667
668         opts = uniq (opts)
669         optstring = string.join (opts, ' ')
670         optstring = re.sub ('\n', ' ', optstring)
671         body = r'''
672 %% Generated automatically by: lilypond-book.py
673 %% options are %s  
674 \include "paper%d.ly"
675 \paper  {
676   %s
677   %s
678
679 ''' % (optstring, music_size, linewidth, indent) + body
680
681         # ughUGH not original options
682         return body
683
684 def parse_options_string(s):
685         d = {}
686         r1 = re.compile("((\w+)={(.*?)})((,\s*)|$)")
687         r2 = re.compile("((\w+)=(.*?))((,\s*)|$)")
688         r3 = re.compile("(\w+?)((,\s*)|$)")
689         while s:
690                 m = r1.match(s)
691                 if m:
692                         s = s[m.end():]
693                         d[m.group(2)] = re.split(",\s*", m.group(3))
694                         continue
695                 m = r2.match(s)
696                 if m:
697                         s = s[m.end():]
698                         d[m.group(2)] = m.group(3)
699                         continue
700                 m = r3.match(s)
701                 if m:
702                         s = s[m.end():]
703                         d[m.group(1)] = 1
704                         continue
705                 
706                 error ("format of option string invalid (was `%')" % s)
707         return d
708
709 def scan_latex_preamble(chunks):
710         # first we want to scan the \documentclass line
711         # it should be the first non-comment line
712         idx = 0
713         while 1:
714                 if chunks[idx][0] == 'ignore':
715                         idx = idx + 1
716                         continue
717                 m = get_re ('header').match(chunks[idx][1])
718                 if m <> None and m.group (1):
719                         options = re.split (',[\n \t]*', m.group(1)[1:-1])
720                 else:
721                         options = []
722                 for o in options:
723                         if o == 'landscape':
724                                 paperguru.m_landscape = 1
725                         m = re.match("(.*?)paper", o)
726                         if m:
727                                 paperguru.m_papersize = m.group()
728                         else:
729                                 m = re.match("(\d\d)pt", o)
730                                 if m:
731                                         paperguru.m_fontsize = int(m.group(1))
732                 break
733         
734         while chunks[idx][0] != 'preamble-end':
735                 if chunks[idx] == 'ignore':
736                         idx = idx + 1
737                         continue
738                 m = get_re ('geometry').search(chunks[idx][1])
739                 if m:
740                         paperguru.m_use_geometry = 1
741                         o = parse_options_string(m.group('options'))
742                         for k in o.keys():
743                                 paperguru.set_geo_option(k, o[k])
744                 idx = idx + 1
745
746 def scan_texi_preamble (chunks):
747         # this is not bulletproof..., it checks the first 10 chunks
748         for c in chunks[:10]: 
749                 if c[0] == 'input':
750                         for s in ('afourpaper', 'afourwide', 'letterpaper',
751                                   'afourlatex', 'smallbook'):
752                                 if string.find(c[1], "@%s" % s) != -1:
753                                         paperguru.m_papersize = s
754
755 def scan_preamble (chunks):
756         if __main__.format == 'texi':
757                 scan_texi_preamble(chunks)
758         else:
759                 assert __main__.format == 'latex'
760                 scan_latex_preamble(chunks)
761                 
762
763 def completize_preamble (chunks):
764         if __main__.format == 'texi':
765                 return chunks
766         pre_b = post_b = graphics_b = None
767         for chunk in chunks:
768                 if chunk[0] == 'preamble-end':
769                         break
770                 if chunk[0] == 'input':
771                         m = get_re('def-pre-re').search(chunk[1])
772                         if m:
773                                 pre_b = 1
774                 if chunk[0] == 'input':
775                         m = get_re('def-post-re').search(chunk[1])
776                         if m:
777                                 post_b = 1
778                 if chunk[0] == 'input':
779                         m = get_re('usepackage-graphics').search(chunk[1])
780                         if m:
781                                 graphics_b = 1
782         x = 0
783         while chunks[x][0] != 'preamble-end':
784                 x = x + 1
785         if not pre_b:
786                 chunks.insert(x, ('input', get_output ('output-default-pre')))
787         if not post_b:
788                 chunks.insert(x, ('input', get_output ('output-default-post')))
789         if not graphics_b:
790                 chunks.insert(x, ('input', get_output ('usepackage-graphics')))
791         return chunks
792
793
794 read_files = []
795 def find_file (name):
796         """
797         Search the include path for NAME. If found, return the (CONTENTS, PATH) of the file.
798         """
799         
800         f = None
801         nm = ''
802         for a in include_path:
803                 try:
804                         nm = os.path.join (a, name)
805                         f = open (nm)
806                         __main__.read_files.append (nm)
807                         break
808                 except IOError:
809                         pass
810         if f:
811                 sys.stderr.write ("Reading `%s'\n" % nm)
812                 return (f.read (), nm)
813         else:
814                 error ("File not found `%s'\n" % name)
815                 return ('', '')
816
817 def do_ignore(match_object):
818         return [('ignore', match_object.group('code'))]
819 def do_preamble_end(match_object):
820         return [('preamble-end', match_object.group('code'))]
821
822 def make_verbatim(match_object):
823         return [('verbatim', match_object.group('code'))]
824
825 def make_verb(match_object):
826         return [('verb', match_object.group('code'))]
827
828 def do_include_file(m):
829         "m: MatchObject"
830         return [('input', get_output ('pagebreak'))] \
831              + read_doc_file(m.group('filename')) \
832              + [('input', get_output ('pagebreak'))] 
833
834 def do_input_file(m):
835         return read_doc_file(m.group('filename'))
836
837 def make_lilypond(m):
838         if m.group('options'):
839                 options = m.group('options')
840         else:
841                 options = ''
842         return [('input', get_output('output-lilypond-fragment') % 
843                         (options, m.group('code')))]
844
845 def make_lilypond_file(m):
846         """
847
848         Find @lilypondfile{bla.ly} occurences and substitute bla.ly
849         into a @lilypond .. @end lilypond block.
850         
851         """
852         
853         if m.group('options'):
854                 options = m.group('options')
855         else:
856                 options = ''
857         (content, nm) = find_file(m.group('filename'))
858         options = "filename=%s," % nm + options
859
860         return [('input', get_output('output-lilypond') %
861                         (options, content))]
862
863 def make_lilypond_block(m):
864         if m.group('options'):
865                 options = get_re('option-sep').split (m.group('options'))
866         else:
867             options = []
868         options = filter(lambda s: s != '', options)
869         return [('lilypond', m.group('code'), options)]
870
871 def do_columns(m):
872         if __main__.format != 'latex':
873                 return []
874         if m.group('num') == 'one':
875                 return [('numcols', m.group('code'), 1)]
876         if m.group('num') == 'two':
877                 return [('numcols', m.group('code'), 2)]
878         
879 def chop_chunks(chunks, re_name, func, use_match=0):
880     newchunks = []
881     for c in chunks:
882         if c[0] == 'input':
883             str = c[1]
884             while str:
885                 m = get_re (re_name).search (str)
886                 if m == None:
887                     newchunks.append (('input', str))
888                     str = ''
889                 else:
890                     if use_match:
891                         newchunks.append (('input', str[:m.start ('match')]))
892                     else:
893                         newchunks.append (('input', str[:m.start (0)]))
894                     #newchunks.extend(func(m))
895                     # python 1.5 compatible:
896                     newchunks = newchunks + func(m)
897                     str = str [m.end(0):]
898         else:
899             newchunks.append(c)
900     return newchunks
901
902 def determine_format (str):
903         if __main__.format == '':
904                 
905                 latex =  re.search ('\\\\document', str[:200])
906                 texinfo =  re.search ('@node|@setfilename', str[:200])
907
908                 f = ''
909                 g = None
910                 
911                 if texinfo and latex == None:
912                         f = 'texi'
913                 elif latex and texinfo == None: 
914                         f = 'latex'
915                 else:
916                         error("error: can't determine format, please specify")
917                 __main__.format = f
918
919         if __main__.paperguru == None:
920                 if __main__.format == 'texi':
921                         g = TexiPaper()
922                 else:
923                         g = LatexPaper()
924                         
925                 __main__.paperguru = g
926
927
928 def read_doc_file (filename):
929         """Read the input file, find verbatim chunks and do \input and \include
930         """
931         (str, path) = find_file(filename)
932         determine_format (str)
933         
934         chunks = [('input', str)]
935         
936         # we have to check for verbatim before doing include,
937         # because we don't want to include files that are mentioned
938         # inside a verbatim environment
939         chunks = chop_chunks(chunks, 'verbatim', make_verbatim)
940         chunks = chop_chunks(chunks, 'verb', make_verb)
941         chunks = chop_chunks(chunks, 'multiline-comment', do_ignore)
942         #ugh fix input
943         chunks = chop_chunks(chunks, 'include', do_include_file, 1)
944         chunks = chop_chunks(chunks, 'input', do_input_file, 1)
945         return chunks
946
947
948 taken_file_names = {}
949 def schedule_lilypond_block (chunk):
950         """Take the body and options from CHUNK, figure out how the
951         real .ly should look, and what should be left MAIN_STR (meant
952         for the main file).  The .ly is written, and scheduled in
953         TODO.
954
955         Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
956
957         TODO has format [basename, extension, extension, ... ]
958         
959         """
960         (type, body, opts) = chunk
961         assert type == 'lilypond'
962         file_body = compose_full_body (body, opts)
963         basename = 'lily-' + `abs(hash (file_body))`
964         for o in opts:
965                 m = re.search ('filename="(.*?)"', o)
966                 if m:
967                         basename = m.group (1)
968                         if not taken_file_names.has_key(basename):
969                             taken_file_names[basename] = 0
970                         else:
971                             taken_file_names[basename] = taken_file_names[basename] + 1
972                             basename = basename + "-%i" % taken_file_names[basename]
973         if not g_read_lys:
974                 update_file(file_body, os.path.join(g_outdir, basename) + '.ly')
975         needed_filetypes = ['tex']
976
977         if format  == 'texi':
978                 needed_filetypes.append('eps')
979                 needed_filetypes.append('png')
980         if 'eps' in opts and not ('eps' in needed_filetypes):
981                 needed_filetypes.append('eps')
982         pathbase = os.path.join (g_outdir, basename)
983         def f(base, ext1, ext2):
984                 a = os.path.isfile(base + ext2)
985                 if (os.path.isfile(base + ext1) and
986                     os.path.isfile(base + ext2) and
987                                 os.stat(base+ext1)[stat.ST_MTIME] >
988                                 os.stat(base+ext2)[stat.ST_MTIME]) or \
989                                 not os.path.isfile(base + ext2):
990                         return 1
991         todo = []
992         if 'tex' in needed_filetypes and f(pathbase, '.ly', '.tex'):
993                 todo.append('tex')
994         if 'eps' in needed_filetypes and f(pathbase, '.tex', '.eps'):
995                 todo.append('eps')
996         if 'png' in needed_filetypes and f(pathbase, '.eps', '.png'):
997                 todo.append('png')
998         newbody = ''
999
1000         if 'printfilename' in opts:
1001                 for o in opts:
1002                         m= re.match ("filename=(.*)", o)
1003                         if m:
1004                                 newbody = newbody + get_output ("output-filename") % m.group(1)
1005                                 break
1006                 
1007         
1008         if 'verbatim' in opts:
1009                 newbody = output_verbatim (body)
1010
1011         for o in opts:
1012                 m = re.search ('intertext="(.*?)"', o)
1013                 if m:
1014                         newbody = newbody  + m.group (1) + "\n\n"
1015         
1016         if 'noinline' in opts:
1017                 s = 'output-noinline'
1018         elif format == 'latex':
1019                 if 'eps' in opts:
1020                         s = 'output-eps'
1021                 else:
1022                         s = 'output-tex'
1023         else: # format == 'texi'
1024                 s = 'output-all'
1025         newbody = newbody + get_output (s) % {'fn': basename }
1026         return ('lilypond', newbody, opts, todo, basename)
1027
1028 def process_lilypond_blocks(outname, chunks):#ugh rename
1029         newchunks = []
1030         # Count sections/chapters.
1031         for c in chunks:
1032                 if c[0] == 'lilypond':
1033                         c = schedule_lilypond_block (c)
1034                 elif c[0] == 'numcols':
1035                         paperguru.m_num_cols = c[2]
1036                 newchunks.append (c)
1037         return newchunks
1038
1039
1040
1041 def system (cmd):
1042         sys.stderr.write ("invoking `%s'\n" % cmd)
1043         st = os.system (cmd)
1044         if st:
1045                 error ('Error command exited with value %d\n' % st)
1046         return st
1047
1048
1049 def get_bbox (filename):
1050         system ('gs -sDEVICE=bbox -q  -sOutputFile=- -dNOPAUSE %s -c quit > %s.bbox 2>&1 ' % (filename, filename))
1051
1052         box = open (filename + '.bbox').read()
1053         m = re.match ('^%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', box)
1054         gr = []
1055         if m:
1056                 gr = map (string.atoi, m.groups ())
1057         
1058         return gr
1059
1060 def make_pixmap (name):
1061         bbox = get_bbox (name + '.eps')
1062         margin = 0
1063         fo = open (name + '.trans.eps' , 'w')
1064         fo.write ('%d %d translate\n' % (-bbox[0]+margin, -bbox[1]+margin))
1065         fo.close ()
1066         
1067         res = 90
1068
1069         x = (2* margin + bbox[2] - bbox[0]) * res / 72.
1070         y = (2* margin + bbox[3] - bbox[1]) * res / 72.
1071
1072         cmd = r"""gs -g%dx%d -sDEVICE=pgm  -dTextAlphaBits=4 -dGraphicsAlphaBits=4  -q -sOutputFile=- -r%d -dNOPAUSE %s %s -c quit | pnmtopng > %s"""
1073         
1074         cmd = cmd % (x, y, res, name + '.trans.eps', name + '.eps',name + '.png')
1075         try:
1076                 status = system (cmd)
1077         except:
1078                 os.unlink (name + '.png')
1079                 error ("Removing output file")
1080
1081 def compile_all_files (chunks):
1082         global foutn
1083         eps = []
1084         tex = []
1085         png = []
1086
1087         for c in chunks:
1088                 if c[0] <> 'lilypond':
1089                         continue
1090                 base  = c[4]
1091                 exts = c[3]
1092                 for e in exts:
1093                         if e == 'eps':
1094                                 eps.append (base)
1095                         elif e == 'tex':
1096                                 #ugh
1097                                 if base + '.ly' not in tex:
1098                                         tex.append (base + '.ly')
1099                         elif e == 'png' and g_do_pictures:
1100                                 png.append (base)
1101         d = os.getcwd()
1102         if g_outdir:
1103                 os.chdir(g_outdir)
1104         if tex:
1105                 # fixme: be sys-independent.
1106                 def incl_opt (x):
1107                         if g_outdir and x[0] <> '/' :
1108                                 x = os.path.join (g_here_dir, x)
1109                         return ' -I %s' % x
1110
1111                 incs = map (incl_opt, include_path)
1112                 lilyopts = string.join (incs, ' ' )
1113                 if do_deps:
1114                         lilyopts = lilyopts + ' --dependencies '
1115                         if g_outdir:
1116                                 lilyopts = lilyopts + '--dep-prefix=' + g_outdir + '/'
1117                 texfiles = string.join (tex, ' ')
1118                 system ('lilypond --header=texidoc %s %s %s' % (lilyopts, g_extra_opts, texfiles))
1119
1120                 #
1121                 # Ugh, fixing up dependencies for .tex generation
1122                 #
1123                 if do_deps:
1124                         depfiles=map (lambda x: re.sub ('(.*)\.ly', '\\1.dep', x), tex)
1125                         for i in depfiles:
1126                                 f =open (i)
1127                                 text=f.read ()
1128                                 f.close ()
1129                                 text=re.sub ('\n([^:\n]*):', '\n' + foutn + ':', text)
1130                                 f = open (i, 'w')
1131                                 f.write (text)
1132                                 f.close ()
1133
1134         for e in eps:
1135                 system(r"tex '\nonstopmode \input %s'" % e)
1136                 system(r"dvips -E -o %s %s" % (e + '.eps', e))
1137                 
1138         for g in png:
1139                 make_pixmap (g)
1140                 
1141         os.chdir (d)
1142
1143
1144 def update_file (body, name):
1145         """
1146         write the body if it has changed
1147         """
1148         same = 0
1149         try:
1150                 f = open (name)
1151                 fs = f.read (-1)
1152                 same = (fs == body)
1153         except:
1154                 pass
1155
1156         if not same:
1157                 f = open (name , 'w')
1158                 f.write (body)
1159                 f.close ()
1160         
1161         return not same
1162
1163
1164 def getopt_args (opts):
1165         "Construct arguments (LONG, SHORT) for getopt from  list of options."
1166         short = ''
1167         long = []
1168         for o in opts:
1169                 if o[1]:
1170                         short = short + o[1]
1171                         if o[0]:
1172                                 short = short + ':'
1173                 if o[2]:
1174                         l = o[2]
1175                         if o[0]:
1176                                 l = l + '='
1177                         long.append (l)
1178         return (short, long)
1179
1180 def option_help_str (o):
1181         "Transform one option description (4-tuple ) into neatly formatted string"
1182         sh = '  '       
1183         if o[1]:
1184                 sh = '-%s' % o[1]
1185
1186         sep = ' '
1187         if o[1] and o[2]:
1188                 sep = ','
1189                 
1190         long = ''
1191         if o[2]:
1192                 long= '--%s' % o[2]
1193
1194         arg = ''
1195         if o[0]:
1196                 if o[2]:
1197                         arg = '='
1198                 arg = arg + o[0]
1199         return '  ' + sh + sep + long + arg
1200
1201
1202 def options_help_str (opts):
1203         "Convert a list of options into a neatly formatted string"
1204         w = 0
1205         strs =[]
1206         helps = []
1207
1208         for o in opts:
1209                 s = option_help_str (o)
1210                 strs.append ((s, o[3]))
1211                 if len (s) > w:
1212                         w = len (s)
1213
1214         str = ''
1215         for s in strs:
1216                 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0])  + 3), s[1])
1217         return str
1218
1219 def help():
1220         sys.stdout.write("""Usage: lilypond-book [options] FILE\n
1221 Generate hybrid LaTeX input from Latex + lilypond
1222 Options:
1223 """)
1224         sys.stdout.write (options_help_str (option_definitions))
1225         sys.stdout.write (r"""Warning all output is written in the CURRENT directory
1226
1227
1228
1229 Report bugs to bug-lilypond@gnu.org.
1230
1231 Written by Tom Cato Amundsen <tca@gnu.org> and
1232 Han-Wen Nienhuys <hanwen@cs.uu.nl>
1233 """)
1234
1235         sys.exit (0)
1236
1237
1238 def write_deps (fn, target, chunks):
1239         global read_files
1240         sys.stdout.write('Writing `%s\'\n' % os.path.join(g_outdir, fn))
1241         f = open (os.path.join(g_outdir, fn), 'w')
1242         f.write ('%s%s: ' % (g_dep_prefix, target))
1243         for d in read_files:
1244                 f.write ('%s ' %  d)
1245         basenames=[]
1246         for c in chunks:
1247                 if c[0] == 'lilypond':
1248                         (type, body, opts, todo, basename) = c;
1249                         basenames.append (basename)
1250         for d in basenames:
1251                 if g_outdir:
1252                         d=g_outdir + '/' + d
1253                 if g_dep_prefix:
1254                         #if not os.isfile (d): # thinko?
1255                         if not re.search ('/', d):
1256                                 d = g_dep_prefix + d
1257                 f.write ('%s.tex ' %  d)
1258         f.write ('\n')
1259         #if len (basenames):
1260         #       for d in basenames:
1261         #               f.write ('%s.ly ' %  d)
1262         #       f.write (' : %s' % target)
1263         f.write ('\n')
1264         f.close ()
1265         read_files = []
1266
1267 def identify():
1268         sys.stdout.write ('lilypond-book (GNU LilyPond) %s\n' % program_version)
1269
1270 def print_version ():
1271         identify()
1272         sys.stdout.write (r"""Copyright 1998--1999
1273 Distributed under terms of the GNU General Public License. It comes with
1274 NO WARRANTY.
1275 """)
1276
1277
1278 def check_texidoc (chunks):
1279         n = []
1280         for c in chunks:
1281                 if c[0] == 'lilypond':
1282                         (type, body, opts, todo, basename) = c;
1283                         pathbase = os.path.join (g_outdir, basename)
1284                         if os.path.isfile (pathbase + '.texidoc'):
1285                                 body = '\n@include %s.texidoc\n' % basename + body
1286                                 c = (type, body, opts, todo, basename)
1287                 n.append (c)
1288         return n
1289
1290
1291 ## what's this? Docme --hwn
1292 ##
1293 def fix_epswidth (chunks):
1294         newchunks = []
1295         for c in chunks:
1296                 if c[0] <> 'lilypond' or 'eps' not in c[2]:
1297                         newchunks.append (c)
1298                         continue
1299
1300                 mag = 1.0
1301                 for o in c[2]:
1302                         m  = re.match ('magnification=([0-9.]+)', o)
1303                         if m:
1304                                 mag = string.atof (m.group (1))
1305
1306                 def replace_eps_dim (match, lmag = mag):
1307                         filename = match.group (1)
1308                         dims = bounding_box_dimensions (filename)
1309
1310                         return '%fpt' % (dims[0] *lmag)
1311         
1312                 body = re.sub (r"""\\lilypondepswidth{(.*?)}""", replace_eps_dim, c[1])
1313                 newchunks.append(('lilypond', body, c[2], c[3], c[4]))
1314                         
1315         return newchunks
1316
1317
1318 foutn=""
1319 def do_file(input_filename):
1320         global foutn
1321         file_settings = {}
1322         if outname:
1323                 my_outname = outname
1324         else:
1325                 my_outname = os.path.basename(os.path.splitext(input_filename)[0])
1326         my_depname = my_outname + '.dep'                
1327
1328         chunks = read_doc_file(input_filename)
1329         chunks = chop_chunks(chunks, 'lilypond', make_lilypond, 1)
1330         chunks = chop_chunks(chunks, 'lilypond-file', make_lilypond_file, 1)
1331         chunks = chop_chunks(chunks, 'lilypond-block', make_lilypond_block, 1)
1332         chunks = chop_chunks(chunks, 'singleline-comment', do_ignore, 1)
1333         chunks = chop_chunks(chunks, 'preamble-end', do_preamble_end)
1334         chunks = chop_chunks(chunks, 'numcols', do_columns)
1335         #print "-" * 50
1336         #for c in chunks: print "c:", c;
1337         #sys.exit()
1338         scan_preamble(chunks)
1339         chunks = process_lilypond_blocks(my_outname, chunks)
1340
1341         foutn = os.path.join (g_outdir, my_outname + '.' + format)
1342
1343         # Do It.
1344         if __main__.g_run_lilypond:
1345                 compile_all_files (chunks)
1346                 chunks = fix_epswidth (chunks)
1347
1348         if __main__.format == 'texi':
1349                 chunks = check_texidoc (chunks)
1350
1351         x = 0
1352         chunks = completize_preamble (chunks)
1353         sys.stderr.write ("Writing `%s'\n" % foutn)
1354         fout = open (foutn, 'w')
1355         for c in chunks:
1356                 fout.write (c[1])
1357         fout.close ()
1358         # should chmod -w
1359
1360         if do_deps:
1361                 write_deps (my_depname, foutn, chunks)
1362
1363
1364 outname = ''
1365 try:
1366         (sh, long) = getopt_args (__main__.option_definitions)
1367         (options, files) = getopt.getopt(sys.argv[1:], sh, long)
1368 except getopt.error, msg:
1369         sys.stderr.write("error: %s" % msg)
1370         sys.exit(1)
1371
1372 do_deps = 0
1373 for opt in options:     
1374         o = opt[0]
1375         a = opt[1]
1376
1377         if o == '--include' or o == '-I':
1378                 include_path.append (a)
1379         elif o == '--version' or o == '-v':
1380                 print_version ()
1381                 sys.exit  (0)
1382         elif o == '--format' or o == '-f':
1383                 __main__.format = a
1384         elif o == '--outname' or o == '-o':
1385                 if len(files) > 1:
1386                         #HACK
1387                         sys.stderr.write("Lilypond-book is confused by --outname on multiple files")
1388                         sys.exit(1)
1389                 outname = a
1390         elif o == '--help' or o == '-h':
1391                 help ()
1392         elif o == '--no-lily' or o == '-n':
1393                 __main__.g_run_lilypond = 0
1394         elif o == '--dependencies' or o == '-M':
1395                 do_deps = 1
1396         elif o == '--default-music-fontsize':
1397                 default_music_fontsize = string.atoi (a)
1398         elif o == '--default-lilypond-fontsize':
1399                 print "--default-lilypond-fontsize is deprecated, use --default-music-fontsize"
1400                 default_music_fontsize = string.atoi (a)
1401         elif o == '--extra-options':
1402                 g_extra_opts = a
1403         elif o == '--force-music-fontsize':
1404                 g_force_lilypond_fontsize = string.atoi(a)
1405         elif o == '--force-lilypond-fontsize':
1406                 print "--force-lilypond-fontsize is deprecated, use --default-lilypond-fontsize"
1407                 g_force_lilypond_fontsize = string.atoi(a)
1408         elif o == '--dep-prefix':
1409                 g_dep_prefix = a
1410         elif o == '--no-pictures':
1411                 g_do_pictures = 0
1412         elif o == '--read-lys':
1413                 g_read_lys = 1
1414         elif o == '--outdir':
1415                 g_outdir = a
1416
1417 identify()
1418 if g_outdir:
1419         if os.path.isfile(g_outdir):
1420                 error ("outdir is a file: %s" % g_outdir)
1421         if not os.path.exists(g_outdir):
1422                 os.mkdir(g_outdir)
1423 setup_environment ()
1424 for input_filename in files:
1425         do_file(input_filename)
1426         
1427 #
1428 # Petr, ik zou willen dat ik iets zinvoller deed,
1429 # maar wat ik kan ik doen, het verandert toch niets?
1430 #   --hwn 20/aug/99