]> git.donarmstrong.com Git - lilypond.git/blob - scripts/ly2dvi.py
patch::: 1.1.13.uu1
[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.7'
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             # bit silly. for ly2dvi, overrules compiled-in datadir...
426             # how to do this better (without running lily, of course?
427             this.setRoot(p[0] + '/share/lilypond', 'init')
428
429         if not os.environ.has_key('HOME'):
430             if os.environ.has_key('HOMEDRIVE') and \
431                  os.environ.has_key('HOMEPATH'):
432                 os.environ['HOME'] = os.environ['HOMEDRIVE'] + \
433                                      os.environ['HOMEPATH']
434             else:
435                 os.environ['HOME'] = os.curdir
436
437         this.rcfile() # Read initialization file(s)
438
439         if os.environ.has_key('LILYINCLUDE'):
440             tmp=this.get('include')
441             for s in string.split(os.environ['LILYINCLUDE'],os.pathsep):
442                 tmp.append(s)
443             this.__set('include', tmp, 'environment')    
444
445
446         t=''
447         if os.environ.has_key ('TEXINPUTS'):
448                 t = os.pathsep + os.environ['TEXINPUTS']
449         os.environ['TEXINPUTS'] = os.path.join(this.get('root'), 'share',
450                                                'lilypond', 'tex' ) + t
451
452         t=''
453         if os.environ.has_key ('MFINPUTS'):
454                 t = os.pathsep + os.environ['MFINPUTS']
455         os.environ['MFINPUTS'] = os.path.join(this.get('root'), 'share',
456                                                'lilypond', 'mf' ) + t
457
458         if os.environ.has_key('TMP'):
459             this.__set('tmp',os.environ['TMP'],'environment')
460
461         
462         fd=this.get_texfile_path ('titledefs.tex')
463         mudefs=[]    
464
465         for line in fd.readlines():
466             m=re.match('\\\\newcommand\*{\\\\mudela([\w]+)}',line)
467             if m:
468                 mudefs.append(m.group(1))
469         fd.close
470         this.__set('titledefs', mudefs, 'init')
471
472     #
473     # __set
474     #
475     def __set(this,var,value,requester):
476         """
477         All of the set methods call this to set a property.  If the value
478         was last set by a requestor of lesser priority the new value is
479         assigned, else the old value has priority and is unchanged.
480         """
481
482         if this.__overrideTable[requester] < this.__data[var][1]:
483             return 0
484         else:
485             this.__data[var] = [value, this.__overrideTable[requester]]
486
487     #
488     # get
489     #
490     def get(this,var):
491         """
492         All of the get methods call this to get a property value.  List
493         variable types are return by value to facilitate an append operation.
494         """
495
496         if var == 'include' or var == 'lilyOutputFiles':
497             return this.__data[var][0][0:]  # return a copy not a ref
498         else:
499             return this.__data[var][0]
500
501     #
502     # get_texfile_path
503     #
504     def get_texfile_path (this, var):
505         """
506         locate and open titledefs.tex file
507         """
508
509         if os.name == 'nt':
510             path = os.path.join(this.get('root'), 'share', 'lilypond',
511                                 'tex', var)
512         else:
513             path =''
514             cmd =('kpsewhich tex %s %s' % (var,errorlog))
515             pipe = os.popen (cmd, 'r')
516             path = pipe.readline ()[:-1] # chop off \n
517             return_status =  pipe.close()
518             if return_status and not path:
519                 path = os.path.join(this.get('root'), 'share', 'lilypond',
520                                     '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'),'share','lilypond','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.exit('ExitBadWidth', size)
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',1,requester)
765         else:
766             this.__set('pagenumber',0,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         this.__set('root',path,requester)
859
860     #
861     # printProps
862     #
863     def printProps(this):
864         """
865         Print properties
866         """
867         
868         for key in this.__data.keys():
869             print "%s <%s>:<%s>" % (key,this.get(key),
870                                     this.__roverrideTable[this.__data[key][1]])
871
872 \f
873
874 #
875 # Misc functions
876 #
877
878 def getLilyopts():
879     inc = ''    
880     if len(Props.get('include')) > 0: 
881         inc = string.join (map (lambda x: '-I "%s"' % x, Props.get('include')))
882     else:
883
884         if Props.get('dependencies'):
885             dep=' -d'
886         else:
887             dep=''
888         return inc + dep
889     return inc
890
891 def writeLilylog(contents):
892     if Props.get('keeplilypond'):
893         file='lilylog.' + str(os.getpid())
894         output = Props.get('output')
895         if output != '':
896             file = os.path.join( output, file )
897         try:
898             fd = open( file, 'w' )
899         except:
900             sys.exit('ExitNoWrite', file)
901         fd.write(contents)
902         fd.close()
903
904 def getTeXFile(contents):
905     texfiles=[]
906     for line in string.split(contents,'\n'):
907         m = re.search('^Paper output to (.+)\.\.\.', line)
908         if m:
909             texfiles.append(m.group(1))
910
911     if texfiles == []:
912         sys.exit('ExitNoTeXName')
913     else:
914         return texfiles
915
916 def program_id ():
917     return name + ' ' + version;
918
919
920 def mailaddress():
921     try:
922         return os.environ['MAILADDRESS']
923     except KeyError:
924         return '(address unknown)'
925
926
927 def identify ():
928     sys.stderr.write (program_id () + '\n')
929
930 def help ():
931     sys.stderr.write (
932         'Generate dvi file from mudela or lilypond output\n'
933         'Usage: ' + name + ' [OPTION]... [FILE]...\n'
934         '\n'
935         'Options:\n'
936         '  -D,--debug           increase verbosity\n'
937         '  -F,--headers=        name of additional LaTeX headers file\n'
938         '  -H,--Height=         set paper height (points) (see manual page)\n'
939         '  -I,--include=DIR     add DIR to LilyPond\'s search path\n'
940         '  -K,--keeplilypond    keep lilypond output files\n'
941         '  -L,--landscape       set landscape orientation\n'
942         '  -N,--nonumber        switch off page numbering\n'
943         '  -O,--orientation=    set orientation (obsolete - use -L instead)\n'
944         '  -P,--postscript      generate postscript file\n'
945         '  -W,--Width=          set paper width (points) (see manual page)\n'
946         '  -d,--dependencies    tell lilypond make a dependencies file\n'
947         '  -h,--help            this help text\n'
948         '  -k,--keeply2dvi      keep ly2dvi output files\n'
949         '  -l,--language=       give LaTeX language (babel)\n'
950         '  -o,--output=         set output directory\n'
951         '  -p,--papersize=      give LaTeX papersize (eg. a4)\n'
952         '  -s,--separate        run all files separately through LaTeX\n'
953         '\n'
954         'files may be (a mix of) input to or output from lilypond(1)\n'
955         )
956
957 \f
958
959 #
960 # main
961 #
962
963 def main():
964     """Generate dvi files from lilypond source/output"""
965
966     infile = Input()
967     outfile = TeXOutput()
968     texInputFiles=[]
969
970     (options, files) = getopt.getopt (sys.argv[1:],
971                                       'DF:H:I:KLNPW:dhkl:o:p:s',
972                                       ['debug', 'headers=', 'Height=',
973                                        'include=', 'keeplilypond', 'landscape',
974                                        'nonumber', 'Width=', 'dependencies',
975                                        'help', 'keeply2dvi', 'language=',
976                                        'output=', 'papersize=', 'separate',
977                                        'postscript'])
978     for opt in options:
979         o = opt[0]
980         a = opt[1]
981         if o == '--debug' or o == '-D':
982             Props.setDebug(1,'commandline')
983         elif o == '--headers' or o == '-F':
984             Props.setHeader(a,'commandline')
985         elif o == '--include' or o == '-I':
986             Props.setInclude(a,'commandline')
987         elif o == '--Height' or o == '-H':
988             Props.setTextHeight(a,'commandline')
989         elif o == '--keeplilypond' or o == '-K':
990             Props.setKeeplilypond(1,'commandline')
991         elif o == '--landscape' or o == '-L':
992             Props.setOrientation('landscape','commandline')
993         elif o == '--nonumber' or o == '-N':
994             Props.setNonumber('commandline')
995         elif o == '--Width' or o == '-W':
996             Props.setLineWidth(a,'commandline')
997         elif o == '--dependencies' or o == '-d':
998             Props.setDependencies(1,'commandline')
999         elif o == '--help' or o == '-h':
1000             help()
1001             return 0
1002         elif o == '--keeply2dvi' or o == '-k':
1003             Props.setKeeply2dvi(1,'commandline')
1004         elif o == '--language' or o == '-l':
1005             Props.setLanguage(a,'commandline')
1006         elif o == '--output' or o == '-o':
1007             Props.setOutput(a,'commandline')
1008         elif o == '--papersize' or o == '-p':
1009             Props.setPaperZize(a,'commandline')
1010         elif o == '--separate' or o == '-s':
1011             Props.setSeparate(1,'commandline')
1012         elif o == '--postscript' or o == '-P':
1013             Props.setPostscript(1,'commandline')
1014
1015     if len(files):
1016         for file in files:
1017             infile.open(file)
1018             type = infile.type()
1019             infile.close()
1020             if type == 'source':
1021                 cmd = 'lilypond %s %s 2>&1' % (getLilyopts(), file)
1022                 sys.stderr.write ('executing: %s\n'% cmd)
1023                 
1024                 fd = os.popen(cmd , 'r')
1025                 log = ''
1026                 
1027                 s = fd.readline()
1028                 while len(s) > 0:
1029                         sys.stderr.write (s)
1030                         sys.stderr.flush ()
1031                         log = log + s
1032                         s = fd.readline ()
1033                 if 0:
1034                         s = fd.read (1)
1035                         while len(s) > 0:
1036                                 sys.stderr.write (s)
1037                                 sys.stderr.flush ()
1038                                 s = fd.read (1)                 
1039                         log = log + s
1040                 stat = fd.close()
1041                 if stat:
1042                     sys.exit('ExitBadLily', cmd )
1043                 texFiles=getTeXFile(log)
1044                 writeLilylog(log)
1045                 Props.addLilyOutputFiles(texFiles,'program')
1046                 texInputFiles = texInputFiles + texFiles
1047             else:
1048                 texInputFiles.append(file)
1049
1050         firstfile=1
1051         for file in texInputFiles:
1052             infile.open(file)
1053             infile.setVars() # first pass set variables
1054             infile.close()
1055             if Props.get('debug'):
1056                 Props.printProps()
1057             if firstfile:
1058                 outfile.start(file)
1059             else:
1060                 outfile.next()
1061             outfile.write("""\
1062 \\input{%s}
1063 """ % (file))
1064             if Props.get('separate'):
1065                 outfile.end()
1066             else:
1067                 firstfile=0
1068         if not Props.get('separate'):
1069             outfile.end()
1070     else:
1071         help()
1072         sys.exit('ExitBadArgs','No files specified')
1073
1074 #
1075 # Exit values
1076 #
1077 ExitTable = {
1078     'ExitInterupt'         : ['Ouch!', 1 ],
1079     'ExitBadArgs'          : ['Wrong number of arguments', 2 ],
1080     'ExitNotFound'         : ['File not found', 3 ],
1081     'ExitBadPaper'         : ['Unknown papersize', 4 ],
1082     'ExitBadHeight'        : ['Invalid Height specification', 5 ],
1083     'ExitBadWidth'         : ['Invalid Width specification', 6 ],
1084     'ExitBadOrient'        : ['Invalid Orientation specification', 7 ],
1085     'ExitNoWrite'          : ['Permission denied', 8 ],
1086     'ExitNoTeXName'        : ['hmm, I could not find an output file name', 9 ],
1087     'ExitBadLily'          : ['Lilypond failed', 10 ],
1088     'ExitBadLatex'         : ['Latex failed', 11 ],
1089     'ExitBadPostscript'    : ['Postscript failed', 12 ],
1090     'ExitUnknown'          : ['Unknown Exit Code', 20 ],
1091     }
1092
1093 def cleanup():
1094     lilyfiles = []
1095     tmpfiles = []
1096     if not Props.get('keeplilypond'):
1097         lilyfiles = Props.get('lilyOutputFiles')
1098     if not Props.get('keeply2dvi'):
1099         tmpfiles = glob.glob('*.' + str(os.getpid()) + '.*' )
1100     for file in lilyfiles + tmpfiles:
1101         if os.path.isfile(file):
1102             os.remove(file)
1103
1104
1105 identify()
1106 Props = Properties()
1107
1108 try:
1109     main()
1110
1111 except KeyboardInterrupt:
1112     print ExitTable['ExitInterupt'][0]
1113     cleanup()
1114     sys.exit(ExitTable['ExitInterupt'][1])
1115
1116 except SystemExit, errno:
1117     if ExitTable.has_key(errno.args[0]):
1118         msg = ExitTable[errno.args[0]]
1119     else:
1120         msg = ExitTable['ExitUnknown']
1121     if len(errno.args) > 1:  
1122         sys.stderr.write( '%s: %s: %s\n' % (name, msg[0], errno.args[1]))
1123     else:
1124         sys.stderr.write( '%s %s\n' % (name, msg[0]))
1125     if Props.get('debug'):
1126         Props.printProps()
1127     cleanup()
1128     sys.exit(msg[1])
1129 else:
1130     cleanup()