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