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