]> git.donarmstrong.com Git - lilypond.git/blobdiff - scripts/mudela-book.py
release: 1.1.43
[lilypond.git] / scripts / mudela-book.py
index 6540542663db1564883ed07d3646ab73e67538b8..fd535434c8420386560231d05651a0215cd6efa1 100644 (file)
-
-# python version of mudela-book.  WIP.
-
-#  TODO: center option
-
+#!@PYTHON@
+#
+# The bugs you find are made by Tom Cato Amundsen <tomcato@xoommail.com>
+# Send patches/questions/bugreports to a mailinglist:
+#  gnu-music-discuss@gnu.org
+#  bug-gnu-music@gnu.org
+#  help-gnu-music@gnu.org
+#
+#  TODO:
+# * Spacing before and after mudela blocks should be fixed. No empy lines
+#   before and after the mudela block should make just little space between
+#   music and text, one or more empy lines should make bigger space, like
+#   between paragraphs.
+# * center option (??)
+# * make mudela-book understand usepackage{geometry}
+# * check that linewidth set in \paper is not wider than actual linewidth?
+# * the following fails because mudelabook doesn't care that the
+#   last } after \end{mudela} finishes the marginpar:
+#     \marginpar{
+#     \begin{mudela}
+#        c d e f g
+#     \end{mudela}}
+# * force-verbatim is maybe not that useful since latex fails with footnotes,
+#   marginpars and others
 # log:
-
 # 0.3:
 #   rewrite in Python.
-
+# 0.4:
+#   much rewritten by new author. I think the work has been split more
+#   logical between different classes.
+# 0.4.1:
+#   - cleanup
+#   - speedup, do all mudela parsing at the same time to avoid multiple
+#     guile startup that takes some seconds on my computer
+# 0.4.2:
+#   - fixed default latex fontsize, it should be 10pt not 11pt
+#   - verbatim option no longer visible
+#   - mudela-book produces .dvi output
+#   - change to use castingalgorithm = \Gourlay instead of \Wordwrap. It gives
+#     better result on small linewidths.
+#   - mudela-book-doc.doc rewritten
+# 0.5:
+#   - junked floating and fragment options, added eps option
+#   - mudela-book will scan the mudela code to find out if it has to add
+#     paper-definition and \score{\notes{...}}
+#   - possible to define commands that look like this: \mudela{ c d e }
+#   - don't produce .dvi output, it was a silly idea...
+# 0.5.1:
+#   - removed init/mudela-book-defs.py, \mudela is defined inside mudela-book
+#   - fragment and nonfragment options will override autodetection of type of
+#     in mudela-block (voice contents or complete code)
+# 0.5.2:
+#   - fixed verbatim option behaviour: don't show \begin{mudela} \end{mudela}
+#     and show both mudela code and music
+#   - veryverbatim option, same as verbatim but show \begin{mudela}
+#     and \end{mudela}. (saves keystrokes in mudela-book-doc.doc)
+#   - intertext="bla bla bla" option
+#   - mudela-book now understand latex \begin{verbatim}
+# 0.5.3:
+#   - bf: \mudela{ \times 2/3{...} }
+#        * \t in \times is not tab character and
+#        * dont treat the first '}' as command ending
+# 0.5.4: (Mats B)
+#   - .fly and .sly files in \mudelafile{} are treated as standalone Lilypond.
+#   - Fragments, .fly and .sly files are in \relative mode.
+# 0.5.5: (Mats B)
+#   - bf: Default fragments have linewidth=-1.0
+#   - Added 'singleline' and 'multiline' options.
+# 0.5.6:
+#   - \mudelafile{} set linewith correct, -1 for .sly and texlinewidth for .fly
+#   - changes to Mudela_output
+#   - changed RE to search for pre/postMudelaExample to make it possible to
+#     comment out a definition.
+#   - use sys.stderr and sys.stdout instead of print
 import os
 import string
-import regex
+import re
 import getopt
 import sys
-import regsub
-
-outdir = 'out/'
-program_version = '0.3'
-
-
 
