]> git.donarmstrong.com Git - lilypond.git/blob - scripts/ly2dvi.py
release: 1.2.6
[lilypond.git] / scripts / ly2dvi.py
1 #!@PYTHON@
2
3 """
4 =======================================================================
5 LilyPond to dvi converter
6
7 Features include Title information, paper size specification, and image
8 orientation.  
9
10 Usage: ly2dvi.py [OPTION]... [FILE]...
11 Input: LilyPond source or LilyPond generated TeX files
12 Output: DVI file
13 =======================================================================
14 """
15
16 name = 'ly2dvi'
17 version = '@TOPLEVEL_VERSION@'
18 errorlog = ''
19
20 import sys
21 import os
22 import getopt
23 import re
24 import string
25 import time
26 import glob
27 import tempfile
28
29
30 class Input:
31     """
32     This class handles all ly2dvi.py input file methods
33     
34     Public methods:
35     
36     __init__()  Constructor
37     open(file)  Open a .ly file or .tex file using lilyinclude path
38     close()     Close current file
39     type()      Determine file type .ly (input) or .tex (output)
40     setVars()   Set title definitions found in .tex (output) file
41     """
42
43     #
44     # Constructors
45     #
46
47     def __init__(this):
48        this.__fd = None 
49
50     #
51     # open
52     #
53     def open(this,file):
54         """
55         open file and set private class variable __fd.  The search
56         sequence is: current directory followed by the directories
57         found in include property list.  Each directory is searched
58         for file, file.ly, file.sly and file.fly.
59         
60         input:  file   filename
61         output: void
62         error:  ExitNotFound Exception
63         """
64
65         for i in [''] + Props.get('include')[0:]:
66             ifile = os.path.join(i,file)
67             for j in ['','.ly','.fly', '.sly']:
68                 jfile = ifile+j
69                 try:
70                     this.__fd = open( jfile, 'r' )
71                     return
72                 except:
73                     pass
74         sys.exit('ExitNotFound', file)
75
76
77     #
78     # close
79     #
80     def close(this):
81         """
82         close file object __fd
83         
84         input:  void
85         output: void
86         error:  None
87         """
88         this.__fd.close()
89
90
91     #
92     # type
93     #
94     def type(this):
95         """
96         Determine input file type.  LilyPond source is 'input' type
97         and LilyPond generated TeX file is 'output' type
98
99         input:  void
100         output: 'input' | 'output'
101         error:  None
102         """
103
104         firstline = this.__fd.readline()
105         this.__fd.seek(0)
106         if  re.match('%created by: GNU LilyPond [0-9]+[.0-9]+',firstline ):
107             return 'output'
108         else:
109             return 'source'
110
111
112     #
113     # setVars
114     #
115     def setVars(this):  
116         """
117         Search for properties in the current input file and set the
118         appropriate values.  The supported properties names are in
119         local variable varTable along with the property list
120         titledefs.
121
122         input:  void
123         output: None
124         error:  None
125         """
126
127         varTable = [
128             #   regexp              set method
129             #   ------              ----------
130             ( 'language',        Props.setLanguage ),
131             ( 'latexheaders',    Props.setHeader ),
132             ( 'orientation',     Props.setOrientation ),
133             ( 'paperpapersize',  Props.setPaperZize ),
134             ( 'papertextheight', Props.setTextHeight ),
135             ( 'paperlinewidth',  Props.setLineWidth ),
136             ( 'filename',        Props.setFilename ),
137             ]
138
139         titles={}
140         for line in this.__fd.readlines():
141             m=re.match('\\\\def\\\\mudela([\w]+){(.*)}',line)
142             if m:
143                 for var in varTable:
144                     if m.group(1) == var[0]:
145                         var[1](m.group(2),'file')
146                         break
147                 for var in Props.get('titledefs'):
148                     if m.group(1) == var:
149                         titles[var]=m.group(2)
150                         break
151         Props.setTitles(titles,'file')
152         this.__fd.seek(0)
153
154 \f
155
156 class TeXOutput:
157     """
158     This class handles all ly2dvi.py output file methods
159
160     private methods:
161      __mudelaDefs(opt)  Send title info to output file
162
163     Public methods:
164     __init__()  Constructor
165     write(str)  Write a string to output file 
166     start(file) Start the latex file
167     next()      Process next output file
168     end()       Finish latex file and run latex 
169     """
170
171     #
172     # constructor
173     #
174     def __init__(this):
175        this.__fd = None 
176        this.__base = ''
177        this.__outfile = ''
178
179     #
180     # __medelaDefs
181     #
182     def __mudelaDefs(this,opt):
183         """
184         Write titles to output
185
186         input:  opt   Supports header and subheader output
187         output: None
188         error:  None
189         """
190
191         titles = Props.get('titles')
192         for key in titles.keys():
193             this.write('%s\\mudela%s{%s}%%\n' % (opt,key,titles[key]))
194
195     #
196     # write
197     #
198     def write(this,str):
199         """
200         Write str to current output file
201
202         input:  str  String to write
203         output: None
204         error:  None
205         """
206         
207         this.__fd.write(str)
208
209     #
210     # start
211     #
212     def start(this,file):
213         """
214         Start LaTeX file.  Calculates the horizontal and vertical
215         margin using pagewidth, pageheight, linewidth, and textheight.
216         Creates temporary output filename and opens it for write.
217         Sends the LaTeX header information to output.  Lastly sends
218         the title information to output.
219
220         input:  file  output file name 
221         output: None
222         error:  None
223         """
224
225         now=time.asctime(time.localtime(time.time()))
226         linewidth = Props.get('linewidth')
227         textheight = Props.get('textheight')
228
229         if Props.get('orientation') == 'landscape':
230             pagewidth = Props.get('pageheight')
231             pageheight = Props.get('pagewidth')
232         else:
233             pageheight = Props.get('pageheight')
234             pagewidth = Props.get('pagewidth')
235                                  
236         horizontalMarginArg =  ( (pagewidth - linewidth)/2 )   
237         verticalMarginArg =  ( (pageheight - textheight)/2  )
238
239         top="""\
240 %% Creator: %s
241 %% Automatically generated from  %s, %s
242
243 \\documentclass[%s]{article}
244
245 %s 
246 \\usepackage{geometry}
247 \\usepackage[latin1]{inputenc} 
248 %%\\usepackage[T1]{fontenc} 
249 %s 
250 %%\\addtolength{\\oddsidemargin}{-1cm} 
251 %%\\addtolength{\\topmargin}{-1cm} 
252 %%\\setlength{\\textwidth}{%s} 
253 %%\\setlength{\\textheight}{%s} 
254 \\geometry{width=%spt, left=%spt, height=%spt, top=%spt} 
255 \\input lilyponddefs 
256 \\input titledefs 
257 %s 
258 \\begin{document}
259 """ % ( program_id(), Props.get('filename'), now, Props.get('papersize'),
260         Props.get('language'), Props.get('pagenumber'), linewidth, textheight,
261         linewidth, horizontalMarginArg, textheight, verticalMarginArg,
262         Props.get('header') )
263         
264         base, ext = os.path.splitext(file)
265         this.__base = base
266         tempfile.template= base + '_ly'
267         this.__outfile = tempfile.mktemp(ext)
268         base, ext = os.path.splitext(this.__outfile)
269         this.__tmpbase = base
270         try:
271             this.__fd = open(this.__outfile,"w")
272         except:
273             sys.exit('ExitNoWrite', this.__outfile)
274         this.write(top)
275         this.__mudelaDefs('')
276         this.write("""\
277 \\makelilytitle
278 """) 
279
280     #
281     # next
282     #
283     def next(this):
284         """
285         Write LaTeX subheader information to support more than one
286         score in a document.  Lastly send current title information to
287         output.
288
289         input:  None
290         output: None
291         error:  None
292         """
293
294         this.write("""\
295 \\def\\theopus{}%
296 \\def\\thepiece{}%
297 \\def\\mudelaopus{}%
298 \\def\\mudelapiece{}%
299 """)
300         this.__mudelaDefs("\\def")
301         this.write("""\
302 \\def\\theopus{\\mudelaopus}% ugh
303 \\def\\thepiece{\\mudelapiece}%
304 \\makelilypiecetitle
305 """)
306
307
308     #
309     # end
310     #
311     def end(this):
312         """
313         Close output file and run latex on it.
314
315         input:  None
316         output: None
317         error:  ExitBadLatex Exception
318         """
319
320         outfile=this.__base + '.dvi'
321         if Props.get('output') != '':
322             outfile = os.path.join(Props.get('output'), outfile )
323             
324         this.write("""\
325 \\vfill\\hfill{\\mudelatagline}
326 \\end{document}
327 """)
328         this.__fd.close()
329         if os.path.isfile(outfile):
330             os.remove(outfile)
331         if ( os.name == 'posix' ):
332             stat = os.system('latex \'\\nonstopmode \\input %s\'' %
333                              (this.__outfile))
334         else: # Windows shells don't eat the single quotes
335             stat = os.system('latex \\nonstopmode \\input %s' %
336                              (this.__outfile))
337         if stat:
338             sys.exit('ExitBadLatex')
339         if not os.path.isfile(outfile):
340                 os.rename(this.__tmpbase + '.dvi', outfile)
341                 
342         sys.stderr.write('\n' + program_id() + ': dvi file name is %s\n\n'
343                          % (outfile))
344
345         if Props.get('postscript'):
346             psoutfile=this.__base + '.ps'
347             if Props.get('output') != '':
348                 psoutfile = os.path.join(Props.get('output'), psoutfile )
349             stat = os.system('dvips -o %s %s' % (psoutfile,outfile))
350             if stat:
351                 sys.exit('ExitBadPostscript')
352             
353
354 \f
355
356
357 # ARG! THIS CODE IS BLOATED:
358 # FIXME: Junk all set/get methods.
359
360 class Properties:
361     """
362     This class handles all ly2dvi.py property manipulation
363
364     Public methods:
365     
366     __init__()  Constructor
367     set<property> methods
368     """
369
370     def __init__(this):
371
372         #
373         # Following is the order of priority for property assignment.  The
374         # list is organized from lowest to highest priority.  Each
375         # assignment is overridden by the next requester in the list.
376         #
377         # Requester     Description
378         # ---------     -----------
379         # init          Initial default values
380         # file          The values found in the lilypond generated TeX files
381         # environment   Envrionment variables LILYINCLUDE, LILYPONDPREFIX
382         # rcfile        $LILYPONDPREFIX/.lilyrc
383         # rcfile        $HOME/.lilyrc
384         # rcfile        ./.lilyrc
385         # commandline   command line arguments
386         # 
387         this.__overrideTable = {
388             'init'        : 0,
389             'file'        : 1,
390             'environment' : 2,
391             'rcfile'      : 3,
392             'commandline' : 4,
393             'program'     : 5
394             }
395
396         this.__roverrideTable = {} # reverse lookup used for debug
397         for i in this.__overrideTable.items():
398             this.__roverrideTable[i[1]]=i[0]
399         
400         this.__data = {
401             'pagewidth'    :  [597, this.__overrideTable['init']],
402             'pageheight'   :  [845, this.__overrideTable['init']],
403             'papersize'    :  ['a4paper', this.__overrideTable['init']],
404             'textheight'   :  [0, this.__overrideTable['init']],
405             'linewidth'    :  [500, this.__overrideTable['init']],
406             'orientation'  :  ['portrait', this.__overrideTable['init']],
407             'language'     :  ['%', this.__overrideTable['init']],
408             'include'      :  [[], this.__overrideTable['init']],
409             'debug'        :  [0, this.__overrideTable['init']],
410             'keeplilypond' :  [0, this.__overrideTable['init']],
411             'keeply2dvi'   :  [0, this.__overrideTable['init']],
412             'pagenumber'   :  ['%', this.__overrideTable['init']],
413             'separate'     :  [0, this.__overrideTable['init']],
414             'output'       :  ['', this.__overrideTable['init']],
415             'header'       :  ['%', this.__overrideTable['init']],
416             'dependencies' :  [0, this.__overrideTable['init']],
417             'root'         :  ['', this.__overrideTable['init']],
418             'tmp'          :  ['d:\tmp', this.__overrideTable['init']],
419             'filename'     :  ['', this.__overrideTable['init']],
420             'titledefs'    :  [[], this.__overrideTable['init']],
421             'titles'       :  [{}, this.__overrideTable['init']],
422             'lilyOutputFiles' :  [[], this.__overrideTable['init']],
423             'postscript'   :  [0, this.__overrideTable['init']],
424             }
425
426         #
427         # Try to set root and HOME first before calling rcfile
428         #
429         if os.environ.has_key('LILYPONDPREFIX'):
430             this.setRoot(os.environ['LILYPONDPREFIX'], 'environment')
431         else:
432             p=os.path.split(sys.argv[0])
433             p=os.path.split(p[0])
434             # bit silly. for ly2dvi, overrules compiled-in datadir...
435             # how to do this better (without running lily, of course?
436             this.setRoot(os.path.join(p[0],'share','lilypond'), 'init')
437
438         if not os.environ.has_key('HOME'):
439             if os.environ.has_key('HOMEDRIVE') and \
440                  os.environ.has_key('HOMEPATH'):
441                 os.environ['HOME'] = os.environ['HOMEDRIVE'] + \
442                                      os.environ['HOMEPATH']
443             else:
444                 os.environ['HOME'] = os.curdir
445
446         this.rcfile() # Read initialization file(s)
447
448         if os.environ.has_key('LILYINCLUDE'):
449             tmp=this.get('include')
450             for s in string.split(os.environ['LILYINCLUDE'],os.pathsep):
451                 tmp.append(s)
452             this.__set('include', tmp, 'environment')    
453
454
455         t= os.pathsep
456         if os.environ.has_key ('TEXINPUTS'):
457                 t = os.environ['TEXINPUTS'] + os.pathsep
458         os.environ['TEXINPUTS'] = t + \
459         os.path.join(this.get('root'), 'tex' ) + \
460         os.pathsep + os.path.join(this.get('root'), 'ps' )
461
462         t=''
463         if os.environ.has_key ('MFINPUTS'):
464                t = os.environ['MFINPUTS'] 
465         os.environ['MFINPUTS'] = os.pathsep + t + \
466                                  os.path.join(this.get('root'), 'mf')
467
468         if os.environ.has_key('TMP'):
469             this.__set('tmp',os.environ['TMP'],'environment')
470
471         
472         fd=this.get_texfile_path ('titledefs.tex')
473         mudefs=[]    
474
475         for line in fd.readlines():
476             m=re.match('\\\\newcommand\*{\\\\mudela([\w]+)}',line)
477             if m:
478                 mudefs.append(m.group(1))
479         fd.close
480         this.__set('titledefs', mudefs, 'init')
481
482     #
483     # __set
484     #
485     def __set(this,var,value,requester):
486         """
487         All of the set methods call this to set a property.  If the value
488         was last set by a requestor of lesser priority the new value is
489         assigned, else the old value has priority and is unchanged.
490         """
491
492         if this.__overrideTable[requester] < this.__data[var][1]:
493             return 0
494         else:
495             this.__data[var] = [value, this.__overrideTable[requester]]
496
497     #
498     # get
499     #
500     def get(this,var):
501         """
502         All of the get methods call this to get a property value.  List
503         variable types are return by value to facilitate an append operation.
504         """
505
506         if var == 'include' or var == 'lilyOutputFiles':
507             return this.__data[var][0][0:]  # return a copy not a ref
508         else:
509             return this.__data[var][0]
510
511     #
512     # get_texfile_path
513     #
514     def get_texfile_path (this, var):
515         """
516         locate and open titledefs.tex file
517         """
518
519         if os.name == 'nt':
520             path = os.path.join(this.get('root'), 'tex', var)
521         else:
522             path =''
523             cmd =('kpsewhich tex %s %s' % (var,errorlog))
524             pipe = os.popen (cmd, 'r')
525             path = pipe.readline ()[:-1] # chop off \n
526             return_status =  pipe.close()
527             if return_status and not path:
528                 path = os.path.join(this.get('root'), 'tex', var)
529         fd = open(path, 'r')
530         return fd
531
532
533     #
534     # Read rc file
535     #
536     def rcfile(this):
537         """
538         Read initialization file(s)
539         """
540         varTable = [
541             #   name              set method
542             #   ----              ----------
543             ( 'DEBUG',          this.setDebug ),
544             ( 'DEPENDENCIES',   this.setDependencies ),
545             ( 'KEEPLILYPOND',   this.setKeeplilypond ),
546             ( 'KEEPLY2DVI',     this.setKeeply2dvi ),
547             ( 'LANGUAGE',       this.setLanguage ),
548             ( 'LATEXHF',        this.setHeader ),
549             ( 'LILYINCLUDE',    this.setInclude ),
550             ( 'LILYPONDPREFIX', this.setRoot ),
551             ( 'NONUMBER',       this.setNonumber ),
552             ( 'ORIENTATION',    this.setOrientation ),
553             ( 'OUTPUTDIR',      this.setOutput ),
554             ( 'PAPERSIZE',      this.setPaperZize ),
555             ( 'PHEIGHT',        this.setTextHeight ),
556             ( 'POSTSCRIPT',     this.setPostscript ),
557             ( 'PWIDTH',         this.setLineWidth ),
558             ( 'SEPARATE',       this.setSeparate ),
559             ( 'TMP',            this.setTmp ),
560             ]
561
562         if ( os.name == 'posix' ):
563             dotFilename='.lilyrc'
564         else: # Windows apps like edit choke on .lilyrc
565             dotFilename='_lilyrc'
566
567         for d in [os.path.join(this.get('root'),'ly'), \
568                   os.environ['HOME'], os.curdir ]:
569             file=os.path.join(d,dotFilename)
570             try:
571                 fd = open( file, 'r' )
572             except:
573                 continue
574             
575             for line in fd.readlines():
576                 if re.match('#.*',line):
577                     continue
578                 m=re.search('([\w]+)=(.*)',line)
579                 if m:
580                     for var in varTable:
581                         if m.group(1) == var[0]:
582                             var[1](m.group(2),'rcfile')
583                             break
584             fd.close
585
586     #
587     # setPaperZize
588     #
589     def setPaperZize(this,size,requester):
590         """
591         Set paper size properties
592         """
593
594         paperTable = [
595             # regex          width    height      name
596             # -----          -----    ------      ----
597             ( 'a0.*',        2389,    3381,    'a0paper' ),
598             ( 'a1$|a1p.*',   1690,    2389,    'a1paper' ),
599             ( 'a2.*',        1194,    1690,    'a2paper' ),
600             ( 'a3.*',        845,     1194,    'a3paper' ),
601             ( 'a4.*',        597,     845,     'a4paper' ),
602             ( 'a5.*',        423,     597,     'a5paper' ),
603             ( 'a6.*',        298,     423,     'a6paper' ),
604             ( 'a7.*',        211,     298,     'a7paper' ),
605             ( 'a8.*',        305,     211,     'a8paper' ),
606             ( 'a9.*',        105,     305,     'a9paper' ),
607             ( 'a10.*',       74,      105,     'a10paper' ),
608             ( 'b0.*',        2847,    4023,    'b0paper' ),
609             ( 'b1.*',        2012,    2847,    'b1paper' ),
610             ( 'b2.*',        1423,    2012,    'b2paper' ),
611             ( 'b3.*',        1006,    1423,    'b3paper' ),
612             ( 'b4.*',        712,     1006,    'b4paper' ),
613             ( 'b5.*',        503,     712,     'b5paper' ),
614             ( 'archA$',      650,     867,     'archApaper' ),
615             ( 'archB$',      867,     1301,    'archBpaper' ),
616             ( 'archC$',      1301,    1734,    'archCpaper' ),
617             ( 'archD$',      1734,    2602,    'archDpaper' ),
618             ( 'archE$',      2602,    3469,    'archEpaper' ),
619             ( 'flsa$|flse$', 614,     940,     'flsapaper' ),
620             ( 'halfletter$', 397,     614,     'halfletterpaper' ),
621             ( 'ledger$',     1229,    795,     'ledgerpaper' ),
622             ( 'legal$',      614,     1012,    'legalpaper' ),
623             ( 'letter$',     614,     795,     'letterpaper' ),
624             ( 'note$',       542,     723,     'notepaper' )
625             ]
626
627         found=0
628         for paper in paperTable:
629             if re.match(paper[0],size):
630                 found=1
631                 this.__set('pagewidth',paper[1],requester)
632                 this.__set('pageheight',paper[2],requester)
633                 this.__set('papersize',paper[3],requester)
634                 break
635
636         if not found:
637             sys.exit('ExitBadPaper',size)
638
639     #
640     # setTextHeight
641     #
642     def setTextHeight(this,size,requester):
643         """
644         Set textheight property
645         """
646
647         m=re.match('([0-9][.0-9]*)(cm|mm|pt|$)',size)
648         if m:
649             if m.group(2) == 'cm':
650                 this.__set('textheight',\
651                            float(m.group(1)) * 72.27/2.54, requester )
652             elif m.group(2) == 'mm':
653                 this.__set('textheight',\
654                            float(m.group(1)) * 72.27/25.4, requester )
655             elif m.group(2) == 'pt':
656                 this.__set('textheight', float(m.group(1)), requester )
657             elif m.group(2) == '':
658                 this.__set('textheight', float(m.group(1)), requester )
659             else:
660                 sys.exit('ExitBadHeight', m.group(2))
661         else:           
662             sys.exit('ExitBadHeight', size)
663
664     #
665     # setLineWidth
666     #
667     def setLineWidth(this,size,requester):
668         """
669         Set linewidth propery
670         """
671
672         m=re.match('([0-9][.0-9]*)(cm|mm|pt|$)',size)
673         if m:
674             if m.group(2) == 'cm':
675                 this.__set('linewidth', \
676                 float(m.group(1)) * 72.27/2.54, requester )
677             elif m.group(2) == 'mm':
678                 this.__set('linewidth', \
679                 float(m.group(1)) * 72.27/25.4, requester )
680             elif m.group(2) == 'pt':
681                 this.__set('linewidth', float(m.group(1)), requester )
682             elif m.group(2) == '':
683                 this.__set('linewidth', float(m.group(1)), requester )
684             else:
685                 sys.exit('ExitBadWidth', m.group(2))
686         else:           
687             sys.stderr.write ('ly2dvi: warning: ignoring linewidth: ' + size + '\n')
688
689     #
690     # setOrientation
691     #
692     def setOrientation(this,orient,requester):
693         """
694         Set orientation property
695         """
696
697         if orient == 'landscape' or orient == 'portrait':
698             this.__set('orientation', orient, requester )
699         else:
700             sys.exit('ExitBadOrient', orient)
701
702     #
703     # setLanguage
704     #
705     def setLanguage(this,lang,requester):
706         """
707         Set language property
708         """
709
710         this.__set('language', '\\usepackage[%s]{babel}' % (lang), requester )
711
712     #
713     # setInclude
714     #
715     def setInclude(this,inc, requester):
716         """
717         Append an include path
718         """
719
720         tmp = this.get('include')
721         tmp.append(inc)
722         this.__set('include', tmp, requester )
723
724     #
725     # setDebug
726     #
727     def setDebug(this,value,requester):
728         """
729         Set or Clear debug flag
730         """
731
732         if int(value) == 1:
733             this.__set('debug',1,requester)
734         else:
735             this.__set('debug',0,requester)
736
737     #
738     # setKeeplilypond
739     #
740     def setKeeplilypond(this, value, requester):        
741         """
742         Set or Clear keeplilypond flag
743         """
744
745         if int(value) == 1:
746             this.__set('keeplilypond',1,requester)
747         else:
748             this.__set('keeplilypond',0,requester)
749
750     #
751     # setKeeply2dvi
752     #
753     def setKeeply2dvi(this, value, requester):  
754         """
755         Set or Clear keeply2dvi flag
756         """
757
758         if int(value) == 1:
759             this.__set('keeply2dvi',1,requester)
760         else:
761             this.__set('keeply2dvi',0,requester)
762
763     #
764     # setNonumber 
765     #
766     def setNonumber(this, value, requester):    
767         """
768         Set nonumber flag
769         """
770
771         if int(value) == 1:
772             this.__set('pagenumber','\\pagestyle{empty}',requester)
773         else:
774             this.__set('pagenumber','%',requester)
775
776     #
777     # setSeparate
778     #
779     def setSeparate(this, value, requester):    
780         """
781         Set or Clear separate flag
782         """
783
784         if int(value) == 1:
785             this.__set('separate',1,requester)
786         else:
787             this.__set('separate',0,requester)
788
789     #
790     # Set output directory name
791     #
792     def setOutput(this,out,requester):
793         this.__set('output',out,requester)
794
795     #
796     # Set latex header name
797     #
798     def setHeader(this,head, requester):
799         this.__set('header',head,requester)
800
801     #
802     # Set or Clear Dependencies flag to generate makefile dependencies
803     #
804     def setDependencies(this, value, requester):        
805         """
806         Set or Clear dependencies flag
807         """
808
809         if int(value) == 1:
810             this.__set('dependencies',1,requester)
811         else:
812             this.__set('dependencies',0,requester)
813
814     #
815     # Set tmp directory
816     #
817     def setTmp(this,dir, requester):    
818         this.__set('tmp',dir,requester)
819
820     #
821     # Set mudela source file name
822     #
823     def setFilename(this,file, requester):      
824         this.__set('filename',file,requester)
825
826     #
827     # Set title commands
828     #
829     def setTitles(this,titles, requester):      
830         this.__set('titles',titles,requester)
831
832     #
833     # Set title commands
834     #
835     def addLilyOutputFiles(this,filelist,requester):
836         """
837         Add a to the lily output list
838         """
839
840         tmp = this.get('lilyOutputFiles')
841         tmp = tmp + filelist
842         this.__set('lilyOutputFiles',tmp,requester)
843
844     #
845     # Set/Clear postscript flag
846     #
847     def setPostscript(this,value,requester):
848         """
849         Set postscript flag
850         """
851
852         if int(value) == 1:
853             this.__set('postscript',1,requester)
854         else:
855             this.__set('postscript',0,requester)
856
857     #
858     # Set root
859     #
860     def setRoot(this,path, requester):  
861         """
862         Set lilypond root directory
863         """
864
865         os.environ['LILYPONDPREFIX'] = path
866         if os.name == 'nt' or os.name == 'dos':
867             path = unc2dos(path);
868
869         this.__set('root',path,requester)
870         
871
872     #
873     # printProps
874     #
875     def printProps(this):
876         """
877         Print properties
878         """
879         
880         for key in this.__data.keys():
881             print "%s <%s>:<%s>" % (key,this.get(key),
882                                     this.__roverrideTable[this.__data[key][1]])
883
884 \f
885
886 #
887 # Misc functions
888 #
889
890 def getLilyopts():
891     inc = ''    
892     if len(Props.get('include')) > 0: 
893         inc = string.join (map (lambda x: '-I "%s"' % x, Props.get('include')))
894     else:
895
896         if Props.get('dependencies'):
897             dep=' -M'
898         else:
899             dep=''
900         return inc + dep
901     return inc
902
903 def writeLilylog(file,contents):
904     if Props.get('keeplilypond'):
905         base, ext = os.path.splitext(file)
906         tempfile.template=base + "_li"
907         file=tempfile.mktemp('.log')
908         output = Props.get('output')
909         if output != '':
910             file = os.path.join( output, file )
911         try:
912             fd = open( file, 'w' )
913         except:
914             sys.exit('ExitNoWrite', file)
915         fd.write(contents)
916         fd.close()
917
918 def getTeXFile(contents):
919     texfiles=[]
920     for line in string.split(contents,'\n'):
921         m = re.search('^Paper output to (.+)\.\.\.', line)
922         if m:
923             texfiles.append(m.group(1))
924
925     if texfiles == []:
926         sys.exit('ExitNoTeXName')
927     else:
928         return texfiles
929
930 def unc2dos(path):
931     """
932     Convert a path of format //<drive>/this/that/the/other to
933     <drive>:\this\that\the\other
934     """
935     m=re.match('^//([A-Za-z])(/.*)$',path)
936     if m:
937         return m.group(1) + ':' + os.path.normpath(m.group(2))
938     
939     
940
941 def program_id ():
942     return 'ly2dvi (GNU lilypond) ' + version;
943
944
945 def mailaddress():
946     try:
947         return os.environ['MAILADDRESS']
948     except KeyError:
949         return '(address unknown)'
950
951
952 def identify ():
953     sys.stderr.write (program_id () + '\n')
954
955 def print_version ():
956     sys.stdout.write (program_id ())
957
958 def help ():
959     sys.stdout.write (
960 """Usage: %s [OPTION]... [FILE]...
961
962 Generate dvi file from mudela or lilypond output
963
964 Options:
965   -D,--debug           increase verbosity
966   -F,--headers=        name of additional LaTeX headers file
967   -H,--Height=         set paper height (points) (see manual page)
968   -I,--include=DIR     add DIR to LilyPond\'s search path
969   -K,--keeplilypond    keep lilypond output files
970   -L,--landscape       set landscape orientation
971   -N,--nonumber        switch off page numbering
972   -O,--orientation=    set orientation (obsolete - use -L instead)
973   -P,--postscript      generate postscript file
974   -W,--Width=          set paper width (points) (see manual page)
975   -M,--dependencies    tell lilypond make a dependencies file
976   -h,--help            this help text
977   -k,--keeply2dvi      keep ly2dvi output files
978   -l,--language=       give LaTeX language (babel)
979   -o,--output=         set output directory
980   -p,--papersize=      give LaTeX papersize (eg. a4)
981   -s,--separate        run all files separately through LaTeX
982
983 files may be (a mix of) input to or output from lilypond(1)
984 """ % name)
985
986 \f
987
988 #
989 # main
990 #
991
992 def main():
993     """Generate dvi files from lilypond source/output"""
994
995     infile = Input()
996     outfile = TeXOutput()
997     texInputFiles=[]
998     tempfile.tempdir=""
999
1000     (options, files) = getopt.getopt (sys.argv[1:],
1001                                       'DF:H:I:KLNPW:Mhkl:o:p:s',
1002                                       ['debug', 'headers=', 'Height=',
1003                                        'include=', 'keeplilypond', 'landscape',
1004                                        'nonumber', 'Width=', 'dependencies',
1005                                        'help', 'keeply2dvi', 'language=',
1006                                        'output=', 'version', 'papersize=', 'separate',
1007                                        'postscript'])
1008     for opt in options:
1009         o = opt[0]
1010         a = opt[1]
1011         if o == '--debug' or o == '-D':
1012             Props.setDebug(1,'commandline')
1013         elif o == '--headers' or o == '-F':
1014             Props.setHeader(a,'commandline')
1015         elif o == '--include' or o == '-I':
1016             Props.setInclude(a,'commandline')
1017         elif o == '--Height' or o == '-H':
1018             Props.setTextHeight(a,'commandline')
1019         elif o == '--keeplilypond' or o == '-K':
1020             Props.setKeeplilypond(1,'commandline')
1021         elif o == '--landscape' or o == '-L':
1022             Props.setOrientation('landscape','commandline')
1023         elif o == '--nonumber' or o == '-N':
1024             Props.setNonumber(1,'commandline')
1025         elif o == '--Width' or o == '-W':
1026             Props.setLineWidth(a,'commandline')
1027         elif o == '--dependencies' or o == '-M':
1028             Props.setDependencies(1,'commandline')
1029         elif o == '--help' or o == '-h':
1030             help()
1031             return 0
1032         elif o == '--keeply2dvi' or o == '-k':
1033             Props.setKeeply2dvi(1,'commandline')
1034         elif o == '--language' or o == '-l':
1035             Props.setLanguage(a,'commandline')
1036         elif o == '--output' or o == '-o':
1037             Props.setOutput(a,'commandline')
1038         elif o == '--papersize' or o == '-p':
1039             Props.setPaperZize(a,'commandline')
1040         elif o == '--separate' or o == '-s':
1041             Props.setSeparate(1,'commandline')
1042         elif o == '--postscript' or o == '-P':
1043             Props.setPostscript(1,'commandline')
1044         elif o == '--version':
1045             print_version ()
1046             return 0
1047             
1048     identify()
1049             
1050     if len(files):
1051         for file in files:
1052             infile.open(file)
1053             type = infile.type()
1054             infile.close()
1055             if type == 'source':
1056                 if os.environ.has_key('OS') and \
1057                    os.environ['OS'] == 'Windows_95':
1058                     cmd = 'ash -c "lilypond %s %s 2>&1"' %(getLilyopts(), file)
1059                 else:
1060                     cmd = 'lilypond %s %s 2>&1' % (getLilyopts(), file)
1061                 sys.stderr.write ('executing: %s\n'% cmd)
1062                 
1063                 fd = os.popen(cmd , 'r')
1064                 log = ''
1065                 
1066                 s = fd.readline()
1067                 while len(s) > 0:
1068                         sys.stderr.write (s)
1069                         sys.stderr.flush ()
1070                         log = log + s
1071                         s = fd.readline ()
1072                 if 0:
1073                         s = fd.read (1)
1074                         while len(s) > 0:
1075                                 sys.stderr.write (s)
1076                                 sys.stderr.flush ()
1077                                 s = fd.read (1)                 
1078                         log = log + s
1079                 stat = fd.close()
1080                 if stat:
1081                     sys.exit('ExitBadLily', cmd )
1082                 texFiles=getTeXFile(log)
1083                 writeLilylog(file,log)
1084                 Props.addLilyOutputFiles(texFiles,'program')
1085                 texInputFiles = texInputFiles + texFiles
1086             else:
1087                 texInputFiles.append(file)
1088
1089         firstfile=1
1090         for file in texInputFiles:
1091             infile.open(file)
1092             infile.setVars() # first pass set variables
1093             infile.close()
1094             if Props.get('debug'):
1095                 Props.printProps()
1096             if firstfile:
1097                 outfile.start(file)  # allow for specified name
1098             else:
1099                 outfile.next()
1100             outfile.write("""\
1101 \\input{%s}
1102 """ % (file))
1103             if Props.get('separate'):
1104                 outfile.end()
1105             else:
1106                 firstfile=0
1107         if not Props.get('separate'):
1108             outfile.end()
1109     else:
1110         help()
1111         sys.exit('ExitBadArgs','No files specified')
1112
1113 #
1114 # Exit values
1115 #
1116 ExitTable = {
1117     'ExitInterupt'         : ['Ouch!', 1 ],
1118     'ExitBadArgs'          : ['Wrong number of arguments', 2 ],
1119     'ExitNotFound'         : ['File not found', 3 ],
1120     'ExitBadPaper'         : ['Unknown papersize', 4 ],
1121     'ExitBadHeight'        : ['Invalid Height specification', 5 ],
1122     'ExitBadWidth'         : ['Invalid Width specification', 6 ],
1123     'ExitBadOrient'        : ['Invalid Orientation specification', 7 ],
1124     'ExitNoWrite'          : ['Permission denied', 8 ],
1125     'ExitNoTeXName'        : ['hmm, I could not find an output file name', 9 ],
1126     'ExitBadLily'          : ['Lilypond failed', 10 ],
1127     'ExitBadLatex'         : ['Latex failed', 11 ],
1128     'ExitBadPostscript'    : ['Postscript failed', 12 ],
1129     'ExitUnknown'          : ['Unknown Exit Code', 20 ],
1130     }
1131
1132 def cleanup():
1133     lilyfiles = []
1134     tmpfiles = []
1135     if not Props.get('keeplilypond'):
1136         lilyfiles = Props.get('lilyOutputFiles')
1137     if not Props.get('keeply2dvi'):
1138         tmpfiles = glob.glob('*_ly[0-9]*.*')
1139     for file in lilyfiles + tmpfiles:
1140         if os.path.isfile(file):
1141             os.remove(file)
1142
1143
1144 Props = Properties()
1145
1146 try:
1147     main()
1148
1149 except KeyboardInterrupt:
1150     print ExitTable['ExitInterupt'][0]
1151     cleanup()
1152     sys.exit(ExitTable['ExitInterupt'][1])
1153
1154 except SystemExit, errno:
1155     if ExitTable.has_key(errno.args[0]):
1156         msg = ExitTable[errno.args[0]]
1157     else:
1158         msg = ExitTable['ExitUnknown']
1159     if len(errno.args) > 1:  
1160         sys.stderr.write( '%s: %s: %s\n' % (name, msg[0], errno.args[1]))
1161     else:
1162         sys.stderr.write( '%s %s\n' % (name, msg[0]))
1163     if Props.get('debug'):
1164         Props.printProps()
1165     cleanup()
1166     sys.exit(msg[1])
1167 else:
1168     cleanup()