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