+outdir = 'out'
+initfile = ''
+program_version = '0.5.6'
+include_path = ['.']
+
+out_files = []
+
+fontsize_i2a = {11:'eleven', 13:'thirteen', 16:'sixteen',
+                20:'twenty', 26:'twentysix'}
+fontsize_pt2i = {'11pt':11, '13pt':13, '16pt':16, '20pt':20, '26pt':26}
+
+begin_mudela_re = re.compile ('^ *\\\\begin{mudela}')
+begin_verbatim_re = re.compile ('^ *\\\\begin{verbatim}')
+end_verbatim_re = re.compile ('^ *\\\\end{verbatim}')
+extract_papersize_re = re.compile('\\\\documentclass[\[, ]*(\w*)paper[\w ,]*\]\{\w*\}')
+extract_fontsize_re = re.compile('[ ,\[]*([0-9]*)pt')
+begin_mudela_opts_re = re.compile('\[[^\]]*\]')
+end_mudela_re = re.compile ('^ *\\\\end{mudela}')
+section_re = re.compile ('\\\\section')
+chapter_re = re.compile ('\\\\chapter')
+input_re = re.compile ('^\\\\input{([^}]*)')
+include_re = re.compile ('^\\\\include{([^}]*)')
+begin_document_re = re.compile ('^ *\\\\begin{document}')
+documentclass_re = re.compile('\\\\documentclass')
+twocolumn_re = re.compile('\\\\twocolumn')
+onecolumn_re = re.compile('\\\\onecolumn')
+mudela_file_re = re.compile('\\\\mudelafile{([^}]+)}')
+file_ext_re = re.compile('.+\\.([^.}]+$)')
+preMudelaExample_re = re.compile('^\s*\\\\def\\\\preMudelaExample')
+postMudelaExample_re = re.compile('^\s*\\\\def\\\\postMudelaExample')
+boundingBox_re = re.compile('%%BoundingBox: ([0-9]+) ([0-9]+) ([0-9]+) ([0-9]+)')
+intertext_re = re.compile("intertext=\"([^\"]*)\"")
 
 def file_exist_b(name):
     try: 
@@ -29,256 +119,684 @@ def file_exist_b(name):
     f.close ()
     return 1
 
-class CompileStatus:
-       pass
+def ps_dimention(fname):
+    fd = open(fname)
+    lines = fd.readlines()
+    for line in lines:
+        s = boundingBox_re.search(line)
+        if s:
+            break
+    return (int(s.groups()[2])-int(s.groups()[0]), 
+            int(s.groups()[3])-int(s.groups()[1]))
 
-def file_mtime (name):
-    return os.stat (name)[8] #mod time
 
-def compile (command, infile, outfile):
-       indate = file_mtime (infile)
+def find_file (name):
+    for a in include_path:
        try:
-               outdate = file_mtime (outfile)
-               recompile = indate > outdate
-
-       except os.error:
-               recompile = 1
-
-       if recompile:
-               sys.stderr.write ('invoking `%s\'\n' % command)
-               
-               status = os.system (command)
-               if status:
-                       raise CompileStatus
-
-
-class Mudela_output:
-       def __init__ (self):
-               self.basename = ''
-               self.fragment = 0
-               self.size = 16
-               
-       def open (self, basename):
-               self.basename = basename
-               self.temp_file = "%s/%s" %(outdir, 'mudela-temp.ly')
-               self.file = open (self.temp_file, 'w')
-               self.file.write ('\\include \"paper%d.ly\"\n' % self.size)
-               if self.size == 16:
-                       s = 'sixteen'
-               else:
-                       s = 'twenty'
-
-               self.file.write ('default_paper = \\paper { \\paper_%s\n linewidth = -15.\\cm; }\n' % s)
-               
-               if self.fragment:
-                       self.file.write ('\\score { \\melodic { ')
-
-       def write (self,s):
-               self.file.write (s)
+           nm = os.path.join (a, name)
+           f = open (nm)
+           return nm
+       except IOError:
+           pass
+    return ''
 
-       def close (self):
-               if self.fragment:
-                       self.file.write (
-                               '}\n \\paper { linewidth = -1.0\\cm;\n' +
-                               'castingalgorithm = \\Wordwrap; } }\n')
 
+class CompileStatus:
+    pass
+class SomethingIsSeriouslyBroken:
+    pass
 
-               self.file.close ()
+def file_mtime (name):
+    return os.stat (name)[8] #mod time
 
