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