]> git.donarmstrong.com Git - lilypond.git/blob - scripts/ly2dvi.py
release: 1.1.63
[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 = '0.0.13'
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 class Properties:
357     """
358     This class handles all ly2dvi.py property manipulation
359
360     Public methods:
361     
362     __init__()  Constructor
363     set<property> methods
364     """
365
366     def __init__(this):
367
368         #
369         # Following is the order of priority for property assignment.  The
370         # list is organized from lowest to highest priority.  Each
371         # assignment is overridden by the next requester in the list.
372         #
373         # Requester     Description
374         # ---------     -----------
375         # init          Initial default values
376         # file          The values found in the lilypond generated TeX files
377         # environment   Envrionment variables LILYINCLUDE, LILYPONDPREFIX
378         # rcfile        $LILYPONDPREFIX/.lilyrc
379         # rcfile        $HOME/.lilyrc
380         # rcfile        ./.lilyrc
381         # commandline   command line arguments
382         # 
383         this.__overrideTable = {
384             'init'        : 0,
385             'file'        : 1,
386             'environment' : 2,
387             'rcfile'      : 3,
388             'commandline' : 4,
389             'program'     : 5
390             }
391
392         this.__roverrideTable = {} # reverse lookup used for debug
393         for i in this.__overrideTable.items():
394             this.__roverrideTable[i[1]]=i[0]
395         
396         this.__data = {
397             'pagewidth'    :  [597, this.__overrideTable['init']],
398             'pageheight'   :  [845, this.__overrideTable['init']],
399             'papersize'    :  ['a4paper', this.__overrideTable['init']],
400             'textheight'   :  [0, this.__overrideTable['init']],
401             'linewidth'    :  [500, this.__overrideTable['init']],
402             'orientation'  :  ['portrait', this.__overrideTable['init']],
403             'language'     :  ['%', this.__overrideTable['init']],
404             'include'      :  [[], this.__overrideTable['init']],
405             'debug'        :  [0, this.__overrideTable['init']],
406             'keeplilypond' :  [0, this.__overrideTable['init']],
407             'keeply2dvi'   :  [0, this.__overrideTable['init']],
408             'pagenumber'   :  ['%', this.__overrideTable['init']],
409             'separate'     :  [0, this.__overrideTable['init']],
410             'output'       :  ['', this.__overrideTable['init']],
411             'header'       :  ['%', this.__overrideTable['init']],
412             'dependencies' :  [0, this.__overrideTable['init']],
413             'root'         :  ['', this.__overrideTable['init']],
414             'tmp'          :  ['d:\tmp', this.__overrideTable['init']],
415             'filename'     :  ['', this.__overrideTable['init']],
416             'titledefs'    :  [[], this.__overrideTable['init']],
417             'titles'       :  [{}, this.__overrideTable['init']],
418             'lilyOutputFiles' :  [[], this.__overrideTable['init']],
419             'postscript'   :  [0, this.__overrideTable['init']],
420             }
421
422         #
423         # Try to set root and HOME first before calling rcfile
424         #
425         if os.environ.has_key('LILYPONDPREFIX'):
426             this.setRoot(os.environ['LILYPONDPREFIX'], 'environment')
427         else:
428             p=os.path.split(sys.argv[0])
429             p=os.path.split(p[0])
430             # bit silly. for ly2dvi, overrules compiled-in datadir...
431             # how to do this better (without running lily, of course?
432             this.setRoot(os.path.join(p[0],'share','lilypond'), 'init')
433
434         if not os.environ.has_key('HOME'):
435             if os.environ.has_key('HOMEDRIVE') and \
436                  os.environ.has_key('HOMEPATH'):
437                 os.environ['HOME'] = os.environ['HOMEDRIVE'] + \
438                                      os.environ['HOMEPATH']
439             else:
440                 os.environ['HOME'] = os.curdir
441
442         this.rcfile() # Read initialization file(s)
443
444         if os.environ.has_key('LILYINCLUDE'):
445             tmp=this.get('include')
446             for s in string.split(os.environ['LILYINCLUDE'],os.pathsep):
447                 tmp.append(s)
448             this.__set('include', tmp, 'environment')    
449
450
451         t= os.pathsep
452         if os.environ.has_key ('TEXINPUTS'):
453                 t = os.environ['TEXINPUTS'] + os.pathsep
454         os.environ['TEXINPUTS'] = t + \
455         os.path.join(this.get('root'), 'tex' ) + \
456         os.pathsep + os.path.join(this.get('root'), 'ps' )
457
458         t=''
459         if os.environ.has_key ('MFINPUTS'):
460                t = os.environ['MFINPUTS'] 
461         os.environ['MFINPUTS'] = os.pathsep + t + \
462                                  os.path.join(this.get('root'), 'mf')
463
464         if os.environ.has_key('TMP'):
465             this.__set('tmp',os.environ['TMP'],'environment')
466
467         
468         fd=this.get_texfile_path ('titledefs.tex')
469         mudefs=[]    
470
471         for line in fd.readlines():
472             m=re.match('\\\\newcommand\*{\\\\mudela([\w]+)}',line)
473             if m:
474                 mudefs.append(m.group(1))
475         fd.close
476         this.__set('titledefs', mudefs, 'init')
477
478     #
479     # __set
480     #
481     def __set(this,var,value,requester):
482         """
483         All of the set methods call this to set a property.  If the value
484         was last set by a requestor of lesser priority the new value is
485         assigned, else the old value has priority and is unchanged.
486         """
487
488         if this.__overrideTable[requester] < this.__data[var][1]:
489             return 0
490         else:
491             this.__data[var] = [value, this.__overrideTable[requester]]
492
493     #
494     # get
495     #
496     def get(this,var):
497         """
498         All of the get methods call this to get a property value.  List
499         variable types are return by value to facilitate an append operation.
500         """
501
502         if var == 'include' or var == 'lilyOutputFiles':
503             return this.__data[var][0][0:]  # return a copy not a ref
504         else:
505             return this.__data[var][0]
506
507     #
508     # get_texfile_path
509     #
510     def get_texfile_path (this, var):
511         """
512         locate and open titledefs.tex file
513         """
514
515         if os.name == 'nt':
516             path = os.path.join(this.get('root'), 'tex', var)
517         else:
518             path =''
519             cmd =('kpsewhich tex %s %s' % (var,errorlog))
520             pipe = os.popen (cmd, 'r')
521             path = pipe.readline ()[:-1] # chop off \n
522             return_status =  pipe.close()
523             if return_status and not path:
524                 path = os.path.join(this.get('root'), 'tex', var)
525         fd = open(path, 'r')
526         return fd
527
528
529     #
530     # Read rc file
531     #
532     def rcfile(this):
533         """
534         Read initialization file(s)
535         """
536         varTable = [
537             #   name              set method
538             #   ----              ----------
539             ( 'DEBUG',          this.setDebug ),
540             ( 'DEPENDENCIES',   this.setDependencies ),
541             ( 'KEEPLILYPOND',   this.setKeeplilypond ),
542             ( 'KEEPLY2DVI',     this.setKeeply2dvi ),
543             ( 'LANGUAGE',       this.setLanguage ),
544             ( 'LATEXHF',        this.setHeader ),
545             ( 'LILYINCLUDE',    this.setInclude ),
546             ( 'LILYPONDPREFIX', this.setRoot ),
547             ( 'NONUMBER',       this.setNonumber ),
548             ( 'ORIENTATION',    this.setOrientation ),
549             ( 'OUTPUTDIR',      this.setOutput ),
550             ( 'PAPERSIZE',      this.setPaperZize ),
551             ( 'PHEIGHT',        this.setTextHeight ),
552             ( 'POSTSCRIPT',     this.setPostscript ),
553             ( 'PWIDTH',         this.setLineWidth ),
554             ( 'SEPARATE',       this.setSeparate ),
555             ( 'TMP',            this.setTmp ),
556             ]
557
558         if ( os.name == 'posix' ):
559             dotFilename='.lilyrc'
560         else: # Windows apps like edit choke on .lilyrc
561             dotFilename='_lilyrc'
562
563         for d in [os.path.join(this.get('root'),'ly'), \
564                   os.environ['HOME'], os.curdir ]:
565             file=os.path.join(d,dotFilename)
566             try:
567                 fd = open( file, 'r' )
568             except:
569                 continue
570             
571             for line in fd.readlines():
572                 if re.match('#.*',line):
573                     continue
574                 m=re.search('([\w]+)=(.*)',line)
575                 if m:
576                     for var in varTable:
577                         if m.group(1) == var[0]:
578                             var[1](m.group(2),'rcfile')
579                             break
580             fd.close
581
582     #
583     # setPaperZize
584     #
585     def setPaperZize(this,size,requester):
586         """
587         Set paper size properties
588         """
589
590         paperTable = [
591             # regex          width    height      name
592             # -----          -----    ------      ----
593             ( 'a0.*',        2389,    3381,    'a0paper' ),
594             ( 'a1$|a1p.*',   1690,    2389,    'a1paper' ),
595             ( 'a2.*',        1194,    1690,    'a2paper' ),
596             ( 'a3.*',        845,     1194,    'a3paper' ),
597             ( 'a4.*',        597,     845,     'a4paper' ),
598             ( 'a5.*',        423,     597,     'a5paper' ),
599             ( 'a6.*',        298,     423,     'a6paper' ),
600             ( 'a7.*',        211,     298,     'a7paper' ),
601             ( 'a8.*',        305,     211,     'a8paper' ),
602             ( 'a9.*',        105,     305,     'a9paper' ),
603             ( 'a10.*',       74,      105,     'a10paper' ),
604             ( 'b0.*',        2847,    4023,    'b0paper' ),
605             ( 'b1.*',        2012,    2847,    'b1paper' ),
606             ( 'b2.*',        1423,    2012,    'b2paper' ),
607             ( 'b3.*',        1006,    1423,    'b3paper' ),
608             ( 'b4.*',        712,     1006,    'b4paper' ),
609             ( 'b5.*',        503,     712,     'b5paper' ),
610             ( 'archA$',      650,     867,     'archApaper' ),
611             ( 'archB$',      867,     1301,    'archBpaper' ),
612             ( 'archC$',      1301,    1734,    'archCpaper' ),
613             ( 'archD$',      1734,    2602,    'archDpaper' ),
614             ( 'archE$',      2602,    3469,    'archEpaper' ),
615             ( 'flsa$|flse$', 614,     940,     'flsapaper' ),
616             ( 'halfletter$', 397,     614,     'halfletterpaper' ),
617             ( 'ledger$',     1229,    795,     'ledgerpaper' ),
618             ( 'legal$',      614,     1012,    'legalpaper' ),
619             ( 'letter$',     614,     795,     'letterpaper' ),
620             ( 'note$',       542,     723,     'notepaper' )
621             ]
622
623         found=0
624         for paper in paperTable:
625             if re.match(paper[0],size):
626                 found=1
627                 this.__set('pagewidth',paper[1],requester)
628                 this.__set('pageheight',paper[2],requester)
629                 this.__set('papersize',paper[3],requester)
630                 break
631
632         if not found:
633             sys.exit('ExitBadPaper',size)
634
635     #
636     # setTextHeight
637     #
638     def setTextHeight(this,size,requester):
639         """
640         Set textheight property
641         """
642
643         m=re.match('([0-9][.0-9]*)(cm|mm|pt|$)',size)
644         if m:
645             if m.group(2) == 'cm':
646                 this.__set('textheight',\
647                            float(m.group(1)) * 72.27/2.54, requester )
648             elif m.group(2) == 'mm':
649                 this.__set('textheight',\
650                            float(m.group(1)) * 72.27/25.4, requester )
651             elif m.group(2) == 'pt':
652                 this.__set('textheight', float(m.group(1)), requester )
653             elif m.group(2) == '':
654                 this.__set('textheight', float(m.group(1)), requester )
655             else:
656                 sys.exit('ExitBadHeight', m.group(2))
657         else:           
658             sys.exit('ExitBadHeight', size)
659
660     #
661     # setLineWidth
662     #
663     def setLineWidth(this,size,requester):
664         """
665         Set linewidth propery
666         """
667
668         m=re.match('([0-9][.0-9]*)(cm|mm|pt|$)',size)
669         if m:
670             if m.group(2) == 'cm':
671                 this.__set('linewidth', \
672                 float(m.group(1)) * 72.27/2.54, requester )
673             elif m.group(2) == 'mm':
674                 this.__set('linewidth', \
675                 float(m.group(1)) * 72.27/25.4, requester )
676             elif m.group(2) == 'pt':
677                 this.__set('linewidth', float(m.group(1)), requester )
678             elif m.group(2) == '':
679                 this.__set('linewidth', float(m.group(1)), requester )
680             else:
681                 sys.exit('ExitBadWidth', m.group(2))
682         else:           
683             sys.stderr.write ('ly2dvi: warning: ignoring linewidth: ' + size + '\n')
684
685     #
686     # setOrientation
687     #
688     def setOrientation(this,orient,requester):
689         """
690         Set orientation property
691         """
692
693         if orient == 'landscape' or orient == 'portrait':
694             this.__set('orientation', orient, requester )
695         else:
696             sys.exit('ExitBadOrient', orient)
697
698     #
699     # setLanguage
700     #
701     def setLanguage(this,lang,requester):
702         """
703         Set language property
704         """
705
706         this.__set('language', '\\usepackage[%s]{babel}' % (lang), requester )
707
708     #
709     # setInclude
710     #
711     def setInclude(this,inc, requester):
712         """
713         Append an include path
714         """
715
716         tmp = this.get('include')
717         tmp.append(inc)
718         this.__set('include', tmp, requester )
719
720     #
721     # setDebug
722     #
723     def setDebug(this,value,requester):
724         """
725         Set or Clear debug flag
726         """
727
728         if int(value) == 1:
729             this.__set('debug',1,requester)
730         else:
731             this.__set('debug',0,requester)
732
733     #
734     # setKeeplilypond
735     #
736     def setKeeplilypond(this, value, requester):        
737         """
738         Set or Clear keeplilypond flag
739         """
740
741         if int(value) == 1:
742             this.__set('keeplilypond',1,requester)
743         else:
744             this.__set('keeplilypond',0,requester)
745
746     #
747     # setKeeply2dvi
748     #
749     def setKeeply2dvi(this, value, requester):  
750         """
751         Set or Clear keeply2dvi flag
752         """
753
754         if int(value) == 1:
755             this.__set('keeply2dvi',1,requester)
756         else:
757             this.__set('keeply2dvi',0,requester)
758
759     #
760     # setNonumber 
761     #
762     def setNonumber(this, value, requester):    
763         """
764         Set nonumber flag
765         """
766
767         if int(value) == 1:
768             this.__set('pagenumber','\\pagestyle{empty}',requester)
769         else:
770             this.__set('pagenumber','%',requester)
771
772     #
773     # setSeparate
774     #
775     def setSeparate(this, value, requester):    
776         """
777         Set or Clear separate flag
778         """
779
780         if int(value) == 1:
781             this.__set('separate',1,requester)
782         else:
783             this.__set('separate',0,requester)
784
785     #
786     # Set output directory name
787     #
788     def setOutput(this,out,requester):
789         this.__set('output',out,requester)
790
791     #
792     # Set latex header name
793     #
794     def setHeader(this,head, requester):
795         this.__set('header',head,requester)
796
797     #
798     # Set or Clear Dependencies flag to generate makefile dependencies
799     #
800     def setDependencies(this, value, requester):        
801         """
802         Set or Clear dependencies flag
803         """
804
805         if int(value) == 1:
806             this.__set('dependencies',1,requester)
807         else:
808             this.__set('dependencies',0,requester)
809
810     #
811     # Set tmp directory
812     #
813     def setTmp(this,dir, requester):    
814         this.__set('tmp',dir,requester)
815
816     #
817     # Set mudela source file name
818     #
819     def setFilename(this,file, requester):      
820         this.__set('filename',file,requester)
821
822     #
823     # Set title commands
824     #
825     def setTitles(this,titles, requester):      
826         this.__set('titles',titles,requester)
827
828     #
829     # Set title commands
830     #
831     def addLilyOutputFiles(this,filelist,requester):
832         """
833         Add a to the lily output list
834         """
835
836         tmp = this.get('lilyOutputFiles')
837         tmp = tmp + filelist
838         this.__set('lilyOutputFiles',tmp,requester)
839
840     #
841     # Set/Clear postscript flag
842     #
843     def setPostscript(this,value,requester):
844         """
845         Set postscript flag
846         """
847
848         if int(value) == 1:
849             this.__set('postscript',1,requester)
850         else:
851             this.__set('postscript',0,requester)
852
853     #
854     # Set root
855     #
856     def setRoot(this,path, requester):  
857         """
858         Set lilypond root directory
859         """
860
861         os.environ['LILYPONDPREFIX'] = path
862         if os.name == 'nt' or os.name == 'dos':
863             path = unc2dos(path);
864
865         this.__set('root',path,requester)
866         
867
868     #
869     # printProps
870     #
871     def printProps(this):
872         """
873         Print properties
874         """
875         
876         for key in this.__data.keys():
877             print "%s <%s>:<%s>" % (key,this.get(key),
878                                     this.__roverrideTable[this.__data[key][1]])
879
880 \f
881
882 #
883 # Misc functions
884 #
885
886 def getLilyopts():
887     inc = ''    
888     if len(Props.get('include')) > 0: 
889         inc = string.join (map (lambda x: '-I "%s"' % x, Props.get('include')))
890     else:
891
892         if Props.get('dependencies'):
893             dep=' -M'
894         else:
895             dep=''
896         return inc + dep
897     return inc
898
899 def writeLilylog(file,contents):
900     if Props.get('keeplilypond'):
901         base, ext = os.path.splitext(file)
902         tempfile.template=base + "_li"
903         file=tempfile.mktemp('.log')
904         output = Props.get('output')
905         if output != '':
906             file = os.path.join( output, file )
907         try:
908             fd = open( file, 'w' )
909         except:
910             sys.exit('ExitNoWrite', file)
911         fd.write(contents)
912         fd.close()
913
914 def getTeXFile(contents):
915     texfiles=[]
916     for line in string.split(contents,'\n'):
917         m = re.search('^Paper output to (.+)\.\.\.', line)
918         if m:
919             texfiles.append(m.group(1))
920
921     if texfiles == []:
922         sys.exit('ExitNoTeXName')
923     else:
924         return texfiles
925
926 def unc2dos(path):
927     """
928     Convert a path of format //<drive>/this/that/the/other to
929     <drive>:\this\that\the\other
930     """
931     m=re.match('^//([A-Za-z])(/.*)$',path)
932     if m:
933         return m.group(1) + ':' + os.path.normpath(m.group(2))
934     
935     
936
937 def program_id ():
938     return name + ' ' + version;
939
940
941 def mailaddress():
942     try:
943         return os.environ['MAILADDRESS']
944     except KeyError:
945         return '(address unknown)'
946
947
948 def identify ():
949     sys.stderr.write (program_id () + '\n')
950
951 def help ():
952     sys.stderr.write (
953         'Generate dvi file from mudela or lilypond output\n'
954         'Usage: ' + name + ' [OPTION]... [FILE]...\n'
955         '\n'
956         'Options:\n'
957         '  -D,--debug           increase verbosity\n'
958         '  -F,--headers=        name of additional LaTeX headers file\n'
959         '  -H,--Height=         set paper height (points) (see manual page)\n'
960         '  -I,--include=DIR     add DIR to LilyPond\'s search path\n'
961         '  -K,--keeplilypond    keep lilypond output files\n'
962         '  -L,--landscape       set landscape orientation\n'
963         '  -N,--nonumber        switch off page numbering\n'
964         '  -O,--orientation=    set orientation (obsolete - use -L instead)\n'
965         '  -P,--postscript      generate postscript file\n'
966         '  -W,--Width=          set paper width (points) (see manual page)\n'
967         '  -M,--dependencies    tell lilypond make a dependencies file\n'
968         '  -h,--help            this help text\n'
969         '  -k,--keeply2dvi      keep ly2dvi output files\n'
970         '  -l,--language=       give LaTeX language (babel)\n'
971         '  -o,--output=         set output directory\n'
972         '  -p,--papersize=      give LaTeX papersize (eg. a4)\n'
973         '  -s,--separate        run all files separately through LaTeX\n'
974         '\n'
975         'files may be (a mix of) input to or output from lilypond(1)\n'
976         )
977
978 \f
979
980 #
981 # main
982 #
983
984 def main():
985     """Generate dvi files from lilypond source/output"""
986
987     infile = Input()
988     outfile = TeXOutput()
989     texInputFiles=[]
990     tempfile.tempdir=""
991
992     (options, files) = getopt.getopt (sys.argv[1:],
993                                       'DF:H:I:KLNPW:Mhkl:o:p:s',
994                                       ['debug', 'headers=', 'Height=',
995                                        'include=', 'keeplilypond', 'landscape',
996                                        'nonumber', 'Width=', 'dependencies',
997                                        'help', 'keeply2dvi', 'language=',
998                                        'output=', 'papersize=', 'separate',
999                                        'postscript'])
1000     for opt in options:
1001         o = opt[0]
1002         a = opt[1]
1003         if o == '--debug' or o == '-D':
1004             Props.setDebug(1,'commandline')
1005         elif o == '--headers' or o == '-F':
1006             Props.setHeader(a,'commandline')
1007         elif o == '--include' or o == '-I':
1008             Props.setInclude(a,'commandline')
1009         elif o == '--Height' or o == '-H':
1010             Props.setTextHeight(a,'commandline')
1011         elif o == '--keeplilypond' or o == '-K':
1012             Props.setKeeplilypond(1,'commandline')
1013         elif o == '--landscape' or o == '-L':
1014             Props.setOrientation('landscape','commandline')
1015         elif o == '--nonumber' or o == '-N':
1016             Props.setNonumber(1,'commandline')
1017         elif o == '--Width' or o == '-W':
1018             Props.setLineWidth(a,'commandline')
1019         elif o == '--dependencies' or o == '-M':
1020             Props.setDependencies(1,'commandline')
1021         elif o == '--help' or o == '-h':
1022             help()
1023             return 0
1024         elif o == '--keeply2dvi' or o == '-k':
1025             Props.setKeeply2dvi(1,'commandline')
1026         elif o == '--language' or o == '-l':
1027             Props.setLanguage(a,'commandline')
1028         elif o == '--output' or o == '-o':
1029             Props.setOutput(a,'commandline')
1030         elif o == '--papersize' or o == '-p':
1031             Props.setPaperZize(a,'commandline')
1032         elif o == '--separate' or o == '-s':
1033             Props.setSeparate(1,'commandline')
1034         elif o == '--postscript' or o == '-P':
1035             Props.setPostscript(1,'commandline')
1036
1037     if len(files):
1038         for file in files:
1039             infile.open(file)
1040             type = infile.type()
1041             infile.close()
1042             if type == 'source':
1043                 if os.environ.has_key('OS') and \
1044                    os.environ['OS'] == 'Windows_95':
1045                     cmd = 'ash -c "lilypond %s %s 2>&1"' %(getLilyopts(), file)
1046                 else:
1047                     cmd = 'lilypond %s %s 2>&1' % (getLilyopts(), file)
1048                 sys.stderr.write ('executing: %s\n'% cmd)
1049                 
1050                 fd = os.popen(cmd , 'r')
1051                 log = ''
1052                 
1053                 s = fd.readline()
1054                 while len(s) > 0:
1055                         sys.stderr.write (s)
1056                         sys.stderr.flush ()
1057                         log = log + s
1058                         s = fd.readline ()
1059                 if 0:
1060                         s = fd.read (1)
1061                         while len(s) > 0:
1062                                 sys.stderr.write (s)
1063                                 sys.stderr.flush ()
1064                                 s = fd.read (1)                 
1065                         log = log + s
1066                 stat = fd.close()
1067                 if stat:
1068                     sys.exit('ExitBadLily', cmd )
1069                 texFiles=getTeXFile(log)
1070                 writeLilylog(file,log)
1071                 Props.addLilyOutputFiles(texFiles,'program')
1072                 texInputFiles = texInputFiles + texFiles
1073             else:
1074                 texInputFiles.append(file)
1075
1076         firstfile=1
1077         for file in texInputFiles:
1078             infile.open(file)
1079             infile.setVars() # first pass set variables
1080             infile.close()
1081             if Props.get('debug'):
1082                 Props.printProps()
1083             if firstfile:
1084                 outfile.start(file)  # allow for specified name
1085             else:
1086                 outfile.next()
1087             outfile.write("""\
1088 \\input{%s}
1089 """ % (file))
1090             if Props.get('separate'):
1091                 outfile.end()
1092             else:
1093                 firstfile=0
1094         if not Props.get('separate'):
1095             outfile.end()
1096     else:
1097         help()
1098         sys.exit('ExitBadArgs','No files specified')
1099
1100 #
1101 # Exit values
1102 #
1103 ExitTable = {
1104     'ExitInterupt'         : ['Ouch!', 1 ],
1105     'ExitBadArgs'          : ['Wrong number of arguments', 2 ],
1106     'ExitNotFound'         : ['File not found', 3 ],
1107     'ExitBadPaper'         : ['Unknown papersize', 4 ],
1108     'ExitBadHeight'        : ['Invalid Height specification', 5 ],
1109     'ExitBadWidth'         : ['Invalid Width specification', 6 ],
1110     'ExitBadOrient'        : ['Invalid Orientation specification', 7 ],
1111     'ExitNoWrite'          : ['Permission denied', 8 ],
1112     'ExitNoTeXName'        : ['hmm, I could not find an output file name', 9 ],
1113     'ExitBadLily'          : ['Lilypond failed', 10 ],
1114     'ExitBadLatex'         : ['Latex failed', 11 ],
1115     'ExitBadPostscript'    : ['Postscript failed', 12 ],
1116     'ExitUnknown'          : ['Unknown Exit Code', 20 ],
1117     }
1118
1119 def cleanup():
1120     lilyfiles = []
1121     tmpfiles = []
1122     if not Props.get('keeplilypond'):
1123         lilyfiles = Props.get('lilyOutputFiles')
1124     if not Props.get('keeply2dvi'):
1125         tmpfiles = glob.glob('*_ly[0-9]*.*')
1126     for file in lilyfiles + tmpfiles:
1127         if os.path.isfile(file):
1128             os.remove(file)
1129
1130
1131 identify()
1132 Props = Properties()
1133
1134 try:
1135     main()
1136
1137 except KeyboardInterrupt:
1138     print ExitTable['ExitInterupt'][0]
1139     cleanup()
1140     sys.exit(ExitTable['ExitInterupt'][1])
1141
1142 except SystemExit, errno:
1143     if ExitTable.has_key(errno.args[0]):
1144         msg = ExitTable[errno.args[0]]
1145     else:
1146         msg = ExitTable['ExitUnknown']
1147     if len(errno.args) > 1:  
1148         sys.stderr.write( '%s: %s: %s\n' % (name, msg[0], errno.args[1]))
1149     else:
1150         sys.stderr.write( '%s %s\n' % (name, msg[0]))
1151     if Props.get('debug'):
1152         Props.printProps()
1153     cleanup()
1154     sys.exit(msg[1])
1155 else:
1156     cleanup()