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