-               inf=self.basename + '.ly'
-               outf = self.basename + '.tex'           
-               if not file_exist_b (inf):
-                       status = 1
-               else:
-#                      print 'invoking %s' %('diff %s %s' % (self.temp_file, inf))
-                       status = os.system ('diff -q %s %s' % (self.temp_file, inf))
-#                      print 'status %d' % status
+def need_recompile_b(infile, outfile):
+    indate = file_mtime (infile)
+    try:
+        outdate = file_mtime (outfile)
+        return indate > outdate
+    except os.error:
+        return 1
+
+#
+# executes os.system(command) if infile is newer than
+# outfile or outfile don't exist
+#
+def compile (command, workingdir, infile, outfile):
+    "Test if infile is newer than outfile. If so, cd to workingdir"
+    "and execute command"
+    indate = file_mtime (workingdir+infile)
+    try:
+        outdate = file_mtime (workingdir+outfile)
+        recompile = indate > outdate
+
+    except os.error:
+        recompile = 1
+
+    if recompile:
+        sys.stderr.write ('invoking `%s\'\n' % command)
+        if workingdir == '':
+            status = os.system (command)
+        else:
+            status = os.system ('cd %s; %s' %(workingdir, command))
+        if status:
+            raise CompileStatus
+
+class Properties:
+    #
+    # init
+    #
+    def __init__(self):
+        self.__linewidth = {
+            1: {'a4':{10: 345, 11: 360, 12: 390},
+                'a5':{10: 276, 11: 276, 12: 276},
+                'b5':{10: 345, 11: 356, 12: 356},
+                'letter':{10: 345, 11: 360, 12: 390},
+                'legal': {10: 345, 11: 360, 12: 390},
+                'executive':{10: 345, 11: 360, 12: 379}},
+            2: {'a4':{10: 167, 11: 175, 12: 190},
+                'a5':{10: 133, 11: 133, 12: 133},
+                'b5':{10: 167, 11: 173, 12: 173},
+                'letter':{10: 167, 11: 175, 12: 190},
+                'legal':{10: 167, 11: 175, 12: 190},
+                'executive':{10: 167, 11: 175, 12: 184}}}
+        # >0 --> force all mudela to this pt size
+        self.force_mudela_fontsize = 0
+        self.force_verbatim_b = 0
+        self.__data = {
+            'mudela-fontsize' : {'init': 16},
+            'papersize' : {'init': 'a4'},
+            'num-column' : {'init': 1},
+            'tex-fontsize' : {'init': 10}
+            }
+    def clear_for_new_file(self):
+        for var in self.__data.keys():
+            self.__data[var] = {'init': self.__data[var]['init']}
+    def clear_for_new_block(self):
+        for var in self.__data.keys():
+            if self.__data[var].has_key('block'):
+                del self.__data[var]['block']
+    def __get_variable(self, var):
+        if self.__data[var].has_key('block'):
+            return self.__data[var]['block']
+        elif self.__data[var].has_key('file'):
+            return self.__data[var]['file']
+        else:
+            return self.__data[var]['init']
+    def setPapersize(self, size, requester):
+        self.__data['papersize'][requester] = size
+    def getPapersize(self):
+        return self.__get_variable('papersize')
+    def setMudelaFontsize(self, size, requester):
+        self.__data['mudela-fontsize'][requester] = size
+    def getMudelaFontsize(self):
+        if self.force_mudela_fontsize:
+            return self.force_mudela_fontsize
+        return self.__get_variable('mudela-fontsize')
+    def setTexFontsize(self, size, requester):
+        self.__data['tex-fontsize'][requester] = size
+    def getTexFontsize(self):
+        return self.__get_variable('tex-fontsize')
+    def setNumColumn(self, num, requester):
+        self.__data['num-column'][requester] = num
+    def getNumColumn(self):
+        return self.__get_variable('num-column')
+    def getLineWidth(self):
+        return self.__linewidth[self.getNumColumn()][self.getPapersize()][self.getTexFontsize()]
 
-               if status:
-                       os.rename (self.temp_file, inf)
 
