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