]> git.donarmstrong.com Git - lilypond.git/blob - scripts/ly2dvi.py
patch::: 1.3.125.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 # usergenerated 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 if version == '@' + 'TOPLEVEL_VERSION' + '@':
29         version = '(unknown version)'           # uGUHGUHGHGUGH
30 errorlog = ''
31
32 import sys
33 import os
34 import getopt
35 import re
36 import string
37 import time
38 import glob
39 import tempfile
40
41 # Can't grep localized msgs
42 os.environ['LC_ALL'] = ''
43 os.environ['LANG'] = ''
44 os.environ['LC_LANG'] = ''
45
46
47
48 class Input:
49     """
50     This class handles all ly2dvi.py input file methods
51     
52     Public methods:
53     
54     __init__()  Constructor
55     open(file)  Open a .ly file or .tex file using lilyinclude path
56     close()     Close current file
57     type()      Determine file type .ly (input) or .tex (output)
58     setVars()   Set title definitions found in .tex (output) file
59     """
60
61     #
62     # Constructors
63     #
64
65     def __init__(this):
66        this.__fd = None 
67
68     #
69     # open
70     #
71     def open(this,file):
72         """
73         open file and set private class variable __fd.  The search
74         sequence is: current directory followed by the directories
75         found in include property list.  Each directory is searched
76         for file, file.ly, file.sly and file.fly.
77         
78         input:  file   filename
79         output: void
80         error:  ExitNotFound Exception
81         """
82
83         for i in [''] + Props.get('include')[0:]:
84             ifile = os.path.join(i,file)
85             for j in ['','.ly','.fly', '.sly']:
86                 jfile = ifile+j
87                 try:
88                     this.__fd = open( jfile, 'r' )
89                     return
90                 except:
91                     pass
92         sys.exit('ExitNotFound', file)
93
94
95     #
96     # close
97     #
98     def close(this):
99         """
100         close file object __fd
101         
102         input:  void
103         output: void
104         error:  None
105         """
106         this.__fd.close()
107
108
109     #
110     # type
111     #
112     def type(this):
113         """
114         Determine input file type.  LilyPond source is 'input' type
115         and LilyPond generated TeX file is 'output' type
116
117         input:  void
118         output: 'input' | 'output'
119         error:  None
120         """
121
122         firstline = this.__fd.readline()
123         this.__fd.seek(0)
124         if  re.match('%created by: GNU LilyPond [0-9]+[.0-9]+',firstline ):
125             return 'output'
126         else:
127             return 'source'
128
129
130     #
131     # setVars
132     #
133     def setVars(this):  
134         """
135         Search for properties in the current input file and set the
136         appropriate values.  The supported properties names are in
137         local variable varTable along with the property list
138         titledefs.
139
140         input:  void
141         output: None
142         error:  None
143         """
144
145         varTable = [
146             #   regexp              set method
147             #   ------              ----------
148             ( 'language',         Props.setLanguage ),
149             ( 'latexheaders',     Props.setHeader ),
150             ( 'paperorientation', Props.setOrientation ),
151             ( 'paperpapersize',   Props.setPaperZize ),
152             ( 'papertextheight',  Props.setTextHeight ),
153             ( 'paperlinewidth',   Props.setLineWidth ),
154             ( 'filename',         Props.setFilename ),
155             ]
156
157         titles={}
158         for line in this.__fd.readlines():
159             m=re.match('\\\\def\\\\lilypond([\w]+){(.*)}',line)
160             if m:
161                 for var in varTable:
162                     if m.group(1) == var[0]:
163                         var[1](m.group(2),'file')
164                         break
165                 for var in Props.get('titledefs'):
166                     if m.group(1) == var:
167                         titles[var]=m.group(2)
168                         break
169         Props.setTitles(titles,'file')
170         this.__fd.seek(0)
171
172 \f
173
174 class TeXOutput:
175     """
176     This class handles all ly2dvi.py output file methods
177
178     private methods:
179      __lilypondDefs(opt)  Send title info to output file
180
181     Public methods:
182     __init__()  Constructor
183     write(str)  Write a string to output file 
184     start(file) Start the latex file
185     next()      Process next output file
186     end()       Finish latex file and run latex 
187     """
188
189     #
190     # constructor
191     #
192     def __init__(this):
193        this.__fd = None 
194        this.__base = ''
195        this.__outfile = ''
196
197     #
198     # __medelaDefs
199     #
200     def __lilypondDefs(this,opt):
201         """
202         Write titles to output
203
204         input:  opt   Supports header and subheader output
205         output: None
206         error:  None
207         """
208
209         titles = Props.get('titles')
210         for key in titles.keys():
211             this.write('%s\\lilypond%s{%s}%%\n' % (opt,key,titles[key]))
212
213     #
214     # write
215     #
216     def write(this,str):
217         """
218         Write str to current output file
219
220         input:  str  String to write
221         output: None
222         error:  None
223         """
224         
225         this.__fd.write(str)
226
227     #
228     # start
229     #
230     def start(this,file):
231         """
232         Start LaTeX file. Sets the linewidth (and possibly the
233         textheight) and leaves the page layout to the geometry
234         package. Creates temporary output filename and opens it
235         for write. Sends the LaTeX header information to output.
236         Lastly sends the title information to output.
237
238         input:  file  output file name 
239         output: None
240         error:  None
241         """
242
243         now=time.asctime(time.localtime(time.time()))
244
245         # Only set the textheight if it was explicitly set by the user,
246         # otherwise use the default. Helps to handle landscape correctly!
247         if Props.get('textheight') > 0:
248             textheightsetting = ',textheight=' + `Props.get('textheight')` + 'pt'
249         else:
250             textheightsetting = ''
251
252
253         top= r"""
254 %% Creator: %s
255 %% Automatically generated from  %s, %s
256
257 \documentclass[%s]{article}
258
259 %s 
260 \usepackage{geometry}
261 \usepackage[latin1]{inputenc} 
262 %%\usepackage[T1]{fontenc} 
263 %%
264 %s 
265 %% don not waste unused space at bottom of page
266 %% (unless we have footnotes ...)
267 %%\headheight9pt
268 %%\headsep0pt
269 %% Maybe this is too drastic, but let us give it a try.
270 \geometry{width=%spt%s,headheight=2mm,headsep=0pt,footskip=2mm,%s} 
271 \input{titledefs}
272 %s
273 \makeatletter
274 \renewcommand{\@oddhead}{\parbox{\textwidth}%%
275     {\mbox{}\small\theheader\hfill\textbf{\thepage}}}%%
276 %% UGR.
277 %%\renewcommand{\@evenhead}{eve!{\small\lilypondinstrument{,}\quad\textbf{\thepage}}\hfil}%%
278 \renewcommand{\@oddfoot}{\parbox{\textwidth}{\mbox{}\thefooter}}%%
279 \begin{document}
280 """ % ( program_id(), Props.get('filename'), now, Props.get('papersize'),
281         Props.get('language'), Props.get('pagenumber'), Props.get('linewidth'),
282         textheightsetting, Props.get('orientation'), Props.get('header') )
283         
284         base, ext = os.path.splitext(file)
285         this.__base = base
286         tempfile.template= base + '_ly'
287         this.__outfile = tempfile.mktemp(ext)
288         base, ext = os.path.splitext(this.__outfile)
289         this.__tmpbase = base
290         try:
291             this.__fd = open(this.__outfile,"w")
292         except:
293             sys.exit('ExitNoWrite', this.__outfile)
294         this.write(top)
295         this.__lilypondDefs('')
296         this.write("""\
297 \\makelilytitle
298 """) 
299
300     #
301     # next
302     #
303     def next(this):
304         """
305         Write LaTeX subheader information to support more than one
306         score in a document.  Lastly send current title information to
307         output.
308
309         input:  None
310         output: None
311         error:  None
312         """
313
314         this.write("""\
315 \\def\\theopus{}%
316 \\def\\thepiece{}%
317 \\def\\lilypondopus{}%
318 \\def\\lilypondpiece{}%
319 """)
320         this.__lilypondDefs("\\def")
321         this.write("""\
322 \\def\\theopus{\\lilypondopus}% ugh
323 \\def\\thepiece{\\lilypondpiece}%
324 \\makelilypiecetitle
325 """)
326
327
328     #
329     # end
330     #
331     def end(this):
332         """
333         Close output file and run latex on it.
334
335         input:  None
336         output: None
337         error:  ExitBadLatex Exception
338         """
339
340         outfile=this.__base + '.dvi'
341         if Props.get('output') != '':
342             if not os.path.exists(Props.get('output')):
343                     os.mkdir(Props.get('output'))
344
345             outfile = os.path.join(Props.get('output'), outfile )
346             
347         this.write(r"""
348 %% \vfill\hfill{\lilypondtagline}
349 \makeatletter
350 \renewcommand{\@oddfoot}{\parbox{\textwidth}{\mbox{}\lilypondtagline}}%%
351 \makeatother
352 \end{document}
353 """)
354         this.__fd.close()
355         if os.path.isfile(outfile):
356             os.remove(outfile)
357         if ( os.name == 'posix' ):
358             stat = os.system('latex \'\\nonstopmode \\input %s\'' %
359                              (this.__outfile))
360         else: # Windows shells don't eat the single quotes
361             stat = os.system('latex \\nonstopmode \\input %s' %
362                              (this.__outfile))
363         if stat:
364             sys.exit('ExitBadLatex')
365         if not os.path.isfile(outfile):
366                 os.rename(this.__tmpbase + '.dvi', outfile)
367                 
368         sys.stderr.write('\n' + program_id() + ': dvi file name is %s\n\n'
369                          % (outfile))
370
371         if Props.get('postscript'):
372             dvipsopts=''
373             if Props.get('orientation') == 'landscape':
374                 dvipsopts=dvipsopts + ' -t landscape'
375             psoutfile=this.__base + '.ps'
376             if Props.get('output') != '':
377                 psoutfile = os.path.join(Props.get('output'), psoutfile )
378             stat = os.system('dvips %s -o %s %s' % (dvipsopts,psoutfile,outfile))
379             if stat:
380                 sys.exit('ExitBadPostscript')
381             
382
383 \f
384
385
386 # ARG! THIS CODE IS BLOATED:
387 # FIXME: Junk all set/get methods.
388
389 class Properties:
390     """
391     This class handles all ly2dvi.py property manipulation
392
393     Public methods:
394     
395     __init__()  Constructor
396     set<property> methods
397     """
398
399     def __init__(this):
400
401         #
402         # Following is the order of priority for property assignment.  The
403         # list is organized from lowest to highest priority.  Each
404         # assignment is overridden by the next requester in the list.
405         #
406         # Requester     Description
407         # ---------     -----------
408         # init          Initial default values
409         # file          The values found in the LilyPond generated TeX files
410         # environment   Envrionment variables LILYINCLUDE, LILYPONDPREFIX
411         # rcfile        $LILYPONDPREFIX/.lilyrc
412         # rcfile        $HOME/.lilyrc
413         # rcfile        ./.lilyrc
414         # commandline   command line arguments
415         # 
416         this.__overrideTable = {
417             'init'        : 0,
418             'file'        : 1,
419             'environment' : 2,
420             'rcfile'      : 3,
421             'commandline' : 4,
422             'program'     : 5
423             }
424
425         this.__roverrideTable = {} # reverse lookup used for debug
426         for i in this.__overrideTable.items():
427             this.__roverrideTable[i[1]]=i[0]
428         
429         this.__data = {
430             'papersize'    :  ['a4paper', this.__overrideTable['init']],
431             'textheight'   :  [0, this.__overrideTable['init']],
432             'linewidth'    :  [500, this.__overrideTable['init']],
433             'orientation'  :  ['portrait', this.__overrideTable['init']],
434             'language'     :  ['%', this.__overrideTable['init']],
435             'include'      :  [[], this.__overrideTable['init']],
436             'debug'        :  [0, this.__overrideTable['init']],
437             'keeplilypond' :  [0, this.__overrideTable['init']],
438             'keeply2dvi'   :  [0, this.__overrideTable['init']],
439             'pagenumber'   :  ['%', this.__overrideTable['init']],
440             'separate'     :  [0, this.__overrideTable['init']],
441             'output'       :  ['', this.__overrideTable['init']],
442             'header'       :  ['%', this.__overrideTable['init']],
443             'dependencies' :  [0, this.__overrideTable['init']],
444             'root'         :  ['', this.__overrideTable['init']],
445             'tmp'          :  ['d:\tmp', this.__overrideTable['init']],
446             'filename'     :  ['', this.__overrideTable['init']],
447             'titledefs'    :  [[], this.__overrideTable['init']],
448             'titles'       :  [{}, this.__overrideTable['init']],
449             'lilyOutputFiles' :  [[], this.__overrideTable['init']],
450             'postscript'   :  [0, this.__overrideTable['init']],
451             }
452
453         #
454         # Try to set root and HOME first before calling rcfile
455         #
456         if os.environ.has_key('LILYPONDPREFIX'):
457             this.setRoot(os.environ['LILYPONDPREFIX'], 'environment')
458         else:
459             p=os.path.split(sys.argv[0])
460             p=os.path.split(p[0])
461             # bit silly. for ly2dvi, overrules compiled-in datadir...
462             # how to do this better (without running lily, of course?
463             this.setRoot(os.path.join(p[0],'share','lilypond'), 'init')
464
465         if not os.environ.has_key('HOME'):
466             if os.environ.has_key('HOMEDRIVE') and \
467                  os.environ.has_key('HOMEPATH'):
468                 os.environ['HOME'] = os.environ['HOMEDRIVE'] + \
469                                      os.environ['HOMEPATH']
470             else:
471                 os.environ['HOME'] = os.curdir
472
473         this.rcfile() # Read initialization file(s)
474
475         if os.environ.has_key('LILYINCLUDE'):
476             tmp=this.get('include')
477             for s in string.split(os.environ['LILYINCLUDE'],os.pathsep):
478                 tmp.append(s)
479             this.__set('include', tmp, 'environment')    
480
481
482         t= os.pathsep
483         if os.environ.has_key ('TEXINPUTS'):
484                 t = os.environ['TEXINPUTS'] + os.pathsep
485         os.environ['TEXINPUTS'] = t + \
486         os.path.join(this.get('root'), 'tex' ) + \
487         os.pathsep + os.path.join(this.get('root'), 'ps' )
488
489         t=''
490         if os.environ.has_key ('MFINPUTS'):
491                t = os.environ['MFINPUTS'] 
492         os.environ['MFINPUTS'] = t + os.pathsep + \
493                                  os.path.join(this.get('root'), 'mf')
494
495         if os.environ.has_key('TMP'):
496             this.__set('tmp',os.environ['TMP'],'environment')
497
498
499     def read_titledefs (this):
500         fd=this.get_texfile_path ('titledefs.tex')
501         mudefs=[]    
502
503         for line in fd.readlines():
504             m=re.match('\\\\newcommand\*{\\\\lilypond([\w]+)}',line)
505             if m:
506                 mudefs.append(m.group(1))
507         fd.close
508         this.__set('titledefs', mudefs, 'init')
509
510     #
511     # __set
512     #
513     def __set(this,var,value,requester):
514         """
515         All of the set methods call this to set a property.  If the value
516         was last set by a requestor of lesser priority the new value is
517         assigned, else the old value has priority and is unchanged.
518         """
519
520         if this.__overrideTable[requester] < this.__data[var][1]:
521             return 0
522         else:
523             this.__data[var] = [value, this.__overrideTable[requester]]
524
525     #
526     # get
527     #
528     def get(this,var):
529         """
530         All of the get methods call this to get a property value.  List
531         variable types are return by value to facilitate an append operation.
532         """
533
534         if var == 'include' or var == 'lilyOutputFiles':
535             return this.__data[var][0][0:]  # return a copy not a ref
536         else:
537             return this.__data[var][0]
538
539     #
540     # get_texfile_path
541     #
542     def get_texfile_path (this, var):
543         """
544         locate and open titledefs.tex file
545         """
546
547         if os.name == 'nt':
548             path = os.path.join(this.get('root'), 'tex', var)
549         else:
550             path =''
551             cmd =('kpsewhich tex %s %s' % (var,errorlog))
552             pipe = os.popen (cmd, 'r')
553             path = pipe.readline ()[:-1] # chop off \n
554             return_status =  pipe.close()
555             if return_status and not path:
556                 path = os.path.join(this.get('root'), 'tex', var)
557         fd = open(path, 'r')
558         return fd
559
560
561     #
562     # Read rc file
563     #
564     def rcfile(this):
565         """
566         Read initialization file(s)
567         """
568         varTable = [
569             #   name              set method
570             #   ----              ----------
571             ( 'DEBUG',          this.setDebug ),
572             ( 'DEPENDENCIES',   this.setDependencies ),
573             ( 'KEEPLILYPOND',   this.setKeeplilypond ),
574             ( 'KEEPLY2DVI',     this.setKeeply2dvi ),
575             ( 'LANGUAGE',       this.setLanguage ),
576             ( 'LATEXHF',        this.setHeader ),
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 + '}',requester)
826
827     #
828     # Set or Clear Dependencies flag to generate makefile dependencies
829     #
830     def setDependencies(this, value, requester):        
831         """
832         Set or Clear dependencies flag
833         """
834
835         if int(value) == 1:
836             this.__set('dependencies',1,requester)
837         else:
838             this.__set('dependencies',0,requester)
839
840     #
841     # Set tmp directory
842     #
843     def setTmp(this,dir, requester):    
844         this.__set('tmp',dir,requester)
845
846     #
847     # Set lilypond source file name
848     #
849     def setFilename(this,file, requester):      
850         this.__set('filename',file,requester)
851
852     #
853     # Set title commands
854     #
855     def setTitles(this,titles, requester):      
856         this.__set('titles',titles,requester)
857
858     #
859     # Set title commands
860     #
861     def addLilyOutputFiles(this,filelist,requester):
862         """
863         Add a to the lily output list
864         """
865
866         tmp = this.get('lilyOutputFiles')
867         tmp = tmp + filelist
868         this.__set('lilyOutputFiles',tmp,requester)
869
870     #
871     # Set/Clear postscript flag
872     #
873     def setPostscript(this,value,requester):
874         """
875         Set postscript flag
876         """
877
878         if int(value) == 1:
879             this.__set('postscript',1,requester)
880         else:
881             this.__set('postscript',0,requester)
882
883     #
884     # Set root
885     #
886     def setRoot(this,path, requester):  
887         """
888         Set LilyPond root directory
889         """
890
891         os.environ['LILYPONDPREFIX'] = path
892         if os.name == 'nt' or os.name == 'dos':
893             path = unc2dos(path);
894
895         this.__set('root',path,requester)
896         
897
898     #
899     # printProps
900     #
901     def printProps(this):
902         """
903         Print properties
904         """
905         
906         for key in this.__data.keys():
907             print "%s <%s>:<%s>" % (key,this.get(key),
908                                     this.__roverrideTable[this.__data[key][1]])
909
910 \f
911
912 #
913 # Misc functions
914 #
915
916 def getLilyopts():
917     inc = ''    
918     if len(Props.get('include')) > 0: 
919         inc = string.join (map (lambda x: '-I "%s"' % x, Props.get('include')))
920     else:
921
922         if Props.get('dependencies'):
923             dep=' -M'
924         else:
925             dep=''
926         return inc + dep
927     return inc
928
929 def writeLilylog(file,contents):
930     if Props.get('keeplilypond'):
931         base, ext = os.path.splitext(file)
932         tempfile.template=base + "_li"
933         file=tempfile.mktemp('.log')
934         output = Props.get('output')
935         if output != '':
936             file = os.path.join( output, file )
937         try:
938             fd = open( file, 'w' )
939         except:
940             sys.exit('ExitNoWrite', file)
941         fd.write(contents)
942         fd.close()
943
944 def getTeXFile(contents):
945     texfiles=[]
946     for line in string.split(contents,'\n'):
947         m = re.search('paper output to (.+)\.\.\.', line)
948         if m:
949             texfiles.append(m.group(1))
950
951     if texfiles == []:
952         sys.exit('ExitNoTeXName')
953     else:
954         return texfiles
955
956 def unc2dos(path):
957     """
958     Convert a path of format //<drive>/this/that/the/other to
959     <drive>:\this\that\the\other
960     """
961     m=re.match('^//([A-Za-z])(/.*)$',path)
962     if m:
963         return m.group(1) + ':' + os.path.normpath(m.group(2))
964     
965     
966
967 def program_id ():
968     return 'ly2dvi (GNU LilyPond) ' + version;
969
970
971 def mailaddress():
972     try:
973         return os.environ['MAILADDRESS']
974     except KeyError:
975         return '(address unknown)'
976
977
978 def identify ():
979     sys.stderr.write (program_id () + '\n')
980
981 def print_version ():
982     sys.stdout.write (program_id () + '\n')
983
984 def help ():
985     sys.stdout.write (
986 """Usage: %s [OPTION]... [FILE]...
987
988 Generate dvi file from LilyPond source/output
989
990 Options:
991   -D,--debug           increase verbosity
992   -F,--headers=        name of additional LaTeX headers file
993   -H,--Height=         set paper height (points) (see manual page)
994   -I,--include=DIR     add DIR to LilyPond\'s search path
995   -K,--keeplilypond    keep LilyPond output files
996   -L,--landscape       set landscape orientation
997   -N,--nonumber        switch off page numbering
998   -O,--orientation=    set orientation (obsolete -- use -L instead)
999   -P,--postscript      generate PostScript file
1000   -W,--Width=          set paper width (points) (see manual page)
1001   -M,--dependencies    tell LilyPond to make a dependencies file
1002   -h,--help            this help text
1003   -k,--keeply2dvi      keep ly2dvi output files
1004   -l,--language=       give LaTeX language (babel)
1005   -o,--output=         set output directory
1006   -p,--papersize=      give LaTeX papersize (eg. a4)
1007   -s,--separate        run all files separately through LaTeX
1008
1009 files may be (a mix of) input to or output from LilyPond(1)
1010 """ % name)
1011
1012 \f
1013
1014 #
1015 # main
1016 #
1017
1018 def main():
1019     """Generate dvi files from LilyPond source/output"""
1020
1021     infile = Input()
1022     outfile = TeXOutput()
1023     texInputFiles=[]
1024     tempfile.tempdir=""
1025
1026     (options, files) = getopt.getopt (sys.argv[1:],
1027                                       'DF:H:I:KLNPW:Mhkl:o:p:s',
1028                                       ['debug', 'headers=', 'Height=',
1029                                        'include=', 'keeplilypond', 'landscape',
1030                                        'nonumber', 'Width=', 'dependencies',
1031                                        'help', 'keeply2dvi', 'language=',
1032                                        'output=', 'version', 'papersize=', 'separate',
1033                                        'postscript'])
1034
1035     for opt in options:
1036         o = opt[0]
1037         a = opt[1]
1038         if o == '--debug' or o == '-D':
1039             Props.setDebug(1,'commandline')
1040         elif o == '--headers' or o == '-F':
1041             Props.setHeader(a,'commandline')
1042         elif o == '--include' or o == '-I':
1043             Props.setInclude(a,'commandline')
1044         elif o == '--Height' or o == '-H':
1045             Props.setTextHeight(a,'commandline')
1046         elif o == '--keeplilypond' or o == '-K':
1047             Props.setKeeplilypond(1,'commandline')
1048         elif o == '--landscape' or o == '-L':
1049             Props.setOrientation('landscape','commandline')
1050         elif o == '--nonumber' or o == '-N':
1051             Props.setNonumber(1,'commandline')
1052         elif o == '--Width' or o == '-W':
1053             Props.setLineWidth(a,'commandline')
1054         elif o == '--dependencies' or o == '-M':
1055             Props.setDependencies(1,'commandline')
1056         elif o == '--help' or o == '-h':
1057             help()
1058             sys.exit (0)
1059         elif o == '--keeply2dvi' or o == '-k':
1060             Props.setKeeply2dvi(1,'commandline')
1061         elif o == '--language' or o == '-l':
1062             Props.setLanguage(a,'commandline')
1063         elif o == '--output' or o == '-o':
1064             Props.setOutput(a,'commandline')
1065         elif o == '--papersize' or o == '-p':
1066             Props.setPaperZize(a,'commandline')
1067         elif o == '--separate' or o == '-s':
1068             Props.setSeparate(1,'commandline')
1069         elif o == '--postscript' or o == '-P':
1070             Props.setPostscript(1,'commandline')
1071         elif o == '--version':
1072             print_version ()
1073             return 0
1074         else:
1075             print o
1076             raise getopt.error
1077             
1078     identify()
1079     Props.read_titledefs ()
1080     
1081     if len(files):
1082         for file in files:
1083             infile.open(file)
1084             type = infile.type()
1085             infile.close()
1086             if type == 'source':
1087                 if os.environ.has_key('OS') and \
1088                    os.environ['OS'] == 'Windows_95':
1089                     cmd = 'ash -c "lilypond %s %s 2>&1"' %(getLilyopts(), file)
1090                 else:
1091                     cmd = 'lilypond %s %s 2>&1' % (getLilyopts(), file)
1092                 sys.stderr.write ('executing: %s\n'% cmd)
1093                 
1094                 fd = os.popen(cmd , 'r')
1095                 log = ''
1096                 
1097                 s = fd.readline()
1098                 while len(s) > 0:
1099                         sys.stderr.write (s)
1100                         sys.stderr.flush ()
1101                         log = log + s
1102                         s = fd.readline ()
1103                 if 0:
1104                         s = fd.read (1)
1105                         while len(s) > 0:
1106                                 sys.stderr.write (s)
1107                                 sys.stderr.flush ()
1108                                 s = fd.read (1)                 
1109                         log = log + s
1110                 stat = fd.close()
1111                 if stat:
1112                     sys.exit('ExitBadLily', cmd )
1113                 texFiles=getTeXFile(log)
1114                 writeLilylog(file,log)
1115                 Props.addLilyOutputFiles(texFiles,'program')
1116                 texInputFiles = texInputFiles + texFiles
1117             else:
1118                 texInputFiles.append(file)
1119
1120         firstfile=1
1121         for file in texInputFiles:
1122             infile.open(file)
1123             infile.setVars() # first pass set variables
1124             infile.close()
1125             if Props.get('debug'):
1126                 Props.printProps()
1127             if firstfile:
1128                 outfile.start(file)  # allow for specified name
1129             else:
1130                 outfile.next()
1131             outfile.write("""\
1132 \\input{%s}
1133 """ % (file))
1134             if Props.get('separate'):
1135                 outfile.end()
1136             else:
1137                 firstfile=0
1138         if not Props.get('separate'):
1139             outfile.end()
1140     else:
1141         help()
1142         sys.exit('ExitBadArgs','No files specified')
1143
1144 #
1145 # Exit values
1146 #
1147 ExitTable = {
1148     'ExitInterupt'         : ['Ouch!', 1 ],
1149     'ExitBadArgs'          : ['Wrong number of arguments', 2 ],
1150     'ExitNotFound'         : ['File not found', 3 ],
1151     'ExitBadPaper'         : ['Unknown papersize', 4 ],
1152     'ExitBadHeight'        : ['Invalid Height specification', 5 ],
1153     'ExitBadWidth'         : ['Invalid Width specification', 6 ],
1154     'ExitBadOrient'        : ['Invalid Orientation specification', 7 ],
1155     'ExitNoWrite'          : ['Permission denied', 8 ],
1156     'ExitNoTeXName'        : ['Hmm, I could not find an output file name', 9 ],
1157     'ExitBadLily'          : ['LilyPond failed', 10 ],
1158     'ExitBadLatex'         : ['Latex failed', 11 ],
1159     'ExitBadPostscript'    : ['Postscript failed', 12 ],
1160     'ExitUnknown'          : ['Unknown Exit Code', 20 ],
1161     }
1162
1163 def cleanup():
1164     lilyfiles = []
1165     tmpfiles = []
1166     if not Props.get('keeplilypond'):
1167         lilyfiles = Props.get('lilyOutputFiles')
1168     if not Props.get('keeply2dvi'):
1169         tmpfiles = glob.glob('*_ly[0-9]*.*')
1170     for file in lilyfiles + tmpfiles:
1171         if os.path.isfile(file):
1172             os.remove(file)
1173
1174
1175 Props = Properties()
1176
1177 try:
1178     main()
1179
1180 except KeyboardInterrupt:
1181     print ExitTable['ExitInterupt'][0]
1182     cleanup()
1183     sys.exit(ExitTable['ExitInterupt'][1])
1184
1185 except SystemExit, errno:
1186     if ExitTable.has_key(errno.args[0]):
1187         msg = ExitTable[errno.args[0]]
1188     else:
1189         msg = ExitTable['ExitUnknown']
1190     if len(errno.args) > 1:  
1191         sys.stderr.write( '%s: %s: %s\n' % (name, msg[0], errno.args[1]))
1192     else:
1193         sys.stderr.write( '%s %s\n' % (name, msg[0]))
1194     if Props.get('debug'):
1195         Props.printProps()
1196     cleanup()
1197     sys.exit(msg[1])
1198 else:
1199     cleanup()