-               compile ('lilypond  -o %s %s'%  (self.basename, inf), inf, outf)
-#                      os.rename (self.basename + '.tex', outdir  +)
-               
-       
+class Mudela_output:
+    """ Using only self.code_type to deside both value of linewith and
+    if we have to put code into \score{...} was silly. Now we use:
+    self.code_type:  show us what need to be added.
+                        None : init value
+                        'NOTES' : add \context Voice{ ... }
+                        'CONTEXT' : add \score{ ... }
+                        'COMPLETE' : jupp
+    self.single_line_b:   0 : linewidth=-1  1: linewith=textwidth
+    """
+    def __init__ (self, basename):
+        self.basename = basename
+        self.temp_filename = "%s/%s" %(outdir, 'mudela-temp.ly')
+        self.file = open (self.temp_filename, 'w')
+        self.__lines = []
+        # 'tex' or 'eps'
+        self.graphic_type = 'tex'
+        self.code_type = None
+        self.single_line_b = 1
+        self.optlist = []
+    def write (self, line):
+        # match only if there is nothing but whitespace before \begin.
+        # we should not have to do this RE for every line
+        if re.search('^\s*\\\\begin{mudela}', line):
+            r  = begin_mudela_opts_re.search(line)
+            if r:
+                o = r.group()[1:-1]
+                self.optlist =  re.compile('[\s,]*').split(o)
+            else:
+                self.optlist = []
+        else: # ugh this is NOT bulletproof...
+            if not self.code_type:
+                if re.search('^\s*%', line) or re.search('^\s*$', line):
+                    pass
+                elif re.search('^\s*\\\\context', line):
+                    self.code_type = 'CONTEXT'
+                    self.single_line_b = 0
+                elif re.search('^\s*\\\\score', line) or \
+                     re.search('^\s*\\\\paper', line) or \
+                     re.search('^\s*\\\\header', line) or \
+                     re.search('^\s*\\\\version', line) or \
+                     re.search('^\s*\\\\include', line) or \
+                     re.search('^\s*[A-Za-z]*\s*=', line):
+                    self.code_type = 'COMPLETE'
+                    self.single_line_b = 0
+                else:
+                    self.code_type = 'NOTES'
+                    self.single_line_b = 1
+            self.__lines.append(line)
+    def write_red_tape(self):
+        if 'eps' in self.optlist:
+            self.graphic_type = 'eps'
+            #self.single_line_b = 1
+        if 'fragment' in self.optlist:
+            self.code_type = 'NOTES'
+        if 'nonfragment' in self.optlist:
+            self.code_type = 'COMPLETE'
+        if 'multiline' in self.optlist:
+            self.single_line_b = 0
+        for pt in fontsize_pt2i.keys():
+            if pt in self.optlist:
+                Props.setMudelaFontsize(fontsize_pt2i[pt], 'block')
+        self.file.write ('\\include \"paper%d.ly\"\n' \
+                         % Props.getMudelaFontsize())
+                         
+        s = fontsize_i2a[Props.getMudelaFontsize()]
+        if 'singleline' in self.optlist:
+            self.single_line_b = 1
+        if self.single_line_b:
+            linewidth_str = 'linewidth = -1.\cm;'
+        else:
+            linewidth_str = 'linewidth = %i.\\pt;' % Props.getLineWidth()
+        self.file.write("\\paper {"
+                        + "\\paper_%s " % s
+                        + linewidth_str
+                        + "castingalgorithm = \Gourlay; \n}")
+                        #+ "castingalgorithm = \Wordwrap; indent = 2.\cm; \n}")
+        if self.code_type == 'CONTEXT':
+            self.file.write('\\score{\n\\notes\\relative c{')
+        if self.code_type == 'NOTES' :
+            self.file.write('\\score{\n\\notes\\relative c{\\context Voice{')
+    def close (self):
+        self.write_red_tape()
+        for l in self.__lines:
+            self.file.write(l)
+        if self.code_type == 'CONTEXT':
+            self.file.write('}}')
+        elif self.code_type == 'NOTES':
+            self.file.write('}}}')
+        self.file.close()
+
+        inf = outdir + self.basename + '.ly'
+        outf = outdir + self.basename + '.tex'
+        if not os.path.isfile(inf):
+            status = 1
+        else:
+            status = os.system ('diff -q %s %s' % (self.temp_filename, inf))
+        if status:
+            os.rename (self.temp_filename, inf)
+
+       recompile_b =  need_recompile_b(inf, outf)
+       if recompile_b:
+            out_files.append((self.graphic_type, inf))
+       return recompile_b
+
+    def insert_me_string(self):
+        "ugh the name of this function is wrong"
+        if self.graphic_type == 'tex':
+            return ['tex', self.basename]
+        elif self.graphic_type == 'eps':
+            return ['eps', self.basename]
+        else:
+            raise SomethingIsSeriouslyBroken
 
 class Tex_output:
-       def __init__ (self, name):
-               self.output_fn = '%s/%s' % (outdir, name)
-               self.mudela = 0
-               self.file = open (self.output_fn , 'w')
-               self.verbatim = 0               
-       def open_mudela (self, basename):
-               self.mudela_basename =basename
-               if self.verbatim:
-                       self.file.write ('\\begin{verbatim}\n')
-               self.mudela = 1
-
-       def write (self, s):
-               self.file.write (s)
-                       
-       def write_mudela (self, s):
-               if self.verbatim:
-                       self.file.write (s)
-                       
-       def close_mudela (self):
-               if self.verbatim:
-                       self.file.write ('\\end{verbatim}\n')
-                       self.verbatim = 0
-               
-               self.file.write (
-                   '\\preMudelaExample\\input %s\n\postMudelaExample' %(self.mudela_basename))
-               self.mudela = 0
-
-
-
-
-
-begin_mudela_re = regex.compile ('^ *\\\\begin{mudela}')
-begin_mudela_opts_re = regex.compile ('^ *\\\\begin{mudela}\[\(.*\)\]')
-end_mudela_re = regex.compile ('^ *\\\\end{mudela}')
-section_re = regex.compile ('\\\\section')
-chapter_re = regex.compile ('\\\\chapter')
-input_re = regex.compile ('^\\\\input[ \t\n]+\\(.*\\)$')
+    def __init__ (self, name):
+        self.output_fn = '%s/%s' % (outdir, name)
+        self.__lines = []
+    def open_verbatim (self, line, level):
+        self.__lines.append('\\begin{verbatim}\n')
+        if level == 2:
+            s = re.sub('veryverbatim[\s,]*', '', line)
+            s = re.sub('intertext=\"([^\"]*)\"[\s,]*', '', s)
+            s = re.sub(',\s*\]', ']', s)
+            s = re.sub('\[\]', '', s)
+            self.__lines.append(s);
+    def close_verbatim (self):
+        self.__lines.append('\\end{verbatim}\n')
+    def write (self, s):
+        self.__lines.append(s)
+    def create_graphics(self):
+        s = ''
+        g_vec = []
+        for line in self.__lines:
+            if type(line)==type([]):
+                g_vec.append(line)
+        for g in g_vec:
+            if need_recompile_b(outdir+g[1]+'.ly', outdir+g[1]+'.tex'):
+                    s = s + ' ' + g[1]+'.ly'
+        if s != '':
+            e = os.system('cd %s; lilypond %s' %(outdir, s))
+            if e:
+                sys.stderr.write("error: lilypond exited with value %i\n" % e)
+                sys.exit(e)
+        for g in g_vec:
+            if g[0] == 'eps':
+                compile('tex %s' % g[1]+'.tex', outdir, g[1]+'.tex', g[1]+'.dvi')
+                compile('dvips -E -o %s %s' %(g[1]+'.eps', g[1]+'.dvi'), outdir,
+                        g[1]+'.dvi', g[1]+'.eps')
+    def write_outfile(self):
+        file = open(self.output_fn+'.latex', 'w')
+        file.write('% Created by mudela-book\n')
+        for line in self.__lines:
+            if type(line)==type([]):
+                if line[0] == 'tex':
+                    #\\def\\interscoreline{}
+                    file.write('\\preMudelaExample \\input %s \\postMudelaExample\n'\
+                              # TeX applies the prefix of the main source automatically.
+                               % (line[1]+'.tex'))
+#                               % (outdir+line[1]+'.tex'))
+                if line[0] == 'eps':
+                    ps_dim = ps_dimention(outdir+line[1]+'.eps')
+                    file.write('\\noindent\\parbox{%ipt}{\includegraphics{%s}}\n' \
+                               % (ps_dim[0], line[1]+'.eps'))
+#                               % (ps_dim[0], outdir+line[1]+'.eps'))
+            else:
+                file.write(line)
+        file.close()
+
+# given parameter s="\mudela[some options]{CODE} some text and commands"
+# it returns a tuple:
+#    (CODE, integer)
+# where the last number is the index of the ending '}'
+def extract_command(s):
+    start_found_b = 0
+    count = 0
+    start = 0
+    for idx in range(len(s)):
+        if s[idx] == '{':
+            if not start_found_b:
+                start = idx
+                start_found_b = 1
+            count = count + 1
+        if s[idx] == '}':
+            count = count - 1
+        if (start_found_b == 1) and (count == 0):
+            break
+    return s[start+1:idx], idx
 
 class Tex_input:
