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