]> git.donarmstrong.com Git - lilypond.git/blob - scripts/mudela-book.py
patch::: 1.3.49.hwn1: deze dus
[lilypond.git] / scripts / mudela-book.py
1 #!@PYTHON@
2
3 import os
4 import string
5 import re
6 import getopt
7 import sys
8 import __main__
9
10
11 initfile = ''
12 program_version = '@TOPLEVEL_VERSION@'
13
14 cwd = os.getcwd ()
15 include_path = [cwd]
16 dep_prefix = ''
17
18 # TODO: Figure out clean set of options.
19
20 # BUG: does not handle \verb|\begin{verbatim}\end{verbatim}| correctly.
21 # Should make a joint RE for \verb and \begin, \end{verbatim}
22
23 # TODO: add an option to read the .ly files from a previous run and dump
24 # the .tex file, so you can do
25 #
26 # * mudela-book file.tex
27 # * convert-mudela *.ly
28 # * mudela-book --read-lys *.ly
29 #
30
31 default_music_fontsize = 16
32 default_text_fontsize = 12
33
34 # latex linewidths:
35 # indices are no. of columns, papersize,  fontsize
36 # Why can't this be calculated?
37 latex_linewidths = {
38  1: {'a4':{10: 345, 11: 360, 12: 390},
39          'a5':{10: 276, 11: 276, 12: 276},
40          'b5':{10: 345, 11: 356, 12: 356},
41          'letter':{10: 345, 11: 360, 12: 390},
42          'legal': {10: 345, 11: 360, 12: 390},
43          'executive':{10: 345, 11: 360, 12: 379}},
44  2: {'a4':{10: 167, 11: 175, 12: 190},
45          'a5':{10: 133, 11: 133, 12: 133},
46          'b5':{10: 167, 11: 173, 12: 173},
47          'letter':{10: 167, 11: 175, 12: 190},
48          'legal':{10: 167, 11: 175, 12: 190},
49          'executive':{10: 167, 11: 175, 12: 184}}}
50
51
52 option_definitions = [
53   ('DIM',  '', 'default-mudela-fontsize', 'default fontsize for music.  DIM is assumed to in points'),
54   ('EXT', 'f', 'format', 'set format.  EXT is one of texi and latex.'),
55   ('', 'h', 'help', 'print help'),
56   ('DIR', 'I', 'include', 'include path'),
57   ('', '', 'init', 'mudela-book initfile'),
58 #  ('DIM', '', 'force-mudela-fontsize', 'force fontsize for all inline mudela. DIM is assumed to in points'),
59   ('', '', 'force-verbatim', 'make all mudela verbatim'),
60   ('', 'M', 'dependencies', 'write dependencies'),
61   ('', 'n', 'no-lily', 'don\'t run lilypond'),
62   ('', '', 'no-pictures', "don\'t generate pictures"),
63   ('FILE', 'o', 'outname', 'prefix for filenames'),
64   ('', 'v', 'version', 'print version information' ),
65   ('PREF', '',  'dep-prefix', 'prepend PREF before each -M dependency')
66   ]
67
68 format = ''
69 run_lilypond = 1
70 use_hash = 1
71 no_match = 'a\ba'
72 do_pictures = 1
73
74 # format specific strings, ie. regex-es for input, and % strings for output
75 output_dict= {
76         'latex': {
77                 'output-mudela-fragment' : r"""\begin[eps,fragment%s]{mudela}
78   \context Staff <
79     \context Voice{
80       %s
81     }
82   >
83 \end{mudela}""", 
84                 'output-mudela':r"""\begin[%s]{mudela}
85 %s
86 \end{mudela}""",
87                 'output-verbatim': r"""\begin{verbatim}%s\end{verbatim}""",
88                 'output-default-post': r"""\def\postMudelaExample{}""",
89                 'output-default-pre': r"""\def\preMudelaExample{}""",
90                 'output-eps': '\\noindent\\parbox{\\mudelaepswidth{%s.eps}}{\includegraphics{%s.eps}}',
91                 'output-tex': '\\preMudelaExample \\input %s.tex \\postMudelaExample\n'
92                 },
93         'texi' : {'output-mudela': """@mudela[%s]
94 %s
95 @end mudela
96 """,
97                   'output-verbatim': r"""@example
98 %s
99 @end example
100 """,
101
102 # do some tweaking: @ is needed in some ps stuff.
103 # override EndLilyPondOutput, since @tex is done
104 # in a sandbox, you can't do \input lilyponddefs at the
105 # top of the document.
106
107 # should also support fragment in
108                   
109                   'output-all': r"""@tex
110 \catcode`\@=12
111 \input lilyponddefs
112 \def\EndLilyPondOutput{}
113 \input %s.tex
114 \catcode`\@=0
115 @end tex
116 @html
117 <p>
118 <img src=%s.png>
119 @end html
120 """,
121                 }
122         }
123
124 def output_verbatim (body):
125         if __main__.format == 'texi':
126                 body = re.sub ('([@{}])', '@\\1', body)
127         return get_output ('output-verbatim') % body
128
129 re_dict = {
130         'latex': {'input': '\\\\mbinput{?([^}\t \n}]*)',
131                   'include': '\\\\mbinclude{([^}]+)}',
132                  
133                   'comma-sep' : ', *',
134                   'header': r"""\\documentclass(\[.*?\])?""",
135                   'preamble-end': '\\\\begin{document}',
136                   'verbatim': r"""(?s)\\begin{verbatim}(.*?)\\end{verbatim}""",
137                   'verb': r"""\\verb(.)(.*?)\1""",
138                   'mudela-file': '\\\\mudelafile(\[[^\\]]+\])?{([^}]+)}',
139                   'mudela' : '\\\\mudela(\[.*?\])?{(.*?)}',
140                   'mudela-block': r"""(?s)\\begin(\[.*?\])?{mudela}(.*?)\\end{mudela}""",
141                   'interesting-cs': '\\\\(chapter|section|twocolumn|onecolumn)',
142                   'def-post-re': r"""\\def\\postMudelaExample""",
143                   'def-pre-re': r"""\\def\\preMudelaExample""",           
144                   },
145         
146         'texi': {
147                  'input':  '@mbinclude[ \n\t]+([^\t \n]*)',# disabled
148                  'include': no_match,
149                  'header': no_match,
150                  'preamble-end': no_match,
151                  'verbatim': r"""(?s)@example(.*?)@end example$""",
152                  'verb': r"""@code{(.*?)}""",
153                  'mudela-file': '@mudelafile(\[[^\\]]+\])?{([^}]+)}',
154                  'mudela' : '@mudela(\[.*?\])?{(.*?)}',
155                  'mudela-block': r"""(?s)@mudela(\[.*?\])?(.*?)@end mudela""",
156                  'interesting-cs': r"""[\\@](chapter|section)""",
157                   'comma-sep' : ', *',           
158                  }
159         }
160
161
162 for r in re_dict.keys ():
163         olddict = re_dict[r]
164         newdict = {}
165         for k in olddict.keys ():
166                 newdict[k] = re.compile (olddict[k])
167         re_dict[r] = newdict
168
169         
170 def uniq (list):
171         list.sort ()
172         s = list
173         list = []
174         for x in s:
175                 if x not in list:
176                         list.append (x)
177         return list
178                 
179
180 def get_output (name):
181         return  output_dict[format][name]
182
183 def get_re (name):
184         return  re_dict[format][name]
185
186
187 def bounding_box_dimensions(fname):
188         try:
189                 fd = open(fname)
190         except IOError:
191                 error ("Error opening `%s'" % fname)
192         str = fd.read ()
193         s = re.search('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)', str)
194         if s:
195                 return (int(s.group(3))-int(s.group(1)), 
196                         int(s.group(4))-int(s.group(2)))
197         else:
198                 return (0,0)
199
200
201
202 read_files = []
203 def find_file (name):
204         f = None
205         for a in include_path:
206                 try:
207                         nm = os.path.join (a, name)
208                         f = open (nm)
209                         __main__.read_files.append (nm)
210                         break
211                 except IOError:
212                         pass
213
214
215         if f:
216                 return f.read ()
217         else:
218                 error ("File not found `%s'\n" % name)
219                 return ''
220
221 def error (str):
222         sys.stderr.write (str + "\n  Exiting ... \n\n")
223         raise 'Exiting.'
224
225
226 def compose_full_body (body, opts):
227         "Construct the text of an input file: add stuff to BODY using OPTS as options."
228         paper = 'a4'
229         music_size = default_music_fontsize
230         latex_size = default_text_fontsize
231
232         cols = 1
233         for o in opts:
234                 m = re.search ('^(.*)paper$', o)
235                 if m:
236                         paper = m.group (1)
237                 
238
239                 m = re.match ('([0-9]+)pt', o)
240                 if m:
241                         music_size = string.atoi(m.group (1))
242
243                 m = re.match ('latexfontsize=([0-9]+)pt', o)
244                 if m:
245                         latex_size = string.atoi (m.group (1))
246
247
248
249         if 'twocolumn' in opts:
250                 cols = 2
251                 
252
253         # urg: breaks on \include of full score
254         # Use nofly option if you want to \include full score.
255         if 'nofly'  not in opts and not re.search ('\\\\score', body):
256                 opts.append ('fragment')
257
258         if 'fragment' in opts and 'nosingleline' not in opts:
259                 opts.append ('singleline')
260
261         if 'singleline' in opts:
262                 l = -1.0;
263         else:
264                 l = latex_linewidths[cols][paper][latex_size]
265
266
267         if 'relative' in opts:
268                 body = '\\relative c { %s }' % body
269
270         
271         if 'fragment' in opts:
272                 body = r"""\score { 
273   \notes { %s }
274   \paper { }  
275 }""" % body
276
277         opts = uniq (opts)
278         optstring = string.join (opts, ' ')
279         optstring = re.sub ('\n', ' ', optstring)
280         
281         body = r"""
282 %% Generated by mudela-book.py; options are %s
283 \include "paper%d.ly"
284 \paper  { linewidth = %f \pt; } 
285 """ % (optstring, music_size, l) + body
286
287         return body
288
289 def find_inclusion_chunks (regex, surround, str):
290         chunks = []
291         while str:
292                 m = regex.search (str)
293
294                 if m == None:
295                         chunks.append (('input', str))
296                         str = ''
297                         break
298
299                 chunks.append (('input', str[: m.start (0)]))
300                 chunks.append (('input', surround))
301                 chunks = chunks +  read_doc_file (m.group (1))
302
303                 chunks.append (('input', surround))
304
305                 str = str [m.end (0):]
306         return chunks
307
308 def find_include_chunks (str):
309         return find_inclusion_chunks (get_re ('include'), '\\newpage', str)
310
311 def find_input_chunks (str):
312         return find_inclusion_chunks (get_re ('input'), '', str)        
313
314 def read_doc_file (filename):
315         """Read the input file, substituting for \input, \include, \mudela{} and \mudelafile"""
316         str = ''
317         str = find_file(filename)
318
319         if __main__.format == '':
320                 latex =  re.search ('\\\\document', str[:200])
321                 texinfo =  re.search ('@node', str[:200])
322                 if (texinfo and latex) or not (texinfo or latex):
323                         error("error: can't determine format, please specify")
324                 if texinfo:
325                         __main__.format = 'texi'
326                 else:
327                         __main__.format = 'latex'
328                         
329         chunks = [('input', str)]
330
331         for func in (find_verbatim_chunks, find_verb_chunks, find_include_chunks, find_input_chunks):
332                 newchunks = []
333                 for c in chunks:
334                         if c[0] == 'input':
335                                 newchunks = newchunks + func (c[1])
336                         else:
337                                 newchunks.append (c)
338                 chunks = newchunks
339         
340         return chunks
341
342
343
344 def scan_preamble (str):
345         options = []
346         m = get_re ('header').search( str)
347
348         # should extract paper & fontsz.
349         if m and m.group (1):
350                 options = options + re.split (',[\n \t]*', m.group(1)[1:-1])
351
352         def verbose_fontsize ( x):
353                 # o ??
354                 #if o.match('[0-9]+pt'):
355                 if re.match('[0-9]+pt', x):
356                         return 'latexfontsize=' + x
357                 else:
358                         return x 
359                         
360         options = map (verbose_fontsize, options)
361
362         return options
363
364
365 def completize_preamble (str):
366         m = get_re ('preamble-end').search( str)
367         if not m:
368                 return str
369         
370         preamble = str [:m.start (0)]
371         str = str [m.start(0):]
372         
373         if not get_re('def-post-re').search (preamble):
374                 preamble = preamble + get_output('output-default-post')
375         if not get_re ('def-pre-re').search(  preamble):
376                 preamble = preamble + get_output ('output-default-pre')
377
378         # UGH ! BUG!
379         #if  re.search ('\\\\includegraphics', str) and not re.search ('usepackage{graphics}',str):
380
381         preamble = preamble + '\\usepackage{graphics}\n'
382
383         return preamble + str
384
385 def find_verbatim_chunks (str):
386         """Chop STR into a list of tagged chunks, ie. tuples of form
387         (TYPE_STR, CONTENT_STR), where TYPE_STR is one of 'input' and 'verbatim'
388
389         """
390
391         chunks = []
392         while str:
393                 m = get_re ('verbatim').search( str)
394                 if m == None:
395                         chunks.append( ('input', str))
396                         str = ''
397                 else:
398                         chunks.append (('input', str[:m.start (0)]))
399                         chunks.append (('verbatim', m.group (0)))
400                 
401                         str = str [m.end(0):]
402                 
403         return chunks
404
405 def find_verb_chunks (str):
406
407         chunks = []
408         while str:
409                 m = get_re ("verb").search(str)
410                 if  m == None:
411                         chunks.append (('input', str))
412                         str = ''
413                 else:
414                         chunks.append (('input', str[:m.start (0)]))
415                         chunks.append (('verbatim', m.group (0)))
416                         str = str [m.end(0):]
417
418         return chunks
419                         
420
421
422 def find_mudela_shorthand_chunks (str):
423         return [('input', find_mudela_shorthands(str))]
424         
425 def find_mudela_shorthands (b):
426         def mudela_short (match):
427                 "Find \mudela{}, and substitute appropriate \begin / \end blocks."
428                 opts = match.group (1)
429                 if opts:
430                         opts = ',' + opts[1:-1]
431                 else:
432                         opts = ''
433                 return get_output ('output-mudela-fragment')  % (opts, match.group (2))
434
435         def mudela_file (match):
436                 "Find \mudelafile, and substitute appropriate \begin / \end blocks."
437                 fn = match.group (2)
438                 str = find_file (fn)
439                 opts = match.group (1)
440                 if opts:
441                         opts = opts[1:-1]
442                         opts = re.split (',[ \n\t]*', opts)
443                 else:
444                         opts = []
445
446                 if re.search ('.fly$', fn):
447                         opts.append ('fly')
448                 elif re.search ('.sly$', fn):
449                         opts = opts + [ 'fly','fragment']
450                 elif re.search ('.ly$', fn):
451                         opts .append ('nofly')
452                         
453                 str_opts = string.join (opts, ',')
454
455                 str = ("%% copied from file `%s'\n" % fn) + str 
456                 return get_output ('output-mudela') % (str_opts, str)
457   
458         b = get_re('mudela-file').sub (mudela_file, b)
459         b = get_re('mudela').sub (mudela_short, b)
460         return b
461         
462 def find_mudela_chunks (str):
463         """Find mudela blocks, while watching for verbatim. Returns
464         (STR,MUDS) with  substituted for the blocks in STR,
465         and the blocks themselves MUDS"""
466   
467         chunks = []
468         while str:
469                 m = get_re ("mudela-block").search( str)
470                 if not m:
471                         chunks.append (('input', str))
472                         str = ''
473                         break
474
475                 chunks.append (('input', str[:m.start (0)]))
476                 
477                 opts = m.group (1)
478                 if opts:
479                         opts = opts[1:-1]
480                 else:
481                         opts = ''
482                 optlist = get_re('comma-sep').split (opts)
483                 
484                 body = m.group (2)
485                 chunks.append (('mudela', body, optlist))
486   
487                 str = str [m.end (0):]
488   
489         return chunks
490   
491   
492   
493 def advance_counters (counter, opts, str):
494         """Advance chap/sect counters,
495         revise OPTS. Return the new counter tuple"""
496         
497         (chapter, section, count) = counter
498         done = ''
499         while str:
500                 m = get_re ('interesting-cs').search(str)
501                 if not m:
502                         done = done + str
503                         str = ''
504                         break
505                 
506                 done = done + str[:m.end (0)]
507                 str = str[m.end(0):]
508                 g = m.group (1)
509
510                 if g == 'twocolumn':
511                         opts.append ('twocolumn')
512                 elif g  == 'onecolumn':
513                         try:
514                                 opts.remove ('twocolumn')
515                         except IndexError:
516                                 pass
517                 elif g == 'chapter':
518                         (chapter, section, count)  = (chapter + 1, 0, 0)
519                 elif g == 'section':
520                         (section, count)  = (section + 1, 0)
521                         
522
523         return (chapter, section, count)
524
525
526 def schedule_mudela_block (base, chunk, extra_opts):
527         """Take the body and options from CHUNK, figure out how the
528         real .ly should look, and what should be left MAIN_STR (meant
529         for the main file).  The .ly is written, and scheduled in
530         TODO.
531
532         Return: a chunk (TYPE_STR, MAIN_STR, OPTIONS, TODO, BASE)
533
534         TODO has format [basename, extension, extension, ... ]
535         
536         """
537
538         (type, body, opts) = chunk
539         assert type == 'mudela'
540         opts = opts +  extra_opts
541         
542         newbody = ''
543         if 'verbatim' in opts:
544                 newbody = output_verbatim (body)
545
546         file_body = compose_full_body (body, opts)
547         basename = base
548         if __main__.use_hash:
549                 basename = `abs(hash (file_body))`
550         updated = update_file (file_body, basename + '.ly')
551         todo = [basename]                       # UGH.
552
553         if not os.path.isfile (basename + '.tex') or updated:
554                 todo.append ('tex')
555                 updated = 1
556
557         for o in opts:
558                 m = re.search ('intertext="(.*?)"', o)
559                 if m:
560                         newbody = newbody  + m.group (1)
561
562         if format == 'texi':
563                 opts.append ('png')
564
565         if 'png' in opts:
566                 opts.append ('eps')
567
568         if 'eps' in opts and ('tex' in todo or
569                               not os.path.isfile (basename + '.eps')):
570                 todo.append ('eps')
571
572         if 'png' in opts and ('eps' in todo or
573                               not os.path.isfile (basename + '.png')):
574                 todo.append ('png')
575
576         if format == 'latex':
577                 if 'eps' in opts :
578                         newbody = newbody + get_output ('output-eps') %  (basename, basename)
579                 else:
580                         newbody = newbody + get_output ('output-tex') % basename
581
582         elif format == 'texi':
583                 newbody = newbody + get_output ('output-all') % (basename, basename) 
584
585         return ('mudela', newbody, opts, todo, base)
586
587
588 def find_eps_dims (match):
589         "Fill in dimensions of EPS files."
590         
591         fn =match.group (1)
592         dims = bounding_box_dimensions (fn)
593
594         return '%ipt' % dims[0]
595
596
597 def print_chunks (ch):
598         for c in ch:
599                 print '-->%s\n%s' % (c[0], c[1])
600                 if len (c) > 2:
601                         print '==>%s' % list (c[2:])
602         print foo
603
604
605 def transform_input_file (in_filename, out_filename):
606         """Read the input, and deliver a list of chunks
607         ready for writing.
608
609         """
610
611         chunks = read_doc_file (in_filename)
612
613         #.  Process \mudela and \mudelafile.
614         for func in [find_mudela_shorthand_chunks,
615                      find_mudela_chunks]:
616                 newchunks = []
617                 for c in chunks:
618                         if c[0] == 'input':
619                                 newchunks = newchunks + func (c[1])
620                         else:
621                                 newchunks.append (c)
622                 chunks = newchunks
623
624         opts = []
625         if chunks:
626                 opts = scan_preamble (chunks[0][1]) 
627         
628         (chap,sect,count) = (0,0,0)
629         newchunks = []
630         # Count sections/chapters.
631         for c in chunks:
632                 if c[0] == 'input':
633                         (chap,sect,count) = advance_counters((chap,sect,count), opts, c[1])
634                 elif c[0] == 'mudela':
635                         base = '%s-%d.%d.%d' % (out_filename, chap, sect, count)
636                         count = count + 1
637                         c = schedule_mudela_block (base, c, opts)
638                         
639                 newchunks.append (c)
640
641         chunks = newchunks
642         newchunks = []
643
644         # Do It.
645         if __main__.run_lilypond:
646                 compile_all_files (chunks)
647                 
648                 # finishing touch.
649                 for c in chunks:
650                         if c[0] == 'mudela' and 'eps' in c[2]:
651                                 body = re.sub (r"""\\mudelaepswidth{(.*?)}""", find_eps_dims, c[1])
652                                 newchunks.append (('mudela', body))
653                         else:
654                                 newchunks.append (c)
655                 chunks = newchunks
656                 
657         if chunks and chunks[0][0] == 'input':
658                 chunks[0] = ('input', completize_preamble (chunks[0][1]))
659
660         return chunks
661
662 def system (cmd):
663         sys.stderr.write ("invoking `%s'\n" % cmd)
664         st = os.system (cmd)
665         if st:
666                 error ('Error command exited with value %d\n' % st)
667         return st
668
669 def compile_all_files (chunks):
670         eps = []
671         tex = []
672         png = []
673         hash_dict = {}
674
675         for c in chunks:
676                 if c[0] <> 'mudela':
677                         continue
678                 base  = c[3][0]
679                 exts = c[3][1:]
680                 for e in exts:
681                         if e == 'eps':
682                                 eps.append (base)
683                         elif e == 'tex':
684                                 tex.append (base + '.ly')
685                         elif e == 'png' and do_pictures:
686                                 png.append (base)
687
688                 if __main__.use_hash:
689                         hash_dict[c[4]] = c[3][0]
690
691         if tex:
692                 lilyopts = map (lambda x:  '-I ' + x, include_path)
693                 lilyopts = string.join (lilyopts, ' ' )
694                 texfiles = string.join (tex, ' ')
695                 system ('lilypond %s %s' % (lilyopts, texfiles))
696
697         for e in eps:
698                 cmd = r"""tex '\nonstopmode \input %s'; dvips -E -o %s %s""" % \
699                       (e, e + '.eps', e)
700                 system (cmd)
701
702         for g in png:
703                 cmd = r"""gs -sDEVICE=pgm  -dTextAlphaBits=4 -dGraphicsAlphaBits=4  -q -sOutputFile=- -r90 -dNOPAUSE %s -c quit | pnmcrop | pnmtopng > %s"""
704
705                 cmd = cmd % (g + '.eps', g + '.png')
706                 system (cmd)
707
708         if __main__.use_hash:
709                 name = ''
710                 last_name = ''
711                 f = 0
712                 ks = hash_dict.keys ()
713                 ks.sort ()
714                 for i in ks:
715                         name = re.sub ("(.*)-[0-9]+\.[0-9]+\.[0-9]+", "\\1", i)
716                         name = name + '.mix'
717                         if name != last_name:
718                                 if last_name:
719                                         f.close ()
720                                 f = open (name, 'w')
721                                 last_name = name
722                         f.write ("%s:%s\n" % (i, hash_dict[i]))
723
724         
725 def update_file (body, name):
726         same = 0
727         try:
728                 f = open (name)
729                 fs = f.read (-1)
730                 same = (fs == body)
731         except:
732                 pass
733
734         if not same:
735                 f = open (name , 'w')
736                 f.write (body)
737                 f.close ()
738
739         
740         return not same
741
742
743
744 def getopt_args (opts):
745         "Construct arguments (LONG, SHORT) for getopt from  list of options."
746         short = ''
747         long = []
748         for o in opts:
749                 if o[1]:
750                         short = short + o[1]
751                         if o[0]:
752                                 short = short + ':'
753                 if o[2]:
754                         l = o[2]
755                         if o[0]:
756                                 l = l + '='
757                         long.append (l)
758         return (short, long)
759
760 def option_help_str (o):
761         "Transform one option description (4-tuple ) into neatly formatted string"
762         sh = '  '       
763         if o[1]:
764                 sh = '-%s' % o[1]
765
766         sep = ' '
767         if o[1] and o[2]:
768                 sep = ','
769                 
770         long = ''
771         if o[2]:
772                 long= '--%s' % o[2]
773
774         arg = ''
775         if o[0]:
776                 if o[2]:
777                         arg = '='
778                 arg = arg + o[0]
779         return '  ' + sh + sep + long + arg
780
781
782 def options_help_str (opts):
783         "Convert a list of options into a neatly formatted string"
784         w = 0
785         strs =[]
786         helps = []
787
788         for o in opts:
789                 s = option_help_str (o)
790                 strs.append ((s, o[3]))
791                 if len (s) > w:
792                         w = len (s)
793
794         str = ''
795         for s in strs:
796                 str = str + '%s%s%s\n' % (s[0], ' ' * (w - len(s[0])  + 3), s[1])
797         return str
798
799 def help():
800         sys.stdout.write("""Usage: mudela-book [options] FILE\n
801 Generate hybrid LaTeX input from Latex + mudela
802 Options:
803 """)
804         sys.stdout.write (options_help_str (option_definitions))
805         sys.stdout.write (r"""Warning all output is written in the CURRENT directory
806
807
808
809 Report bugs to bug-gnu-music@gnu.org.
810
811 Written by Tom Cato Amundsen <tomcato@xoommail.com> and
812 Han-Wen Nienhuys <hanwen@cs.uu.nl>
813 """)
814
815         sys.exit (0)
816
817
818 def write_deps (fn, target):
819         sys.stdout.write('writing `%s\'\n' % fn)
820
821         f = open (fn, 'w')
822         
823         f.write ('%s%s: ' % (dep_prefix, target))
824         for d in __main__.read_files:
825                 f.write ('%s ' %  d)
826         f.write ('\n')
827         f.close ()
828         __main__.read_files = []
829
830 def identify():
831         sys.stdout.write ('mudela-book (GNU LilyPond) %s\n' % program_version)
832
833 def print_version ():
834         identify()
835         sys.stdout.write (r"""Copyright 1998--1999
836 Distributed under terms of the GNU General Public License. It comes with
837 NO WARRANTY.
838 """)
839
840
841 outname = ''
842 try:
843         (sh, long) = getopt_args (__main__.option_definitions)
844         (options, files) = getopt.getopt(sys.argv[1:], sh, long)
845 except getopt.error, msg:
846         sys.stderr.write("error: %s" % msg)
847         sys.exit(1)
848
849 do_deps = 0
850 for opt in options:     
851         o = opt[0]
852         a = opt[1]
853
854         if o == '--include' or o == '-I':
855                 include_path.append (a)
856         elif o == '--version':
857                 print_version ()
858                 sys.exit  (0)
859
860         elif o == '--format' or o == '-o':
861                 __main__.format = a
862         elif o == '--outname' or o == '-o':
863                 if len(files) > 1:
864                         #HACK
865                         sys.stderr.write("Mudela-book is confused by --outname on multiple files")
866                         sys.exit(1)
867                 outname = a
868         elif o == '--help' or o == '-h':
869                 help ()
870         elif o == '--no-lily' or o == '-n':
871                 __main__.run_lilypond = 0
872         elif o == '--dependencies':
873                 do_deps = 1
874         elif o == '--default-mudela-fontsize':
875                 default_music_fontsize = string.atoi (a)
876         elif o == '--init':
877                 initfile =  a
878         elif o == '--dep-prefix':
879                 dep_prefix = a
880         elif o == '--no-pictures':
881                 do_pictures = 0
882
883 identify()
884
885 for input_filename in files:
886         file_settings = {}
887         if outname:
888                 my_outname = outname
889         else:
890                 my_outname = os.path.basename(os.path.splitext(input_filename)[0])
891         my_depname = my_outname + '.dep'                
892
893         chunks = transform_input_file (input_filename, my_outname)
894
895         foutn = my_outname + '.' + format
896         sys.stderr.write ("Writing `%s'\n" % foutn)
897         fout = open (foutn, 'w')
898         for c in chunks:
899                 fout.write (c[1])
900         fout.close ()
901
902         if do_deps:
903                 write_deps (my_depname, foutn)
904
905
906
907 #
908 # Petr, ik zou willen dat ik iets zinvoller deed,
909 # maar wat ik kan ik doen, het verandert toch niets?
910 #   --hwn 20/aug/99