]> git.donarmstrong.com Git - lilypond.git/blob - scripts/mudela-book.py
release: 1.1.44
[lilypond.git] / scripts / mudela-book.py
1 #!@PYTHON@
2 #
3 # The bugs you find are made by Tom Cato Amundsen <tomcato@xoommail.com>
4 # Send patches/questions/bugreports to a mailinglist:
5 #  gnu-music-discuss@gnu.org
6 #  bug-gnu-music@gnu.org
7 #  help-gnu-music@gnu.org
8 #
9 #  TODO:
10 # * center option (??)
11 # * make mudela-book understand usepackage{geometry}
12 # * check that linewidth set in \paper is not wider than actual linewidth?
13 # * the following fails because mudelabook doesn't care that the
14 #   last } after \end{mudela} finishes the marginpar:
15 #     \marginpar{
16 #     \begin{mudela}
17 #        c d e f g
18 #     \end{mudela}}
19 # * force-verbatim is maybe not that useful since latex fails with footnotes,
20 #   marginpars and others
21 # log:
22 # 0.3:
23 #   rewrite in Python.
24 # 0.4:
25 #   much rewritten by new author. I think the work has been split more
26 #   logical between different classes.
27 # 0.4.1:
28 #   - cleanup
29 #   - speedup, do all mudela parsing at the same time to avoid multiple
30 #     guile startup that takes some seconds on my computer
31 # 0.4.2:
32 #   - fixed default latex fontsize, it should be 10pt not 11pt
33 #   - verbatim option no longer visible
34 #   - mudela-book produces .dvi output
35 #   - change to use castingalgorithm = \Gourlay instead of \Wordwrap. It gives
36 #     better result on small linewidths.
37 #   - mudela-book-doc.doc rewritten
38 # 0.5:
39 #   - junked floating and fragment options, added eps option
40 #   - mudela-book will scan the mudela code to find out if it has to add
41 #     paper-definition and \score{\notes{...}}
42 #   - possible to define commands that look like this: \mudela{ c d e }
43 #   - don't produce .dvi output, it was a silly idea...
44 # 0.5.1:
45 #   - removed init/mudela-book-defs.py, \mudela is defined inside mudela-book
46 #   - fragment and nonfragment options will override autodetection of type of
47 #     in mudela-block (voice contents or complete code)
48 # 0.5.2:
49 #   - fixed verbatim option behaviour: don't show \begin{mudela} \end{mudela}
50 #     and show both mudela code and music
51 #   - veryverbatim option, same as verbatim but show \begin{mudela}
52 #     and \end{mudela}. (saves keystrokes in mudela-book-doc.doc)
53 #   - intertext="bla bla bla" option
54 #   - mudela-book now understand latex \begin{verbatim}
55 # 0.5.3:
56 #   - bf: \mudela{ \times 2/3{...} }
57 #        * \t in \times is not tab character and
58 #        * dont treat the first '}' as command ending
59 # 0.5.4: (Mats B)
60 #   - .fly and .sly files in \mudelafile{} are treated as standalone Lilypond.
61 #   - Fragments, .fly and .sly files are in \relative mode.
62 # 0.5.5: (Mats B)
63 #   - bf: Default fragments have linewidth=-1.0
64 #   - Added 'singleline' and 'multiline' options.
65 # 0.5.6:
66 #   - \mudelafile{} set linewith correct, -1 for .sly and texlinewidth for .fly
67 #   - changes to Mudela_output
68 #   - changed RE to search for pre/postMudelaExample to make it possible to
69 #     comment out a definition.
70 #   - use sys.stderr and sys.stdout instead of print
71 import os
72 import string
73 import re
74 import getopt
75 import sys
76
77 outdir = 'out'
78 initfile = ''
79 program_version = '0.5.6'
80 include_path = ['.']
81
82 out_files = []
83
84 fontsize_i2a = {11:'eleven', 13:'thirteen', 16:'sixteen',
85                 20:'twenty', 26:'twentysix'}
86 fontsize_pt2i = {'11pt':11, '13pt':13, '16pt':16, '20pt':20, '26pt':26}
87
88 begin_mudela_re = re.compile ('^ *\\\\begin{mudela}')
89 begin_verbatim_re = re.compile ('^ *\\\\begin{verbatim}')
90 end_verbatim_re = re.compile ('^ *\\\\end{verbatim}')
91 extract_papersize_re = re.compile('\\\\documentclass[\[, ]*(\w*)paper[\w ,]*\]\{\w*\}')
92 extract_fontsize_re = re.compile('[ ,\[]*([0-9]*)pt')
93 begin_mudela_opts_re = re.compile('\[[^\]]*\]')
94 end_mudela_re = re.compile ('^ *\\\\end{mudela}')
95 section_re = re.compile ('\\\\section')
96 chapter_re = re.compile ('\\\\chapter')
97 input_re = re.compile ('^\\\\input{([^}]*)')
98 include_re = re.compile ('^\\\\include{([^}]*)')
99 begin_document_re = re.compile ('^ *\\\\begin{document}')
100 documentclass_re = re.compile('\\\\documentclass')
101 twocolumn_re = re.compile('\\\\twocolumn')
102 onecolumn_re = re.compile('\\\\onecolumn')
103 mudela_file_re = re.compile('\\\\mudelafile{([^}]+)}')
104 file_ext_re = re.compile('.+\\.([^.}]+$)')
105 preMudelaExample_re = re.compile('^\s*\\\\def\\\\preMudelaExample')
106 postMudelaExample_re = re.compile('^\s*\\\\def\\\\postMudelaExample')
107 boundingBox_re = re.compile('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)')
108 intertext_re = re.compile("intertext=\"([^\"]*)\"")
109
110 def file_exist_b(name):
111     try: 
112         f = open(name)
113     except IOError:
114         return 0
115     f.close ()
116     return 1
117
118 def ps_dimention(fname):
119     fd = open(fname)
120     lines = fd.readlines()
121     for line in lines:
122         s = boundingBox_re.search(line)
123         if s:
124             break
125     return (int(s.groups()[2])-int(s.groups()[0]), 
126             int(s.groups()[3])-int(s.groups()[1]))
127
128
129 def find_file (name):
130     for a in include_path:
131         try:
132             nm = os.path.join (a, name)
133             f = open (nm)
134             return nm
135         except IOError:
136             pass
137     return ''
138
139
140 class CompileStatus:
141     pass
142 class SomethingIsSeriouslyBroken:
143     pass
144
145 def file_mtime (name):
146     return os.stat (name)[8] #mod time
147
148 def need_recompile_b(infile, outfile):
149     indate = file_mtime (infile)
150     try:
151         outdate = file_mtime (outfile)
152         return indate > outdate
153     except os.error:
154         return 1
155
156 #
157 # executes os.system(command) if infile is newer than
158 # outfile or outfile don't exist
159 #
160 def compile (command, workingdir, infile, outfile):
161     "Test if infile is newer than outfile. If so, cd to workingdir"
162     "and execute command"
163     indate = file_mtime (workingdir+infile)
164     try:
165         outdate = file_mtime (workingdir+outfile)
166         recompile = indate > outdate
167
168     except os.error:
169         recompile = 1
170
171     if recompile:
172         sys.stderr.write ('invoking `%s\'\n' % command)
173         if workingdir == '':
174             status = os.system (command)
175         else:
176             status = os.system ('cd %s; %s' %(workingdir, command))
177         if status:
178             raise CompileStatus
179
180 class Properties:
181     #
182     # init
183     #
184     def __init__(self):
185         self.__linewidth = {
186             1: {'a4':{10: 345, 11: 360, 12: 390},
187                 'a5':{10: 276, 11: 276, 12: 276},
188                 'b5':{10: 345, 11: 356, 12: 356},
189                 'letter':{10: 345, 11: 360, 12: 390},
190                 'legal': {10: 345, 11: 360, 12: 390},
191                 'executive':{10: 345, 11: 360, 12: 379}},
192             2: {'a4':{10: 167, 11: 175, 12: 190},
193                 'a5':{10: 133, 11: 133, 12: 133},
194                 'b5':{10: 167, 11: 173, 12: 173},
195                 'letter':{10: 167, 11: 175, 12: 190},
196                 'legal':{10: 167, 11: 175, 12: 190},
197                 'executive':{10: 167, 11: 175, 12: 184}}}
198         # >0 --> force all mudela to this pt size
199         self.force_mudela_fontsize = 0
200         self.force_verbatim_b = 0
201         self.__data = {
202             'mudela-fontsize' : {'init': 16},
203             'papersize' : {'init': 'a4'},
204             'num-column' : {'init': 1},
205             'tex-fontsize' : {'init': 10}
206             }
207     def clear_for_new_file(self):
208         for var in self.__data.keys():
209             self.__data[var] = {'init': self.__data[var]['init']}
210     def clear_for_new_block(self):
211         for var in self.__data.keys():
212             if self.__data[var].has_key('block'):
213                 del self.__data[var]['block']
214     def __get_variable(self, var):
215         if self.__data[var].has_key('block'):
216             return self.__data[var]['block']
217         elif self.__data[var].has_key('file'):
218             return self.__data[var]['file']
219         else:
220             return self.__data[var]['init']
221     def setPapersize(self, size, requester):
222         self.__data['papersize'][requester] = size
223     def getPapersize(self):
224         return self.__get_variable('papersize')
225     def setMudelaFontsize(self, size, requester):
226         self.__data['mudela-fontsize'][requester] = size
227     def getMudelaFontsize(self):
228         if self.force_mudela_fontsize:
229             return self.force_mudela_fontsize
230         return self.__get_variable('mudela-fontsize')
231     def setTexFontsize(self, size, requester):
232         self.__data['tex-fontsize'][requester] = size
233     def getTexFontsize(self):
234         return self.__get_variable('tex-fontsize')
235     def setNumColumn(self, num, requester):
236         self.__data['num-column'][requester] = num
237     def getNumColumn(self):
238         return self.__get_variable('num-column')
239     def getLineWidth(self):
240         return self.__linewidth[self.getNumColumn()][self.getPapersize()][self.getTexFontsize()]
241
242
243 class Mudela_output:
244     """ Using only self.code_type to deside both value of linewith and
245     if we have to put code into \score{...} was silly. Now we use:
246     self.code_type:  show us what need to be added.
247                         None : init value
248                         'NOTES' : add \context Voice{ ... }
249                         'CONTEXT' : add \score{ ... }
250                         'COMPLETE' : jupp
251     self.single_line_b:   0 : linewidth=-1  1: linewith=textwidth
252     """
253     def __init__ (self, basename):
254         self.basename = basename
255         self.temp_filename = "%s/%s" %(outdir, 'mudela-temp.ly')
256         self.file = open (self.temp_filename, 'w')
257         self.__lines = []
258         # 'tex' or 'eps'
259         self.graphic_type = 'tex'
260         self.code_type = None
261         self.single_line_b = 1
262         self.optlist = []
263     def write (self, line):
264         # match only if there is nothing but whitespace before \begin.
265         # we should not have to do this RE for every line
266         if re.search('^\s*\\\\begin{mudela}', line):
267             r  = begin_mudela_opts_re.search(line)
268             if r:
269                 o = r.group()[1:-1]
270                 self.optlist =  re.compile('[\s,]*').split(o)
271             else:
272                 self.optlist = []
273         else: # ugh this is NOT bulletproof...
274             if not self.code_type:
275                 if re.search('^\s*%', line) or re.search('^\s*$', line):
276                     pass
277                 elif re.search('^\s*\\\\context', line):
278                     self.code_type = 'CONTEXT'
279                     self.single_line_b = 0
280                 elif re.search('^\s*\\\\score', line) or \
281                      re.search('^\s*\\\\paper', line) or \
282                      re.search('^\s*\\\\header', line) or \
283                      re.search('^\s*\\\\version', line) or \
284                      re.search('^\s*\\\\include', line) or \
285                      re.search('^\s*[A-Za-z]*\s*=', line):
286                     self.code_type = 'COMPLETE'
287                     self.single_line_b = 0
288                 else:
289                     self.code_type = 'NOTES'
290                     self.single_line_b = 1
291             self.__lines.append(line)
292     def write_red_tape(self):
293         if 'eps' in self.optlist:
294             self.graphic_type = 'eps'
295             #self.single_line_b = 1
296         if 'fragment' in self.optlist:
297             self.code_type = 'NOTES'
298         if 'nonfragment' in self.optlist:
299             self.code_type = 'COMPLETE'
300         if 'multiline' in self.optlist:
301             self.single_line_b = 0
302         for pt in fontsize_pt2i.keys():
303             if pt in self.optlist:
304                 Props.setMudelaFontsize(fontsize_pt2i[pt], 'block')
305         self.file.write ('\\include \"paper%d.ly\"\n' \
306                          % Props.getMudelaFontsize())
307                          
308         s = fontsize_i2a[Props.getMudelaFontsize()]
309         if 'singleline' in self.optlist:
310             self.single_line_b = 1
311         if self.single_line_b:
312             linewidth_str = 'linewidth = -1.\cm;'
313         else:
314             linewidth_str = 'linewidth = %i.\\pt;' % Props.getLineWidth()
315         self.file.write("\\paper {"
316                         + "\\paper_%s " % s
317                         + linewidth_str
318                         + "castingalgorithm = \Gourlay; \n}")
319                         #+ "castingalgorithm = \Wordwrap; indent = 2.\cm; \n}")
320         if self.code_type == 'CONTEXT':
321             self.file.write('\\score{\n\\notes\\relative c{')
322         if self.code_type == 'NOTES' :
323             self.file.write('\\score{\n\\notes\\relative c{\\context Voice{')
324     def close (self):
325         self.write_red_tape()
326         for l in self.__lines:
327             self.file.write(l)
328         if self.code_type == 'CONTEXT':
329             self.file.write('}}')
330         elif self.code_type == 'NOTES':
331             self.file.write('}}}')
332         self.file.close()
333
334         inf = outdir + self.basename + '.ly'
335         outf = outdir + self.basename + '.tex'
336         if not os.path.isfile(inf):
337             status = 1
338         else:
339             status = os.system ('diff -q %s %s' % (self.temp_filename, inf))
340         if status:
341             os.rename (self.temp_filename, inf)
342
343         recompile_b =  need_recompile_b(inf, outf)
344         if recompile_b:
345             out_files.append((self.graphic_type, inf))
346         return recompile_b
347
348     def insert_me_string(self):
349         "ugh the name of this function is wrong"
350         if self.graphic_type == 'tex':
351             return ['tex', self.basename]
352         elif self.graphic_type == 'eps':
353             return ['eps', self.basename]
354         else:
355             raise SomethingIsSeriouslyBroken
356
357 class Tex_output:
358     def __init__ (self, name):
359         self.output_fn = '%s/%s' % (outdir, name)
360         self.__lines = []
361     def open_verbatim (self, line, level):
362         self.__lines.append('\\begin{verbatim}\n')
363         if level == 2:
364             s = re.sub('veryverbatim[\s,]*', '', line)
365             s = re.sub('intertext=\"([^\"]*)\"[\s,]*', '', s)
366             s = re.sub(',\s*\]', ']', s)
367             s = re.sub('\[\]', '', s)
368             self.__lines.append(s);
369     def close_verbatim (self):
370         self.__lines.append('\\end{verbatim}\n')
371     def write (self, s):
372         self.__lines.append(s)
373     def create_graphics(self):
374         s = ''
375         g_vec = []
376         for line in self.__lines:
377             if type(line)==type([]):
378                 g_vec.append(line)
379         for g in g_vec:
380             if need_recompile_b(outdir+g[1]+'.ly', outdir+g[1]+'.tex'):
381                     s = s + ' ' + g[1]+'.ly'
382         if s != '':
383             e = os.system('cd %s; lilypond %s' %(outdir, s))
384             if e:
385                 sys.stderr.write("error: lilypond exited with value %i\n" % e)
386                 sys.exit(e)
387         for g in g_vec:
388             if g[0] == 'eps':
389                 compile('tex %s' % g[1]+'.tex', outdir, g[1]+'.tex', g[1]+'.dvi')
390                 compile('dvips -E -o %s %s' %(g[1]+'.eps', g[1]+'.dvi'), outdir,
391                         g[1]+'.dvi', g[1]+'.eps')
392     def write_outfile(self):
393         file = open(self.output_fn+'.latex', 'w')
394         file.write('% Created by mudela-book\n')
395         last_line = None
396         for line in self.__lines:
397             if type(line)==type([]):
398                 if last_line == '\n':
399                     file.write(r'\vspace{0.5cm}')
400                 if line[0] == 'tex':                    
401                     file.write('\\preMudelaExample \\input %s \\postMudelaExample\n'\
402                                % (line[1]+'.tex'))
403                 if line[0] == 'eps':
404                     ps_dim = ps_dimention(outdir+line[1]+'.eps')
405                     file.write('\\noindent\\parbox{%ipt}{\includegraphics{%s}}\n' \
406                                % (ps_dim[0], line[1]+'.eps'))
407             else:
408                 file.write(line)
409             if type(last_line)==type([]):
410                 if line=='\n':
411                     file.write(r'\vspace{0.5cm}')
412             last_line = line
413         file.close()
414
415 # given parameter s="\mudela[some options]{CODE} some text and commands"
416 # it returns a tuple:
417 #    (CODE, integer)
418 # where the last number is the index of the ending '}'
419 def extract_command(s):
420     start_found_b = 0
421     count = 0
422     start = 0
423     for idx in range(len(s)):
424         if s[idx] == '{':
425             if not start_found_b:
426                 start = idx
427                 start_found_b = 1
428             count = count + 1
429         if s[idx] == '}':
430             count = count - 1
431         if (start_found_b == 1) and (count == 0):
432             break
433     return s[start+1:idx], idx
434
435 class Tex_input:
436     def __init__ (self, filename):
437         for fn in [filename, filename+'.tex', filename+'.doc']:
438             try:
439                 self.infile = open (fn)
440                 self.filename = fn
441                 return
442             except:
443                 continue
444         raise IOError
445
446     def get_lines (self):
447         lines = self.infile.readlines ()
448         (retlines, retdeps) = ([],[self.filename])
449         for line in lines:
450             r_inp = input_re.search (line)
451             r_inc = include_re.search (line)
452
453             # Filename rules for \input :
454             # input: no .tex ext
455             # 1. will search for file with exact that name (tex-input.my will be found)
456             # 2. will search for file with .tex ext, (tex-input.my
457             #    will find tex-input.my.tex)
458             # input: with .tex ext
459             # 1. will find exact match
460             
461             # Filename rules for \include :
462             # 1. will only find files with name given to \include + .tex ext
463             if r_inp:
464                 try:
465                     t = Tex_input (r_inp.groups()[0])
466                     ls = t.get_lines ()
467                     retlines = retlines + ls[0]
468                     retdeps = retdeps + ls[1]
469                 except:
470                     sys.stderr.write("warning: can't find %s, let's hope latex will\n" % r_inp.groups()[0])
471                     retlines.append (line)
472             elif r_inc:
473                 try:
474                     t = Tex_input (r_inc.groups()[0]+'.tex')
475                     ls =t.get_lines ()
476                     ls[0].insert(0, '\\newpage\n')
477                     ls[0].append('\\newpage\n')
478                     retlines = retlines + ls[0]
479                     retdeps = retdeps + ls[1]
480                 except:
481                     sys.stderr.write("warning: can't find %s, let's hope latex will" % r_inc.groups()[0])
482                     retlines.append (line)
483             else:
484                 # This code should be rewritten, it looks terrible
485                 r_mud = defined_mudela_cmd_re.search(line)
486                 if r_mud:
487                     # TODO document this
488                     ss = "\\\\verb(?P<xx>[^a-zA-Z])\s*\\\\%s\s*(?P=xx)" \
489                          % re.escape(r_mud.group()[1:])
490                     # just append the line if the command is inside \verb|..|
491                     if re.search(ss, line):
492                         retlines.append(line)
493                         continue
494                     while 1:
495                         opts = r_mud.groups()[2]
496                         cmd_start_idx = r_mud.span()[0]
497                         if cmd_start_idx > 0:
498                             retlines.append(line[:cmd_start_idx])
499                             
500                         cmd_data, rest_idx = extract_command(line[cmd_start_idx:])
501                         rest_idx = rest_idx + cmd_start_idx + 1
502                         if opts == None:
503                             opts = ''
504                         else:
505                             opts = ', '+opts
506                         
507                         v = string.split(defined_mudela_cmd[r_mud.groups()[0]], '\n')
508                         for l in v[1:-1]:
509                             l = string.replace(l, '\\fontoptions', opts)
510                             l = string.replace(l, '\\maininput', cmd_data)
511                             retlines.append(l)
512                         r_mud = defined_mudela_cmd_re.search(line[rest_idx:])
513                         if not r_mud:
514                             rs = line[rest_idx:]
515                             while rs[0] == " ":
516                                 rs = rs[1:]
517                             if rs != "\n":
518                                 retlines.append(line[rest_idx:])
519                             break;
520                         line = line[rest_idx:]
521                 else:
522                     retlines.append (line)
523         return (retlines, retdeps)
524
525
526 class Main_tex_input(Tex_input):
527     def __init__ (self, name, outname):
528
529         Tex_input.__init__ (self, name) # ugh
530         self.outname = outname
531         self.chapter = 0
532         self.section = 0
533         self.fine_count =0
534         self.mudtex = Tex_output (self.outname)
535         self.mudela = None
536         self.deps = []
537         self.verbatim = 0
538         # set to 'mudela' when we are processing mudela code,
539         # both verbatim and graphic-to-be
540         self.mode = 'latex'
541     def set_sections (self, l):
542         if section_re.search (l):
543             self.section = self.section + 1
544         if chapter_re.search (l):
545             self.section = 0
546             self.chapter = self.chapter + 1
547
548     def gen_basename (self):
549         return '%s-%d.%d.%d' % (self.outname, self.chapter,
550                                 self.section, self.fine_count)
551     def extract_papersize_from_documentclass(self, line):
552         pre = extract_papersize_re.search(line)
553         if not pre:
554             return None
555         return pre.groups()[0]
556     def extract_fontsize_from_documentclass(self, line):
557         r = extract_fontsize_re.search(line)
558         if r:
559             return int(r.groups()[0])
560     def do_it(self):
561         preMudelaDef = postMudelaDef = 0
562         (lines, self.deps) = self.get_lines ()
563         #HACK
564         latex_verbatim = 0
565         for line in lines:
566             if documentclass_re.search (line):
567                 p = self.extract_papersize_from_documentclass (line)
568                 if p:
569                     Props.setPapersize(p, 'file')
570                 f = self.extract_fontsize_from_documentclass (line)
571                 if f:
572                     Props.setTexFontsize (f, 'file')
573             elif twocolumn_re.search (line):
574                 Props.setNumColumn (2, 'file')
575             elif onecolumn_re.search (line):
576                 Props.setNumColumn (1, 'file')
577             elif preMudelaExample_re.search (line):
578                 preMudelaDef = 1
579             elif postMudelaExample_re.search (line):
580                 postMudelaDef = 1
581             elif begin_verbatim_re.search (line):
582                 latex_verbatim = 1
583             elif end_verbatim_re.search (line):
584                 latex_verbatim = 0
585             elif begin_document_re.search (line):
586                 if not preMudelaDef:
587                     self.mudtex.write ('\\def\\preMudelaExample{}\n')
588                 if not postMudelaDef:
589                     self.mudtex.write ('\\def\\postMudelaExample{}\n')
590
591             elif mudela_file_re.search(line):
592                 r = mudela_file_re.search(line)
593
594                 self.mudela = Mudela_output(self.gen_basename())
595                 fn = r.group (1)
596                 full_path = find_file (fn)
597                 if not full_path:
598                     sys.stderr.write("error: can't find file '%s'\n" % fn)
599                     sys.exit (1)
600
601                 f = open (full_path, 'r')
602                 lines =f.readlines ()
603                 for x in lines:
604                     self.mudela.write (x)
605                 r = file_ext_re.search(fn)
606                 if r:
607                     if r.group(1) == 'fly':
608                         self.mudela.optlist.append('multiline')
609                 stat =self.mudela.close ()
610                 if stat:
611                         sys.stdout.write("(File %s needs recompiling)\n" % full_path)
612                 self.mudtex.write (self.mudela.insert_me_string())
613                 self.deps.append (full_path)
614                 del self.mudela
615                 self.mudela = None
616                 self.fine_count = self.fine_count + 1
617                 continue
618             elif begin_mudela_re.search (line) and not latex_verbatim:
619                 Props.clear_for_new_block()
620                 if __debug__:
621                     if self.mode == 'mudela':
622                         raise AssertionError
623                 self.mode = 'mudela'
624                 r  = begin_mudela_opts_re.search (line)
625                 if r:
626                     o = r.group()[1:][:-1]
627                     optlist =  re.compile('[ ,]*').split(o)
628                     m = intertext_re.search(r.group())
629                     if m:
630                         self.intertext = m.groups()[0]
631                     else:
632                         self.intertext = None
633                 else:
634                     optlist = []
635                 if ('veryverbatim' in optlist):
636                     self.verbatim = 2
637                 elif ('verbatim' in optlist) or (Props.force_verbatim_b):
638                     self.verbatim = 1
639                 else:
640                     self.verbatim = 0
641                 if self.verbatim:
642                     self.mudtex.open_verbatim (line, self.verbatim)
643                 self.mudela = Mudela_output (self.gen_basename ())
644                 self.mudela.write (line)
645                 continue
646             elif end_mudela_re.search (line) and not latex_verbatim:
647                 if __debug__:
648                     if self.mode != 'mudela':
649                         raise AssertionError
650
651                 if self.verbatim:
652                     if self.verbatim == 2:
653                         self.mudtex.write (line)
654                     self.mudtex.close_verbatim ()
655                 self.mudela.close ()
656                 if self.verbatim and self.intertext:
657                     self.mudtex.write(self.intertext)
658                 self.mudtex.write (self.mudela.insert_me_string())
659                 del self.mudela
660                 self.mudela = None
661                 self.fine_count = self.fine_count + 1
662                 self.mode = 'latex'
663                 continue
664
665
666             if self.mode == 'mudela':
667                 self.mudela.write (line)
668                 if self.verbatim:
669                     self.mudtex.write (line)
670             else:
671                 self.mudtex.write (line)
672                 self.set_sections(line)
673         self.mudtex.create_graphics()
674         self.mudtex.write_outfile()
675         del self.mudtex
676                 
677
678 def help():
679     sys.stdout.write("""Usage: mudela-book [options] FILE\n
680 Generate hybrid LaTeX input from Latex + mudela
681 Options:\n
682   -h, --help                     print this help
683   -d, --outdir=DIR               directory to put generated files
684   -o, --outname=FILE             prefix for filenames
685   --default-mudela-fontsize=??pt default fontsize for music
686   --force-mudela-fontsize=??pt   force fontsize for all inline mudela
687   --force-verbatim               make all mudela verbatim\n
688   --dependencies                 write dependencies
689   --include                      include path
690   --init                         mudela-book initfile
691   """
692                      )
693     sys.exit (0)
694
695
696 def write_deps (fn, out,  deps):
697         out_fn = os.path.join (outdir, fn)
698         
699         sys.stdout.write('writing `%s\'\n' % out_fn)
700         
701         f = open (out_fn, 'w')
702         target = re.sub (os.sep + os.sep, os.sep, os.path.join (outdir, out + '.latex'))
703         f.write ('%s: %s\n'% (target,
704                               reduce (lambda x,y: x + ' '+ y, deps)))
705         f.close ()
706
707 def identify():
708     sys.stderr.write ('This is %s version %s\n' % ('mudela-book', program_version))
709
710 def main():
711     global outdir, initfile, defined_mudela_cmd, defined_mudela_cmd_re
712     outname = ''
713     try:
714         (options, files) = getopt.getopt(
715             sys.argv[1:], 'hd:o:I:', ['outdir=', 'outname=',
716                                     'default-mudela-fontsize=',
717                                     'force-mudela-fontsize=',
718                                     'help', 'dependencies', 'include=',
719                                     'force-verbatim', 'init='])
720     except getopt.error, msg:
721         sys.stderr.write("error: %s" % msg)
722         sys.exit(1)
723         
724     do_deps = 0
725     for opt in options:    
726         o = opt[0]
727         a = opt[1]
728         if o == '--include' or o == '-I':
729             include_path.append (a)
730         elif o == '--outname' or o == '-o':
731             if len(files) > 1:
732                 #HACK
733                 sys.stderr.write("Mudela-book is confused by --outname on multiple files")
734                 sys.exit(1)
735             outname = a
736         elif o == '--outdir' or o == '-d':
737             outdir = a
738         elif o == '--help' or o == '-h':
739             help ()
740         elif o == '--dependencies':
741             do_deps = 1
742         elif o == '--default-mudela-fontsize':
743             if not fontsize_pt2i.has_key(a):
744                 sys.stderr.write("Error: illegal fontsize: %s" % a)
745                 sys.stderr.write("  accepted fontsizes are: 11pt, 13pt, 16pt, 20pt, 26pt")
746                 sys.exit()
747             Props.setMudelaFontsize(fontsize_pt2i[a], 'init')
748         elif o == '--force-mudela-fontsize':
749             if not fontsize_pt2i.has_key(a):
750                 sys.stderr.write("Error: illegal fontsize: %s" % a)
751                 sys.stderr.write("  accepted fontsizes are: 11pt, 13pt, 16pt, 20pt, 26pt")
752                 sys.exit()
753             Props.force_mudela_fontsize = fontsize_pt2i[a]
754         elif o == '--force-verbatim':
755             Props.force_verbatim_b = 1
756         elif o == '--init':
757             initfile =  a
758     if outdir[-1:] != '/':
759         outdir = outdir + '/'
760
761     # r""" ... """ means: leave escape seqs alone.
762     defined_mudela_cmd = {'mudela': r"""
763 \begin{mudela}[eps, singleline \fontoptions]
764   \context Staff <
765     \context Voice{
766       \maininput
767     }
768   >
769 \end{mudela}
770 """}
771     if initfile != '':
772         f = open(initfile)
773         s = f.read()
774         f.close()
775         d = eval(s)
776         for i in d.keys():
777             defined_mudela_cmd[i] = d[i]
778         del d
779
780     c = string.join (defined_mudela_cmd.keys(), '|')
781
782     defined_mudela_cmd_re = re.compile("\\\\(%s)(\[(\d*pt)\])*{([^}]*)}" %c)
783
784     if not os.path.isdir(outdir):
785         os.system('mkdir %s' % outdir)
786
787     for input_filename in files:
788         Props.clear_for_new_file()
789         if outname:
790             my_outname = outname
791         else:
792             my_outname = os.path.basename(os.path.splitext(input_filename)[0])
793         my_depname = my_outname + '.dep'        
794         inp = Main_tex_input (input_filename, my_outname)
795         inp.do_it ()
796         if do_deps:
797             write_deps (my_depname, my_outname, inp.deps)
798
799 identify()
800 Props = Properties()
801 main()