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