-       def __init__ (self,name):
-               if regex.search ('\\.[^/\\\\]+',name) == -1:
-                       name = name + '.tex'
-               print 'opening %s' % name
-               self.filename = name
-               self.infile = open (name)
-               
-       def get_lines (self):
-               lines = self.infile.readlines ()
-               (retlines, retdeps) = ([],[self.filename])
-               for line in lines:
-                       if input_re.search (line) <> -1:
-                               t = Tex_input (input_re.group (1))
-                               ls =t.get_lines ()
-                               retlines = retlines + ls[0]
-                               retdeps = retdeps + ls[1]
-                       else:
-                               retlines.append (line)
-
-               return (retlines, retdeps)
+    def __init__ (self, filename):
+        for fn in [filename, filename+'.tex', filename+'.doc']:
+            try:
+                self.infile = open (fn)
+                self.filename = fn
+                return
+            except:
+                continue
+        raise IOError
+
+    def get_lines (self):
+        lines = self.infile.readlines ()
+        (retlines, retdeps) = ([],[self.filename])
+        for line in lines:
+            r_inp = input_re.search (line)
+            r_inc = include_re.search (line)
+
+            # Filename rules for \input :
+            # input: no .tex ext
+            # 1. will search for file with exact that name (tex-input.my will be found)
+            # 2. will search for file with .tex ext, (tex-input.my
+            #    will find tex-input.my.tex)
+            # input: with .tex ext
+            # 1. will find exact match
+            
+            # Filename rules for \include :
+            # 1. will only find files with name given to \include + .tex ext
+            if r_inp:
+                try:
+                    t = Tex_input (r_inp.groups()[0])
+                    ls = t.get_lines ()
+                    retlines = retlines + ls[0]
+                    retdeps = retdeps + ls[1]
+                except:
+                    sys.stderr.write("warning: can't find %s, let's hope latex will\n" % r_inp.groups()[0])
+                    retlines.append (line)
+            elif r_inc:
+                try:
+                    t = Tex_input (r_inc.groups()[0]+'.tex')
+                    ls =t.get_lines ()
+                    ls[0].insert(0, '\\newpage\n')
+                    ls[0].append('\\newpage\n')
+                    retlines = retlines + ls[0]
+                    retdeps = retdeps + ls[1]
+                except:
+                    sys.stderr.write("warning: can't find %s, let's hope latex will" % r_inc.groups()[0])
+                    retlines.append (line)
+            else:
+                # This code should be rewritten, it looks terrible
+                r_mud = defined_mudela_cmd_re.search(line)
+                if r_mud:
+                   # TODO document this
+                    ss = "\\\\verb(?P<xx>[^a-zA-Z])\s*\\\\%s\s*(?P=xx)" \
+                         % re.escape(r_mud.group()[1:])
+                    # just append the line if the command is inside \verb|..|
+                    if re.search(ss, line):
+                        retlines.append(line)
+                        continue
+                    while 1:
+                        opts = r_mud.groups()[2]
+                        cmd_start_idx = r_mud.span()[0]
+                        if cmd_start_idx > 0:
+                            retlines.append(line[:cmd_start_idx])
+                            
+                        cmd_data, rest_idx = extract_command(line[cmd_start_idx:])
+                        rest_idx = rest_idx + cmd_start_idx + 1
+                        if opts == None:
+                            opts = ''
+                        else:
+                            opts = ', '+opts
+                        
+                        v = string.split(defined_mudela_cmd[r_mud.groups()[0]], '\n')
+                        for l in v[1:-1]:
+                            l = string.replace(l, '\\fontoptions', opts)
+                            l = string.replace(l, '\\maininput', cmd_data)
+                            retlines.append(l)
+                        r_mud = defined_mudela_cmd_re.search(line[rest_idx:])
+                        if not r_mud:
+                            rs = line[rest_idx:]
+                            while rs[0] == " ":
+                                rs = rs[1:]
+                            if rs != "\n":
+                                retlines.append(line[rest_idx:])
+                            break;
+                        line = line[rest_idx:]
+                else:
+                    retlines.append (line)
+        return (retlines, retdeps)
+
 
 class Main_tex_input(Tex_input):
