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