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