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