]> git.donarmstrong.com Git - lilypond.git/blob - scripts/ly2dvi.py
release: 1.0.17
[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.5'
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, 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']:
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('% Creator: 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 \\cmrtwenty% ugh
274 \\makelilytitle
275 """) 
276
277     #
278     # next
279     #
280     def next(this):
281         """
282         Write LaTeX subheader information to support more than one
283         score in a document.  Lastly send current title information to
284         output.
285
286         input:  None
287         output: None
288         error:  None
289         """
290
291         this.write("""\
292 \\def\\theopus{}%
293 \\def\\thepiece{}%
294 \\def\\mudelaopus{}%
295 \\def\\mudelapiece{}%
296 """)
297         this.__mudelaDefs("\\def")
298         this.write("""\
299 \\def\\theopus{\\mudelaopus}% ugh
300 \\def\\thepiece{\\mudelapiece}%
301 \\makelilypiecetitle
302 """)
303
304
305     #
306     # end
307     #
308     def end(this):
309         """
310         Close output file and run latex on it.
311
312         input:  None
313         output: None
314         error:  ExitBadLatex Exception
315         """
316
317         outfile=this.__base + '.dvi'
318         if Props.get('output') != '':
319             outfile = os.path.join(Props.get('output'), outfile )
320             
321         this.write("""\
322 \\vfill\\hfill{\\LilyIdString}
323 \\end{document}
324 """)
325         this.__fd.close()
326         if ( os.name == 'posix' ):
327             stat = os.system('latex \'\\nonstopmode \\input %s\'' %
328                              (this.__outfile))
329         else: # Windows shells don't eat the single quotes
330             stat = os.system('latex \\nonstopmode \\input %s' %
331                              (this.__outfile))
332         if stat:
333             sys.exit('ExitBadLatex')
334         if os.path.isfile(outfile):
335             os.remove(outfile)
336         os.rename(this.__base + '.' + str(os.getpid()) + '.dvi', outfile)
337         sys.stderr.write( '\n' + program_id() + ': dvi file name is %s\n\n'
338                    % (outfile))
339
340         if Props.get('postscript'):
341             psoutfile=this.__base + '.ps'
342             if Props.get('output') != '':
343                 psoutfile = os.path.join(Props.get('output'), psoutfile )
344             stat = os.system('dvips -o %s %s 2>&1' % (psoutfile,outfile))
345             if stat:
346                 sys.exit('ExitBadPostscript')
347             
348
349 \f
350
351 class Properties:
352     """
353     This class handles all ly2dvi.py property manipulation
354
355     Public methods:
356     
357     __init__()  Constructor
358     set<property> methods
359     """
360
361     def __init__(this):
362
363         #
364         # Following is the order of priority for property assignment.  The
365         # list is organized from lowest to highest priority.  Each
366         # assignment is overridden by the next requester in the list.
367         #
368         # Requester     Description
369         # ---------     -----------
370         # init          Initial default values
371         # file          The values found in the lilypond generated TeX files
372         # environment   Envrionment variables LILYINCLUDE, LILYPONDPREFIX
373         # rcfile        $LILYPONDPREFIX/share/lilypond/.lilyrc
374         # rcfile        $HOME/.lilyrc
375         # rcfile        ./.lilyrc
376         # commandline   command line arguments
377         # 
378         this.__overrideTable = {
379             'init'        : 0,
380             'file'        : 1,
381             'environment' : 2,
382             'rcfile'      : 3,
383             'commandline' : 4,
384             'program'     : 5
385             }
386
387         this.__roverrideTable = {} # reverse lookup used for debug
388         for i in this.__overrideTable.items():
389             this.__roverrideTable[i[1]]=i[0]
390         
391         this.__data = {
392             'pagewidth'    :  [597, this.__overrideTable['init']],
393             'pageheight'   :  [845, this.__overrideTable['init']],
394             'papersize'    :  ['a4paper', this.__overrideTable['init']],
395             'textheight'   :  [0, this.__overrideTable['init']],
396             'linewidth'    :  [0, this.__overrideTable['init']],
397             'orientation'  :  ['portrait', this.__overrideTable['init']],
398             'language'     :  ['%', this.__overrideTable['init']],
399             'include'      :  [[], this.__overrideTable['init']],
400             'debug'        :  [0, this.__overrideTable['init']],
401             'keeplilypond' :  [0, this.__overrideTable['init']],
402             'keeply2dvi'   :  [0, this.__overrideTable['init']],
403             'pagenumber'   :  ['%', this.__overrideTable['init']],
404             'separate'     :  [0, this.__overrideTable['init']],
405             'output'       :  ['', this.__overrideTable['init']],
406             'header'       :  ['%', this.__overrideTable['init']],
407             'dependencies' :  [0, this.__overrideTable['init']],
408             'root'         :  ['', this.__overrideTable['init']],
409             'tmp'          :  ['d:\tmp', this.__overrideTable['init']],
410             'filename'     :  ['', this.__overrideTable['init']],
411             'titledefs'    :  [[], this.__overrideTable['init']],
412             'titles'       :  [{}, this.__overrideTable['init']],
413             'lilyOutputFiles' :  [[], this.__overrideTable['init']],
414             'postscript'   :  [0, this.__overrideTable['init']],
415             }
416
417         #
418         # Try to set root and HOME first before calling rcfile
419         #
420         if os.environ.has_key('LILYPONDPREFIX'):
421             this.setRoot(os.environ['LILYPONDPREFIX'], 'environment')
422         else:
423             p=os.path.split(sys.argv[0])
424             p=os.path.split(p[0])
425             this.setRoot(p[0],'init')
426
427         if not os.environ.has_key('HOME'):
428             if os.environ.has_key('HOMEDRIVE') and \
429                  os.environ.has_key('HOMEPATH'):
430                 os.environ['HOME'] = os.environ['HOMEDRIVE'] + \
431                                      os.environ['HOMEPATH']
432             else:
433                 os.environ['HOME'] = os.curdir
434
435         this.rcfile() # Read initialization file(s)
436
437         if os.environ.has_key('LILYINCLUDE'):
438             tmp=this.get('include')
439             for s in string.split(os.environ['LILYINCLUDE'],os.pathsep):
440                 tmp.append(s)
441             this.__set('include', tmp, 'environment')    
442
443
444         t=''
445         if os.environ.has_key ('TEXINPUTS'):
446                 t = os.pathsep + os.environ['TEXINPUTS']
447         os.environ['TEXINPUTS'] = os.path.join(this.get('root'), 'texmf',
448                                                'tex', 'lilypond' ) + t
449
450         t=''
451         if os.environ.has_key ('MFINPUTS'):
452                 t = os.pathsep + os.environ['MFINPUTS']
453         os.environ['MFINPUTS'] = os.path.join(this.get('root'), 'texmf',
454                                                'mf', 'public', 'lilypond' ) + t
455
456         if os.environ.has_key('TMP'):
457             this.__set('tmp',os.environ['TMP'],'environment')
458
459         
460         fd=this.get_texfile_path ('titledefs.tex')
461         mudefs=[]    
462
463         for line in fd.readlines():
464             m=re.match('\\\\newcommand\*{\\\\mudela([\w]+)}',line)
465             if m:
466                 mudefs.append(m.group(1))
467         fd.close
468         this.__set('titledefs', mudefs, 'init')
469
470     #
471     # __set
472     #
473     def __set(this,var,value,requester):
474         """
475         All of the set methods call this to set a property.  If the value
476         was last set by a requestor of lesser priority the new value is
477         assigned, else the old value has priority and is unchanged.
478         """
479
480         if this.__overrideTable[requester] < this.__data[var][1]:
481             return 0
482         else:
483             this.__data[var] = [value, this.__overrideTable[requester]]
484
485     #
486     # get
487     #
488     def get(this,var):
489         """
490         All of the get methods call this to get a property value.  List
491         variable types are return by value to facilitate an append operation.
492         """
493
494         if var == 'include' or var == 'lilyOutputFiles':
495             return this.__data[var][0][0:]  # return a copy not a ref
496         else:
497             return this.__data[var][0]
498
499     #
500     # get_texfile_path
501     #
502     def get_texfile_path (this, var):
503         """
504         locate and open titledefs.tex file
505         """
506
507         if os.name == 'nt':
508             path = os.path.join(this.get('root'), 'texmf', 'tex',
509                                 'lilypond', var)
510         else:
511             path =''
512             cmd =('kpsewhich tex %s %s' % (var,errorlog))
513             pipe = os.popen (cmd, 'r')
514             path = pipe.readline ()[:-1] # chop off \n
515             return_status =  pipe.close()
516             if return_status and not path:
517                 path = os.path.join(this.get('root'), 'texmf', 'tex',
518                                     'lilypond', 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'),'share','lilypond'), \
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.exit('ExitBadWidth', size)
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',1,requester)
763         else:
764             this.__set('pagenumber',0,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         this.__set('root',path,requester)
857
858     #
859     # printProps
860     #
861     def printProps(this):
862         """
863         Print properties
864         """
865         
866         for key in this.__data.keys():
867             print "%s <%s>:<%s>" % (key,this.get(key),
868                                     this.__roverrideTable[this.__data[key][1]])
869
870 \f
871
872 #
873 # Misc functions
874 #
875
876 def getLilyopts():
877     inc = ''    
878     if len(Props.get('include')) > 0: 
879         inc = '-I ' + string.join(Props.get('include'),os.pathsep)
880     else:
881
882         if Props.get('dependencies'):
883             dep=' -d'
884         else:
885             dep=''
886         return inc + dep
887     return inc
888
889 def writeLilylog(contents):
890     if Props.get('keeplilypond'):
891         file='lilylog.' + str(os.getpid())
892         output = Props.get('output')
893         if output != '':
894             file = os.path.join( output, file )
895         try:
896             fd = open( file, 'w' )
897         except:
898             sys.exit('ExitNoWrite', file)
899         fd.write(contents)
900         fd.close()
901
902 def getTeXFile(contents):
903     texfiles=[]
904     for line in string.split(contents,'\n'):
905         m = re.search('^TeX output to (.+)\.\.\.', line)
906         if m:
907             texfiles.append(m.group(1))
908
909     if texfiles == []:
910         sys.exit('ExitNoTeXName')
911     else:
912         return texfiles
913
914 def program_id ():
915     return name + ' ' + version;
916
917
918 def mailaddress():
919     try:
920         return os.environ['MAILADDRESS']
921     except KeyError:
922         return '(address unknown)'
923
924
925 def identify ():
926     sys.stderr.write (program_id () + '\n')
927
928 def help ():
929     sys.stderr.write (
930         'Generate dvi file from mudela or lilypond output\n'
931         'Usage: ' + name + ' [OPTION]... [FILE]...\n'
932         '\n'
933         'Options:\n'
934         '  -D,--debug           increase verbosity\n'
935         '  -F,--headers=        name of additional LaTeX headers file\n'
936         '  -H,--Height=         set paper height (points) (see manual page)\n'
937         '  -I,--include=DIR     add DIR to LilyPond\'s search path\n'
938         '  -K,--keeplilypond    keep lilypond output files\n'
939         '  -L,--landscape       set landscape orientation\n'
940         '  -N,--nonumber        switch off page numbering\n'
941         '  -O,--orientation=    set orientation (obsolete - use -L instead)\n'
942         '  -P,--postscript      generate postscript file\n'
943         '  -W,--Width=          set paper width (points) (see manual page)\n'
944         '  -d,--dependencies    tell lilypond make a dependencies file\n'
945         '  -h,--help            this help text\n'
946         '  -k,--keeply2dvi      keep ly2dvi output files\n'
947         '  -l,--language=       give LaTeX language (babel)\n'
948         '  -o,--output=         set output directory\n'
949         '  -p,--papersize=      give LaTeX papersize (eg. a4)\n'
950         '  -s,--separate        run all files separately through LaTeX\n'
951         '\n'
952         'files may be (a mix of) input to or output from lilypond(1)\n'
953         )
954
955 \f
956
957 #
958 # main
959 #
960
961 def main():
962     """Generate dvi files from lilypond source/output"""
963
964     infile = Input()
965     outfile = TeXOutput()
966     texInputFiles=[]
967
968     (options, files) = getopt.getopt (sys.argv[1:],
969                                       'DF:H:I:KLNPW:dhkl:o:p:s',
970                                       ['debug', 'headers=', 'Height=',
971                                        'include=', 'keeplilypond', 'landscape',
972                                        'nonumber', 'Width=', 'dependencies',
973                                        'help', 'keeply2dvi', 'language=',
974                                        'output=', 'papersize=', 'separate',
975                                        'postscript'])
976     for opt in options:
977         o = opt[0]
978         a = opt[1]
979         if o == '--debug' or o == '-D':
980             Props.setDebug(1,'commandline')
981         elif o == '--headers' or o == '-F':
982             Props.setHeader(a,'commandline')
983         elif o == '--include' or o == '-I':
984             Props.setInclude(a,'commandline')
985         elif o == '--Height' or o == '-H':
986             Props.setTextHeight(a,'commandline')
987         elif o == '--keeplilypond' or o == '-K':
988             Props.setKeeplilypond(1,'commandline')
989         elif o == '--landscape' or o == '-L':
990             Props.setOrientation('landscape','commandline')
991         elif o == '--nonumber' or o == '-N':
992             Props.setNonumber('commandline')
993         elif o == '--Width' or o == '-W':
994             Props.setLineWidth(a,'commandline')
995         elif o == '--dependencies' or o == '-d':
996             Props.setDependencies(1,'commandline')
997         elif o == '--help' or o == '-h':
998             help()
999             return 0
1000         elif o == '--keeply2dvi' or o == '-k':
1001             Props.setKeeply2dvi(1,'commandline')
1002         elif o == '--language' or o == '-l':
1003             Props.setLanguage(a,'commandline')
1004         elif o == '--output' or o == '-o':
1005             Props.setOutput(a,'commandline')
1006         elif o == '--papersize' or o == '-p':
1007             Props.setPaperZize(a,'commandline')
1008         elif o == '--separate' or o == '-s':
1009             Props.setSeparate(1,'commandline')
1010         elif o == '--postscript' or o == '-P':
1011             Props.setPostscript(1,'commandline')
1012
1013     if len(files):
1014         for file in files:
1015             infile.open(file)
1016             type = infile.type()
1017             infile.close()
1018             if type == 'source':
1019                 cmd = 'lilypond %s %s 2>&1' % (getLilyopts(), file)
1020                 fd = os.popen( cmd , 'r' )
1021                 log = ''
1022                 line=fd.readline()
1023                 while line:
1024                     log = log + line
1025                     sys.stderr.write( line )
1026                     line=fd.readline()
1027                 stat = fd.close()
1028                 if stat:
1029                     sys.exit('ExitBadLily', cmd )
1030                 texFiles=getTeXFile(log)
1031                 writeLilylog(log)
1032                 Props.addLilyOutputFiles(texFiles,'program')
1033                 texInputFiles = texInputFiles + texFiles
1034             else:
1035                 texInputFiles.append(file)
1036
1037         firstfile=1
1038         for file in texInputFiles:
1039             infile.open(file)
1040             infile.setVars() # first pass set variables
1041             infile.close()
1042             if Props.get('debug'):
1043                 Props.printProps()
1044             if firstfile:
1045                 outfile.start(file)
1046             else:
1047                 outfile.next()
1048             outfile.write("""\
1049 \\input{%s}
1050 """ % (file))
1051             if Props.get('separate'):
1052                 outfile.end()
1053             else:
1054                 firstfile=0
1055         if not Props.get('separate'):
1056             outfile.end()
1057     else:
1058         help()
1059         sys.exit('ExitBadArgs','No files specified')
1060
1061 #
1062 # Exit values
1063 #
1064 ExitTable = {
1065     'ExitInterupt'         : ['Ouch!', 1 ],
1066     'ExitBadArgs'          : ['Wrong number of arguments', 2 ],
1067     'ExitNotFound'         : ['File not found', 3 ],
1068     'ExitBadPaper'         : ['Unknown papersize', 4 ],
1069     'ExitBadHeight'        : ['Invalid Height specification', 5 ],
1070     'ExitBadWidth'         : ['Invalid Width specification', 6 ],
1071     'ExitBadOrient'        : ['Invalid Orientation specification', 7 ],
1072     'ExitNoWrite'          : ['Permission denied', 8 ],
1073     'ExitNoTeXName'        : ['hmm, I could not find an output file name', 9 ],
1074     'ExitBadLily'          : ['Lilypond failed', 10 ],
1075     'ExitBadLatex'         : ['Latex failed', 11 ],
1076     'ExitBadPostscript'    : ['Postscript failed', 12 ],
1077     'ExitUnknown'          : ['Unknown Exit Code', 20 ],
1078     }
1079
1080 def cleanup():
1081     lilyfiles = []
1082     tmpfiles = []
1083     if not Props.get('keeplilypond'):
1084         lilyfiles = Props.get('lilyOutputFiles')
1085     if not Props.get('keeply2dvi'):
1086         tmpfiles = glob.glob('*.' + str(os.getpid()) + '.*' )
1087     for file in lilyfiles + tmpfiles:
1088         if os.path.isfile(file):
1089             os.remove(file)
1090
1091
1092 identify()
1093 Props = Properties()
1094
1095 try:
1096     main()
1097
1098 except KeyboardInterrupt:
1099     print ExitTable['ExitInterupt'][0]
1100     cleanup()
1101     sys.exit(ExitTable['ExitInterupt'][1])
1102
1103 except SystemExit, errno:
1104     if ExitTable.has_key(errno.args[0]):
1105         msg = ExitTable[errno.args[0]]
1106     else:
1107         msg = ExitTable['ExitUnknown']
1108     if len(errno.args) > 1:  
1109         sys.stderr.write( '%s: %s: %s\n' % (name, msg[0], errno.args[1]))
1110     else:
1111         sys.stderr.write( '%s %s\n' % (name, msg[0]))
1112     if Props.get('debug'):
1113         Props.printProps()
1114     cleanup()
1115     sys.exit(msg[1])
1116 else:
1117     cleanup()