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