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