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