]> git.donarmstrong.com Git - lilypond.git/blob - scripts/ly2dvi32.py
patch::: 1.0.12.jcn2: opniewvisite
[lilypond.git] / scripts / ly2dvi32.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.4'
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         path =''
508         cmd =('kpsewhich tex %s %s' % (var,errorlog))
509         pipe = os.popen (cmd, 'r')
510         path = pipe.readline ()[:-1] # chop off \n
511         return_status =  pipe.close()
512         if return_status and not path:
513             path = os.path.join(this.get('root'), 'texmf', 'tex',
514                                 'lilypond', var)
515         fd = open(path, 'r')
516         return fd
517
518
519     #
520     # Read rc file
521     #
522     def rcfile(this):
523         """
524         Read initialization file(s)
525         """
526         varTable = [
527             #   name              set method
528             #   ----              ----------
529             ( 'DEBUG',          this.setDebug ),
530             ( 'DEPENDENCIES',   this.setDependencies ),
531             ( 'KEEPLILYPOND',   this.setKeeplilypond ),
532             ( 'KEEPLY2DVI',     this.setKeeply2dvi ),
533             ( 'LANGUAGE',       this.setLanguage ),
534             ( 'LATEXHF',        this.setHeader ),
535             ( 'LILYINCLUDE',    this.setInclude ),
536             ( 'LILYPONDPREFIX', this.setRoot ),
537             ( 'NONUMBER',       this.setNonumber ),
538             ( 'ORIENTATION',    this.setOrientation ),
539             ( 'OUTPUTDIR',      this.setOutput ),
540             ( 'PAPERSIZE',      this.setPaperZize ),
541             ( 'PHEIGHT',        this.setTextHeight ),
542             ( 'POSTSCRIPT',     this.setPostscript ),
543             ( 'PWIDTH',         this.setLineWidth ),
544             ( 'SEPARATE',       this.setSeparate ),
545             ( 'TMP',            this.setTmp ),
546             ]
547
548         if ( os.name == 'posix' ):
549             dotFilename='.lilyrc'
550         else: # Windows apps like edit choke on .lilyrc
551             dotFilename='_lilyrc'
552
553         for d in [os.path.join(this.get('root'),'share','lilypond'), \
554                   os.environ['HOME'], os.curdir ]:
555             file=os.path.join(d,dotFilename)
556             try:
557                 fd = open( file, 'r' )
558             except:
559                 continue
560             
561             for line in fd.readlines():
562                 if re.match('#.*',line):
563                     continue
564                 m=re.search('([\w]+)=(.*)',line)
565                 if m:
566                     for var in varTable:
567                         if m.group(1) == var[0]:
568                             var[1](m.group(2),'rcfile')
569                             break
570             fd.close
571
572     #
573     # setPaperZize
574     #
575     def setPaperZize(this,size,requester):
576         """
577         Set paper size properties
578         """
579
580         paperTable = [
581             # regex          width    height      name
582             # -----          -----    ------      ----
583             ( 'a0.*',        2389,    3381,    'a0paper' ),
584             ( 'a1$|a1p.*',   1690,    2389,    'a1paper' ),
585             ( 'a2.*',        1194,    1690,    'a2paper' ),
586             ( 'a3.*',        845,     1194,    'a3paper' ),
587             ( 'a4.*',        597,     845,     'a4paper' ),
588             ( 'a5.*',        423,     597,     'a5paper' ),
589             ( 'a6.*',        298,     423,     'a6paper' ),
590             ( 'a7.*',        211,     298,     'a7paper' ),
591             ( 'a8.*',        305,     211,     'a8paper' ),
592             ( 'a9.*',        105,     305,     'a9paper' ),
593             ( 'a10.*',       74,      105,     'a10paper' ),
594             ( 'b0.*',        2847,    4023,    'b0paper' ),
595             ( 'b1.*',        2012,    2847,    'b1paper' ),
596             ( 'b2.*',        1423,    2012,    'b2paper' ),
597             ( 'b3.*',        1006,    1423,    'b3paper' ),
598             ( 'b4.*',        712,     1006,    'b4paper' ),
599             ( 'b5.*',        503,     712,     'b5paper' ),
600             ( 'archA$',      650,     867,     'archApaper' ),
601             ( 'archB$',      867,     1301,    'archBpaper' ),
602             ( 'archC$',      1301,    1734,    'archCpaper' ),
603             ( 'archD$',      1734,    2602,    'archDpaper' ),
604             ( 'archE$',      2602,    3469,    'archEpaper' ),
605             ( 'flsa$|flse$', 614,     940,     'flsapaper' ),
606             ( 'halfletter$', 397,     614,     'halfletterpaper' ),
607             ( 'ledger$',     1229,    795,     'ledgerpaper' ),
608             ( 'legal$',      614,     1012,    'legalpaper' ),
609             ( 'letter$',     614,     795,     'letterpaper' ),
610             ( 'note$',       542,     723,     'notepaper' )
611             ]
612
613         found=0
614         for paper in paperTable:
615             if re.match(paper[0],size):
616                 found=1
617                 this.__set('pagewidth',paper[1],requester)
618                 this.__set('pageheight',paper[2],requester)
619                 this.__set('papersize',paper[3],requester)
620                 break
621
622         if not found:
623             sys.exit('ExitBadPaper',size)
624
625     #
626     # setTextHeight
627     #
628     def setTextHeight(this,size,requester):
629         """
630         Set textheight property
631         """
632
633         m=re.match('([0-9][.0-9]*)(cm|mm|pt|$)',size)
634         if m:
635             if m.group(2) == 'cm':
636                 this.__set('textheight',\
637                            float(m.group(1)) * 72.27/2.54, requester )
638             elif m.group(2) == 'mm':
639                 this.__set('textheight',\
640                            float(m.group(1)) * 72.27/25.4, requester )
641             elif m.group(2) == 'pt':
642                 this.__set('textheight', float(m.group(1)), requester )
643             elif m.group(2) == '':
644                 this.__set('textheight', float(m.group(1)), requester )
645             else:
646                 sys.exit('ExitBadHeight', m.group(2))
647         else:           
648             sys.exit('ExitBadHeight', size)
649
650     #
651     # setLineWidth
652     #
653     def setLineWidth(this,size,requester):
654         """
655         Set linewidth propery
656         """
657
658         m=re.match('([0-9][.0-9]*)(cm|mm|pt|$)',size)
659         if m:
660             if m.group(2) == 'cm':
661                 this.__set('linewidth', \
662                 float(m.group(1)) * 72.27/2.54, requester )
663             elif m.group(2) == 'mm':
664                 this.__set('linewidth', \
665                 float(m.group(1)) * 72.27/25.4, requester )
666             elif m.group(2) == 'pt':
667                 this.__set('linewidth', float(m.group(1)), requester )
668             elif m.group(2) == '':
669                 this.__set('linewidth', float(m.group(1)), requester )
670             else:
671                 sys.exit('ExitBadWidth', m.group(2))
672         else:           
673             sys.exit('ExitBadWidth', size)
674
675     #
676     # setOrientation
677     #
678     def setOrientation(this,orient,requester):
679         """
680         Set orientation property
681         """
682
683         if orient == 'landscape' or orient == 'portrait':
684             this.__set('orientation', orient, requester )
685         else:
686             sys.exit('ExitBadOrient', orient)
687
688     #
689     # setLanguage
690     #
691     def setLanguage(this,lang,requester):
692         """
693         Set language property
694         """
695
696         this.__set('language', '\\usepackage[%s]{babel}' % (lang), requester )
697
698     #
699     # setInclude
700     #
701     def setInclude(this,inc, requester):
702         """
703         Append an include path
704         """
705
706         tmp = this.get('include')
707         tmp.append(inc)
708         this.__set('include', tmp, requester )
709
710     #
711     # setDebug
712     #
713     def setDebug(this,value,requester):
714         """
715         Set or Clear debug flag
716         """
717
718         if int(value) == 1:
719             this.__set('debug',1,requester)
720         else:
721             this.__set('debug',0,requester)
722
723     #
724     # setKeeplilypond
725     #
726     def setKeeplilypond(this, value, requester):        
727         """
728         Set or Clear keeplilypond flag
729         """
730
731         if int(value) == 1:
732             this.__set('keeplilypond',1,requester)
733         else:
734             this.__set('keeplilypond',0,requester)
735
736     #
737     # setKeeply2dvi
738     #
739     def setKeeply2dvi(this, value, requester):  
740         """
741         Set or Clear keeply2dvi flag
742         """
743
744         if int(value) == 1:
745             this.__set('keeply2dvi',1,requester)
746         else:
747             this.__set('keeply2dvi',0,requester)
748
749     #
750     # setNonumber 
751     #
752     def setNonumber(this, value, requester):    
753         """
754         Set nonumber flag
755         """
756
757         if int(value) == 1:
758             this.__set('pagenumber',1,requester)
759         else:
760             this.__set('pagenumber',0,requester)
761
762     #
763     # setSeparate
764     #
765     def setSeparate(this, value, requester):    
766         """
767         Set or Clear separate flag
768         """
769
770         if int(value) == 1:
771             this.__set('separate',1,requester)
772         else:
773             this.__set('separate',0,requester)
774
775     #
776     # Set output directory name
777     #
778     def setOutput(this,out,requester):
779         this.__set('output',out,requester)
780
781     #
782     # Set latex header name
783     #
784     def setHeader(this,head, requester):
785         this.__set('header',head,requester)
786
787     #
788     # Set or Clear Dependencies flag to generate makefile dependencies
789     #
790     def setDependencies(this, requester):       
791         """
792         Set or Clear dependencies flag
793         """
794
795         if int(value) == 1:
796             this.__set('dependencies',1,requester)
797         else:
798             this.__set('dependencies',0,requester)
799
800     #
801     # Set tmp directory
802     #
803     def setTmp(this,dir, requester):    
804         this.__set('tmp',dir,requester)
805
806     #
807     # Set mudela source file name
808     #
809     def setFilename(this,file, requester):      
810         this.__set('filename',file,requester)
811
812     #
813     # Set title commands
814     #
815     def setTitles(this,titles, requester):      
816         this.__set('titles',titles,requester)
817
818     #
819     # Set title commands
820     #
821     def addLilyOutputFiles(this,filelist,requester):
822         """
823         Add a to the lily output list
824         """
825
826         tmp = this.get('lilyOutputFiles')
827         tmp = tmp + filelist
828         this.__set('lilyOutputFiles',tmp,requester)
829
830     #
831     # Set/Clear postscript flag
832     #
833     def setPostscript(this,value,requester):
834         """
835         Set postscript flag
836         """
837
838         if int(value) == 1:
839             this.__set('postscript',1,requester)
840         else:
841             this.__set('postscript',0,requester)
842
843     #
844     # Set root
845     #
846     def setRoot(this,path, requester):  
847         """
848         Set lilypond root directory
849         """
850
851         os.environ['LILYPONDPREFIX'] = path
852         this.__set('root',path,requester)
853
854     #
855     # printProps
856     #
857     def printProps(this):
858         """
859         Print properties
860         """
861         
862         for key in this.__data.keys():
863             print "%s <%s>:<%s>" % (key,this.get(key),
864                                     this.__roverrideTable[this.__data[key][1]])
865
866 \f
867
868 #
869 # Misc functions
870 #
871
872 def getLilyopts():
873     inc = ''    
874     if len(Props.get('include')) > 0: 
875         inc = '-I ' + string.join(Props.get('include'),os.pathsep)
876     else:
877
878         if Props.get('dependencies'):
879             dep=' -d'
880         else:
881             dep=''
882         return inc + dep
883     return inc
884
885 def writeLilylog(contents):
886     if Props.get('keeplilypond'):
887         file='lilylog.' + str(os.getpid())
888         output = Props.get('output')
889         if output != '':
890             file = os.path.join( output, file )
891         try:
892             fd = open( file, 'w' )
893         except:
894             sys.exit('ExitNoWrite', file)
895         fd.write(contents)
896         fd.close()
897
898 def getTeXFile(contents):
899     texfiles=[]
900     for line in string.split(contents,'\n'):
901         m = re.search('^TeX output to (.+)\.\.\.', line)
902         if m:
903             texfiles.append(m.group(1))
904
905     if texfiles == []:
906         sys.exit('ExitNoTeXName')
907     else:
908         return texfiles
909
910 def program_id ():
911     return name + ' ' + version;
912
913
914 def mailaddress():
915     try:
916         return os.environ['MAILADDRESS']
917     except KeyError:
918         return '(address unknown)'
919
920
921 def identify ():
922     sys.stderr.write (program_id () + '\n')
923
924 def help ():
925     sys.stderr.write (
926         'Generate dvi file from mudela or lilypond output\n'
927         'Usage: ' + name + ' [OPTION]... [FILE]...\n'
928         '\n'
929         'Options:\n'
930         '  -D,--debug           increase verbosity\n'
931         '  -F,--headers=        name of additional LaTeX headers file\n'
932         '  -H,--Height=         set paper height (points) (see manual page)\n'
933         '  -I,--include=DIR     add DIR to LilyPond\'s search path\n'
934         '  -K,--keeplilypond    keep lilypond output files\n'
935         '  -L,--landscape       set landscape orientation\n'
936         '  -N,--nonumber        switch off page numbering\n'
937         '  -O,--orientation=    set orientation (obsolete - use -L instead)\n'
938         '  -P,--postscript      generate postscript file\n'
939         '  -W,--Width=          set paper width (points) (see manual page)\n'
940         '  -d,--dependencies    tell lilypond make a dependencies file\n'
941         '  -h,--help            this help text\n'
942         '  -k,--keeply2dvi      keep ly2dvi output files\n'
943         '  -l,--language=       give LaTeX language (babel)\n'
944         '  -o,--output=         set output directory\n'
945         '  -p,--papersize=      give LaTeX papersize (eg. a4)\n'
946         '  -s,--separate        run all files separately through LaTeX\n'
947         '\n'
948         'files may be (a mix of) input to or output from lilypond(1)\n'
949         )
950
951 \f
952
953 #
954 # main
955 #
956
957 def main():
958     """Generate dvi files from lilypond source/output"""
959
960     infile = Input()
961     outfile = TeXOutput()
962     texInputFiles=[]
963
964     (options, files) = getopt.getopt (sys.argv[1:],
965                                       'DF:H:I:KLNPW:dhkl:o:p:s',
966                                       ['debug', 'headers=', 'Height=',
967                                        'include=', 'keeplilypond', 'landscape',
968                                        'nonumber', 'Width=', 'dependencies',
969                                        'help', 'keeply2dvi', 'language=',
970                                        'output=', 'papersize=', 'separate',
971                                        'postscript'])
972     for opt in options:
973         o = opt[0]
974         a = opt[1]
975         if o == '--debug' or o == '-D':
976             Props.setDebug(1,'commandline')
977         elif o == '--headers' or o == '-F':
978             Props.setHeader(a,'commandline')
979         elif o == '--include' or o == '-I':
980             Props.setInclude(a,'commandline')
981         elif o == '--Height' or o == '-H':
982             Props.setTextHeight(a,'commandline')
983         elif o == '--keeplilypond' or o == '-K':
984             Props.setKeeplilypond(1,'commandline')
985         elif o == '--landscape' or o == '-L':
986             Props.setOrientation('landscape','commandline')
987         elif o == '--nonumber' or o == '-N':
988             Props.setNonumber('commandline')
989         elif o == '--Width' or o == '-W':
990             Props.setLineWidth(a,'commandline')
991         elif o == '--dependencies' or o == '-d':
992             Props.setDependencies(1,'commandline')
993         elif o == '--help' or o == '-h':
994             help()
995             return 0
996         elif o == '--keeply2dvi' or o == '-k':
997             Props.setKeeply2dvi(1,'commandline')
998         elif o == '--language' or o == '-l':
999             Props.setLanguage(a,'commandline')
1000         elif o == '--output' or o == '-o':
1001             Props.setOutput(a,'commandline')
1002         elif o == '--papersize' or o == '-p':
1003             Props.setPaperZize(a,'commandline')
1004         elif o == '--separate' or o == '-s':
1005             Props.setSeparate(1,'commandline')
1006         elif o == '--postscript' or o == '-P':
1007             Props.setPostscript(1,'commandline')
1008
1009     if len(files):
1010         for file in files:
1011             infile.open(file)
1012             type = infile.type()
1013             infile.close()
1014             if type == 'source':
1015                 cmd = 'lilypond %s %s 2>&1' % (getLilyopts(), file)
1016                 fd = os.popen( cmd , 'r' )
1017                 log = ''
1018                 line=fd.readline()
1019                 while line:
1020                     log = log + line
1021                     sys.stderr.write( line )
1022                     line=fd.readline()
1023                 stat = fd.close()
1024                 if stat:
1025                     sys.exit('ExitBadLily', cmd )
1026                 texFiles=getTeXFile(log)
1027                 writeLilylog(log)
1028                 Props.addLilyOutputFiles(texFiles,'program')
1029                 texInputFiles = texInputFiles + texFiles
1030             else:
1031                 texInputFiles.append(file)
1032
1033         firstfile=1
1034         for file in texInputFiles:
1035             infile.open(file)
1036             infile.setVars() # first pass set variables
1037             infile.close()
1038             if Props.get('debug'):
1039                 Props.printProps()
1040             if firstfile:
1041                 outfile.start(file)
1042             else:
1043                 outfile.next()
1044             outfile.write("""\
1045 \\input{%s}
1046 """ % (file))
1047             if Props.get('separate'):
1048                 outfile.end()
1049             else:
1050                 firstfile=0
1051         if not Props.get('separate'):
1052             outfile.end()
1053     else:
1054         help()
1055         sys.exit('ExitBadArgs','No files specified')
1056
1057 #
1058 # Exit values
1059 #
1060 ExitTable = {
1061     'ExitInterupt'         : ['Ouch!', 1 ],
1062     'ExitBadArgs'          : ['Wrong number of arguments', 2 ],
1063     'ExitNotFound'         : ['File not found', 3 ],
1064     'ExitBadPaper'         : ['Unknown papersize', 4 ],
1065     'ExitBadHeight'        : ['Invalid Height specification', 5 ],
1066     'ExitBadWidth'         : ['Invalid Width specification', 6 ],
1067     'ExitBadOrient'        : ['Invalid Orientation specification', 7 ],
1068     'ExitNoWrite'          : ['Permission denied', 8 ],
1069     'ExitNoTeXName'        : ['hmm, I could not find an output file name', 9 ],
1070     'ExitBadLily'          : ['Lilypond failed', 10 ],
1071     'ExitBadLatex'         : ['Latex failed', 11 ],
1072     'ExitBadPostscript'    : ['Postscript failed', 12 ],
1073     'ExitUnknown'          : ['Unknown Exit Code', 20 ],
1074     }
1075
1076 def cleanup():
1077     lilyfiles = []
1078     tmpfiles = []
1079     if not Props.get('keeplilypond'):
1080         lilyfiles = Props.get('lilyOutputFiles')
1081     if not Props.get('keeply2dvi'):
1082         tmpfiles = glob.glob('*.' + str(os.getpid()) + '.*' )
1083     for file in lilyfiles + tmpfiles:
1084         if os.path.isfile(file):
1085             os.remove(file)
1086
1087
1088 identify()
1089 Props = Properties()
1090
1091 try:
1092     main()
1093
1094 except KeyboardInterrupt:
1095     print ExitTable['ExitInterupt'][0]
1096     cleanup()
1097     sys.exit(ExitTable['ExitInterupt'][1])
1098
1099 except SystemExit, errno:
1100     if ExitTable.has_key(errno.args[0]):
1101         msg = ExitTable[errno.args[0]]
1102     else:
1103         msg = ExitTable['ExitUnknown']
1104     if len(errno.args) > 1:  
1105         sys.stderr.write( '%s: %s: %s\n' % (name, msg[0], errno.args[1]))
1106     else:
1107         sys.stderr.write( '%s %s\n' % (name, msg[0]))
1108     if Props.get('debug'):
1109         Props.printProps()
1110     cleanup()
1111     sys.exit(msg[1])
1112 else:
1113     cleanup()