-       def __init__ (self, name, outname):
-               
-               Tex_input.__init__ (self, name) # ugh
-
-               self.outname = outname
-               self.chapter = 0
-               self.section = 0
-               self.fine_count =0
-               self.mudtex = Tex_output (self.outname)
-               self.mudela = None
-               self.deps = []
-       def set_sections (self, l):
-               if section_re.search (l) <> -1:
-                       self.section = self.section + 1
-               if chapter_re.search (l) <> -1:
-                       self.section = 0
-                       self.chapter = self.chapter + 1
-
-                       
-       def gulp_mudela (self):
-               pass
-
-       def gen_basename (self):
-               return '%s/%s-%d.%d.%d' % (outdir, self.outname,self.chapter,self.section,self.fine_count)
-
-       def do_it(self):
-               (lines, self.deps) = self.get_lines ()
-               for line in lines:
-                       if begin_mudela_re.search (line) <> -1:
-                               if begin_mudela_opts_re.search (line) <> -1:
-                                       opts = begin_mudela_opts_re.group (1)
-                               else:
-                                       opts = ''
-                               optlist = string.split (opts, ',')
-                               self.mudela = Mudela_output ()
-                               if 'fragment' in optlist:
-                                       self.mudela.fragment = 1
-                               if '16pt' in optlist:
-                                       self.mudela.size = 16
-                               if '20pt' in optlist:
-                                       self.mudela.size = 20
-                               
-                               if 'verbatim' in optlist:
-                                       self.mudtex.verbatim = 1
-
-                               b = self.gen_basename ()
-                               self.mudtex.open_mudela (b)
-                               self.mudela.open (b)
-
-
-                               continue
-                       elif end_mudela_re.search (line) <> -1:
-                               self.mudela.close ()
-                               self.mudtex.close_mudela ()
-                               self.mudela = None
-                               self.fine_count = self.fine_count + 1
-                               continue
-                           
-                       if self.mudela:
-                               self.mudela.write (line)
-                               self.mudtex.write_mudela (line)
-                       else:
-                               self.mudtex.write (line)
-                       self.set_sections(line)
-               del self.mudtex
+    def __init__ (self, name, outname):
+
+        Tex_input.__init__ (self, name) # ugh
+        self.outname = outname
+        self.chapter = 0
+        self.section = 0
+        self.fine_count =0
+        self.mudtex = Tex_output (self.outname)
+        self.mudela = None
+        self.deps = []
+        self.verbatim = 0
+        # set to 'mudela' when we are processing mudela code,
+        # both verbatim and graphic-to-be
+        self.mode = 'latex'
+    def set_sections (self, l):
+        if section_re.search (l):
+            self.section = self.section + 1
+        if chapter_re.search (l):
+            self.section = 0
+            self.chapter = self.chapter + 1
+
+    def gen_basename (self):
+        return '%s-%d.%d.%d' % (self.outname, self.chapter,
+                                self.section, self.fine_count)
+    def extract_papersize_from_documentclass(self, line):
+        pre = extract_papersize_re.search(line)
+        if not pre:
+            return None
+        return pre.groups()[0]
+    def extract_fontsize_from_documentclass(self, line):
+        r = extract_fontsize_re.search(line)
+        if r:
+            return int(r.groups()[0])
+    def do_it(self):
+        preMudelaDef = postMudelaDef = 0
+        (lines, self.deps) = self.get_lines ()
+        #HACK
+        latex_verbatim = 0
+        for line in lines:
+            if documentclass_re.search (line):
+                p = self.extract_papersize_from_documentclass (line)
+                if p:
+                    Props.setPapersize(p, 'file')
+                f = self.extract_fontsize_from_documentclass (line)
+                if f:
+                    Props.setTexFontsize (f, 'file')
+            elif twocolumn_re.search (line):
+                Props.setNumColumn (2, 'file')
+            elif onecolumn_re.search (line):
+                Props.setNumColumn (1, 'file')
+            elif preMudelaExample_re.search (line):
+                preMudelaDef = 1
+            elif postMudelaExample_re.search (line):
+                postMudelaDef = 1
+            elif begin_verbatim_re.search (line):
+                latex_verbatim = 1
+            elif end_verbatim_re.search (line):
+                latex_verbatim = 0
+            elif begin_document_re.search (line):
+                if not preMudelaDef:
+                    self.mudtex.write ('\\def\\preMudelaExample{}\n')
+                if not postMudelaDef:
+                    self.mudtex.write ('\\def\\postMudelaExample{}\n')
+
+            elif mudela_file_re.search(line):
+               r = mudela_file_re.search(line)
+
+               self.mudela = Mudela_output(self.gen_basename())
+               fn = r.group (1)
+               full_path = find_file (fn)
+               if not full_path:
+                   sys.stderr.write("error: can't find file '%s'\n" % fn)
+                   sys.exit (1)
+
+               f = open (full_path, 'r')
+               lines =f.readlines ()
+               for x in lines:
+                   self.mudela.write (x)
+               r = file_ext_re.search(fn)
+                if r:
+                    if r.group(1) == 'fly':
+                        self.mudela.optlist.append('multiline')
+               stat =self.mudela.close ()
+               if stat:
+                       sys.stdout.write("(File %s needs recompiling)\n" % full_path)
+                self.mudtex.write (self.mudela.insert_me_string())
+               self.deps.append (full_path)
+               del self.mudela
+                self.mudela = None
+                self.fine_count = self.fine_count + 1
+               continue
+            elif begin_mudela_re.search (line) and not latex_verbatim:
+                Props.clear_for_new_block()
+                if __debug__:
+                    if self.mode == 'mudela':
+                        raise AssertionError
+                self.mode = 'mudela'
+                r  = begin_mudela_opts_re.search (line)
+                if r:
+                    o = r.group()[1:][:-1]
+                    optlist =  re.compile('[ ,]*').split(o)
+                    m = intertext_re.search(r.group())
+                    if m:
+                        self.intertext = m.groups()[0]
+                    else:
+                        self.intertext = None
+                else:
+                    optlist = []
+                if ('veryverbatim' in optlist):
+                    self.verbatim = 2
+                elif ('verbatim' in optlist) or (Props.force_verbatim_b):
+                    self.verbatim = 1
+                else:
+                    self.verbatim = 0
+                if self.verbatim:
+                    self.mudtex.open_verbatim (line, self.verbatim)
+                self.mudela = Mudela_output (self.gen_basename ())
+                self.mudela.write (line)
+                continue
+            elif end_mudela_re.search (line) and not latex_verbatim:
+                if __debug__:
+                    if self.mode != 'mudela':
+                        raise AssertionError
+
+                if self.verbatim:
+                    if self.verbatim == 2:
+                        self.mudtex.write (line)
+                    self.mudtex.close_verbatim ()
+                self.mudela.close ()
+                if self.verbatim and self.intertext:
+                    self.mudtex.write(self.intertext)
+                self.mudtex.write (self.mudela.insert_me_string())
+                del self.mudela
+                self.mudela = None
+                self.fine_count = self.fine_count + 1
+                self.mode = 'latex'
+                continue
+
+
+            if self.mode == 'mudela':
+                self.mudela.write (line)
+                if self.verbatim:
+                    self.mudtex.write (line)
+            else:
+                self.mudtex.write (line)
+                self.set_sections(line)
+        self.mudtex.create_graphics()
+        self.mudtex.write_outfile()
+        del self.mudtex
                
 
 def help():
