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