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