-    sys.stdout.write("Usage: mudela-book [options] FILE\n"
-                + "Generate hybrid LaTeX input from Latex + mudela"
-                + "Options:\n"
-                + "  -h, --help             print this help\n"
-                + "  -d, --outdir=DIR       prefix directory\n" 
-                + "  -o, --outname=FILE     prefix for filenames\n"
+    sys.stdout.write("""Usage: mudela-book [options] FILE\n
+Generate hybrid LaTeX input from Latex + mudela
+Options:\n
+  -h, --help                     print this help
+  -d, --outdir=DIR               directory to put generated files
+  -o, --outname=FILE             prefix for filenames
+  --default-mudela-fontsize=??pt default fontsize for music
+  --force-mudela-fontsize=??pt   force fontsize for all inline mudela
+  --force-verbatim               make all mudela verbatim\n
+  --dependencies                 write dependencies
+  --include                      include path
+  --init                         mudela-book initfile
+  """
                     )
     sys.exit (0)
 
 
-
-sys.stderr.write ('This is %s version %s\n' % ('mudela-book', program_version))
-
-outname = ''
-(options, files) = getopt.getopt(
-       sys.argv[1:], 'hd:o:', [ 'outdir=', 'outname=', 'help', 'dependencies'])
-
-do_deps = 0
-for opt in options:
-       o = opt[0]
-       a = opt[1]
-       if o == '--outname' or o == '-o':
-               outname = a
-       if o == '--outdir' or o == '-d':
-               outdir = a
-       if o == '--help' or o == '-h':
-               help ()
-       if o == '--dependencies':
-               do_deps = 1
-
 def write_deps (fn, out,  deps):
-       f = open (outdir + fn, 'w')
-       f.write ('%s: %s\n'% (outdir + out + '.dvi', reduce (lambda x,y: x + ' '+ y, deps)))
+       out_fn = os.path.join (outdir, fn)
+       
+       sys.stdout.write('writing `%s\'\n' % out_fn)
+       
+       f = open (out_fn, 'w')
+       target = re.sub (os.sep + os.sep, os.sep, os.path.join (outdir, out + '.latex'))
+       f.write ('%s: %s\n'% (target,
+                             reduce (lambda x,y: x + ' '+ y, deps)))
        f.close ()
 
-
-for f in files:
-       my_outname = outname
-       if not my_outname:
-               my_outname = regsub.sub ('\\(.*\\)\\.doc', '\\1', f)
-
-       my_depname = my_outname + '.dep'
-
-       inp = Main_tex_input (f, my_outname)
-       inp.do_it ()
-
-       if do_deps:
-               write_deps (my_depname, my_outname, inp.deps)
-
-
+def identify():
+    sys.stderr.write ('This is %s version %s\n' % ('mudela-book', program_version))
+
+def main():
+    global outdir, initfile, defined_mudela_cmd, defined_mudela_cmd_re
+    outname = ''
+    try:
+        (options, files) = getopt.getopt(
+            sys.argv[1:], 'hd:o:I:', ['outdir=', 'outname=',
+                                    'default-mudela-fontsize=',
+                                    'force-mudela-fontsize=',
+                                    'help', 'dependencies', 'include=',
+                                    'force-verbatim', 'init='])
+    except getopt.error, msg:
+        sys.stderr.write("error: %s" % msg)
+        sys.exit(1)
+        
+    do_deps = 0
+    for opt in options:    
+       o = opt[0]
+       a = opt[1]
+       if o == '--include' or o == '-I':
+           include_path.append (a)
+       elif o == '--outname' or o == '-o':
+            if len(files) > 1:
+                #HACK
+                sys.stderr.write("Mudela-book is confused by --outname on multiple files")
+                sys.exit(1)
+            outname = a
+        elif o == '--outdir' or o == '-d':
+            outdir = a
+        elif o == '--help' or o == '-h':
+            help ()
+       elif o == '--dependencies':
+            do_deps = 1
+        elif o == '--default-mudela-fontsize':
+            if not fontsize_pt2i.has_key(a):
+                sys.stderr.write("Error: illegal fontsize: %s" % a)
+                sys.stderr.write("  accepted fontsizes are: 11pt, 13pt, 16pt, 20pt, 26pt")
+                sys.exit()
+            Props.setMudelaFontsize(fontsize_pt2i[a], 'init')
+       elif o == '--force-mudela-fontsize':
+            if not fontsize_pt2i.has_key(a):
+                sys.stderr.write("Error: illegal fontsize: %s" % a)
+                sys.stderr.write("  accepted fontsizes are: 11pt, 13pt, 16pt, 20pt, 26pt")
+                sys.exit()
+            Props.force_mudela_fontsize = fontsize_pt2i[a]
+        elif o == '--force-verbatim':
+            Props.force_verbatim_b = 1
+        elif o == '--init':
+            initfile =  a
+    if outdir[-1:] != '/':
+        outdir = outdir + '/'
+
+    # r""" ... """ means: leave escape seqs alone.
+    defined_mudela_cmd = {'mudela': r"""
+\begin{mudela}[eps, singleline \fontoptions]
+  \context Staff <
+    \context Voice{
+      \maininput
+    }
+  >
+\end{mudela}
+"""}
+    if initfile != '':
+        f = open(initfile)
+        s = f.read()
+        f.close()
+        d = eval(s)
+        for i in d.keys():
+            defined_mudela_cmd[i] = d[i]
+        del d
+
+    c = string.join (defined_mudela_cmd.keys(), '|')
+
+    defined_mudela_cmd_re = re.compile("\\\\(%s)(\[(\d*pt)\])*{([^}]*)}" %c)
+
+    if not os.path.isdir(outdir):
+        os.system('mkdir %s' % outdir)
+
+    for input_filename in files:
+        Props.clear_for_new_file()
+        if outname:
+            my_outname = outname
+        else:
+            my_outname = os.path.basename(os.path.splitext(input_filename)[0])
+        my_depname = my_outname + '.dep'        
+        inp = Main_tex_input (input_filename, my_outname)
+        inp.do_it ()
+        if do_deps:
+            write_deps (my_depname, my_outname, inp.deps)
+
+identify()
+Props = Properties()
+main()