]> git.lyx.org Git - lyx.git/blob - lib/configure.py
es.po: updates by Ignacio
[lyx.git] / lib / configure.py
1 #! /usr/bin/env python
2 # -*- coding: utf-8 -*-
3 #
4 # file configure.py
5 # This file is part of LyX, the document processor.
6 # Licence details can be found in the file COPYING.
7
8 # \author Bo Peng
9 # Full author contact details are available in file CREDITS.
10
11 import sys, os, re, shutil, glob
12
13
14 class Tee:
15     ''' Writing to a Tee object will write to all file objects it keeps.
16         That is to say, writing to Tee(sys.stdout, open(logfile, 'w')) will
17         write to sys.stdout as well as a log file.
18     '''
19     def __init__(self, *args):
20         self.files = args
21
22     def write(self, data):
23         for f in self.files:
24             result = f.write(data)
25         return result
26
27     def writelines(self, seq):
28         for i in seq:
29             self.write(i)
30
31
32 def writeToFile(filename, lines, append = False):
33     " utility function: write or append lines to filename "
34     if append:
35         file = open(filename, 'a')
36     else:
37         file = open(filename, 'w')
38     file.write(lines)
39     file.close()
40
41
42 def addToRC(lines):
43     ''' utility function: shortcut for appending lines to outfile
44         add newline at the end of lines.
45     '''
46     if lines.strip() != '':
47         writeToFile(outfile, lines + '\n', append = True)
48
49
50 def removeFiles(filenames):
51     '''utility function: 'rm -f'
52         ignore errors when file does not exist, or is a directory.
53     '''
54     for file in filenames:
55         try:
56             os.remove(file)
57         except:
58             pass
59
60
61 def cmdOutput(cmd):
62     '''utility function: run a command and get its output as a string
63         cmd: command to run
64     '''
65     fout = os.popen(cmd)
66     output = fout.read()
67     fout.close()
68     return output.strip()
69
70
71 def setEnviron():
72     ''' I do not really know why this is useful, but we might as well keep it.
73         NLS nuisances.
74         Only set these to C if already set.  These must not be set unconditionally
75         because not all systems understand e.g. LANG=C (notably SCO).
76         Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
77         Non-C LC_CTYPE values break the ctype check.
78     '''
79     os.environ['LANG'] = os.getenv('LANG', 'C')
80     os.environ['LC'] = os.getenv('LC_ALL', 'C')
81     os.environ['LC_MESSAGE'] = os.getenv('LC_MESSAGE', 'C')
82     os.environ['LC_CTYPE'] = os.getenv('LC_CTYPE', 'C')
83
84
85 def createDirectories():
86     ''' Create the build directories if necessary '''
87     for dir in ['bind', 'clipart', 'doc', 'examples', 'images', 'kbd', \
88         'layouts', 'scripts', 'templates', 'ui' ]:
89         if not os.path.isdir( dir ):
90             try:
91                 os.mkdir( dir)
92             except:
93                 print "Failed to create directory ", dir
94                 sys.exit(1)
95
96
97 def checkTeXPaths():
98     ''' Determine the path-style needed by the TeX engine on Win32 (Cygwin) '''
99     windows_style_tex_paths = ''
100     if os.name == 'nt' or sys.platform == 'cygwin':
101         from tempfile import mkstemp
102         fd, tmpfname = mkstemp(suffix='.ltx')
103         if os.name == 'nt':
104             inpname = tmpfname.replace('\\', '/')
105         else:
106             inpname = cmdOutput('cygpath -m ' + tmpfname)
107         logname = os.path.basename(inpname.replace('.ltx', '.log'))
108         inpname = inpname.replace('~', '\\string~')
109         os.write(fd, r'\relax')
110         os.close(fd)
111         latex_out = cmdOutput(r'latex "\nonstopmode\input{%s}"' % inpname)
112         if 'Error' in latex_out:
113             print "configure: TeX engine needs posix-style paths in latex files"
114             windows_style_tex_paths = 'false'
115         else:
116             print "configure: TeX engine needs windows-style paths in latex files"
117             windows_style_tex_paths = 'true'
118         removeFiles([tmpfname, logname, 'texput.log'])
119     return windows_style_tex_paths
120
121
122 ## Searching some useful programs
123 def checkProg(description, progs, rc_entry = [], path = [], not_found = ''):
124     '''
125         This function will search a program in $PATH plus given path
126         If found, return directory and program name (not the options).
127
128         description: description of the program
129
130         progs: check programs, for each prog, the first word is used
131             for searching but the whole string is used to replace
132             %% for a rc_entry. So, feel free to add '$$i' etc for programs.
133
134         path: additional pathes
135
136         rc_entry: entry to outfile, can be
137             1. emtpy: no rc entry will be added
138             2. one pattern: %% will be replaced by the first found program,
139                 or '' if no program is found.
140             3. several patterns for each prog and not_found. This is used 
141                 when different programs have different usages. If you do not 
142                 want not_found entry to be added to the RC file, you can specify 
143                 an entry for each prog and use '' for the not_found entry.
144
145         not_found: the value that should be used instead of '' if no program
146             was found
147
148     '''
149     # one rc entry for each progs plus not_found entry
150     if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1:
151         print "rc entry should have one item or item for each prog and not_found."
152         sys.exit(2)
153     print 'checking for ' + description + '...'
154     ## print '(' + ','.join(progs) + ')',
155     for idx in range(len(progs)):
156         # ac_prog may have options, ac_word is the command name
157         ac_prog = progs[idx]
158         ac_word = ac_prog.split(' ')[0]
159         print '+checking for "' + ac_word + '"... ',
160         path = os.environ["PATH"].split(os.pathsep) + path
161         extlist = ['']
162         if os.environ.has_key("PATHEXT"):
163             extlist = extlist + os.environ["PATHEXT"].split(os.pathsep)
164         for ac_dir in path:
165             for ext in extlist:
166                 if os.path.isfile( os.path.join(ac_dir, ac_word + ext) ):
167                     print ' yes'
168                     # write rc entries for this command
169                     if len(rc_entry) == 1:
170                         addToRC(rc_entry[0].replace('%%', ac_prog))
171                     elif len(rc_entry) > 1:
172                         addToRC(rc_entry[idx].replace('%%', ac_prog))
173                     return [ac_dir, ac_word]
174         # if not successful
175         print ' no'
176     # write rc entries for 'not found'
177     if len(rc_entry) > 0:  # the last one.
178         addToRC(rc_entry[-1].replace('%%', not_found))
179     return ['', not_found]
180
181
182 def checkViewer(description, progs, rc_entry = [], path = []):
183     ''' The same as checkProg, but for viewers and editors '''
184     return checkProg(description, progs, rc_entry, path, not_found = 'auto')
185
186
187 def checkDTLtools():
188     ''' Check whether DTL tools are available (Windows only) '''
189     # Find programs! Returned path is not used now
190     if ((os.name == 'nt' or sys.platform == 'cygwin') and
191             checkProg('DVI to DTL converter', ['dv2dt']) != ['', ''] and
192             checkProg('DTL to DVI converter', ['dt2dv']) != ['', '']):
193         dtl_tools = True
194     else:
195         dtl_tools = False
196     return dtl_tools
197
198
199 def checkLatex(dtl_tools):
200     ''' Check latex, return lyx_check_config '''
201     path, LATEX = checkProg('a Latex2e program', ['latex $$i', 'platex $$i', 'latex2e $$i'])
202     path, PPLATEX = checkProg('a DVI postprocessing program', ['pplatex $$i'])
203     #-----------------------------------------------------------------
204     path, PLATEX = checkProg('pLaTeX, the Japanese LaTeX', ['platex $$i'])
205     # check if PLATEX is pLaTeX2e
206     writeToFile('chklatex.ltx', '''
207 \\nonstopmode
208 \\@@end
209 ''')
210     # run platex on chklatex.ltx and check result
211     if cmdOutput(PLATEX + ' chklatex.ltx').find('pLaTeX2e') != -1:
212         # We have the Japanese pLaTeX2e
213         addToRC(r'\converter platex   dvi       "%s"   "latex"' % PLATEX)
214         LATEX = PLATEX
215     else:
216         PLATEX = ''
217     removeFiles(['chklatex.ltx', 'chklatex.log'])
218     #-----------------------------------------------------------------
219     # use LATEX to convert from latex to dvi if PPLATEX is not available    
220     if PPLATEX == '':
221         PPLATEX = LATEX
222     if dtl_tools:
223         # Windows only: DraftDVI
224         addToRC(r'''\converter latex      dvi2       "%s"       "latex"
225 \converter dvi2       dvi        "python -tt $$s/scripts/clean_dvi.py $$i $$o"  ""''' % PPLATEX)
226     else:
227         addToRC(r'\converter latex      dvi        "%s" "latex"' % PPLATEX)
228     # no latex
229     if LATEX != '':
230         # Check if latex is usable
231         writeToFile('chklatex.ltx', '''
232 \\nonstopmode\\makeatletter
233 \\ifx\\undefined\\documentclass\\else
234   \\message{ThisIsLaTeX2e}
235 \\fi
236 \\@@end
237 ''')
238         # run latex on chklatex.ltx and check result
239         if cmdOutput(LATEX + ' chklatex.ltx').find('ThisIsLaTeX2e') != -1:
240             # valid latex2e
241             return LATEX
242         else:
243             print "Latex not usable (not LaTeX2e) "
244         # remove temporary files
245         removeFiles(['chklatex.ltx', 'chklatex.log'])
246     return ''
247
248
249 def checkFormatEntries(dtl_tools):  
250     ''' Check all formats (\Format entries) '''
251     checkViewer('a Tgif viewer and editor', ['tgif'],
252         rc_entry = [r'\Format tgif       obj     Tgif                   "" "%%" "%%"    "vector"'])
253     #
254     checkViewer('a FIG viewer and editor', ['xfig', 'jfig3-itext.jar', 'jfig3.jar'],
255         rc_entry = [r'\Format fig        fig     FIG                    "" "%%" "%%"    "vector"'])
256     #
257     checkViewer('a Grace viewer and editor', ['xmgrace'],
258         rc_entry = [r'\Format agr        agr     Grace                  "" "%%" "%%"    "vector"'])
259     #
260     checkViewer('a FEN viewer and editor', ['xboard -lpf $$i -mode EditPosition'],
261         rc_entry = [r'\Format fen        fen     FEN                    "" "%%" "%%"    ""'])
262     #
263     path, iv = checkViewer('a raster image viewer', ['xv', 'kview', 'gimp-remote', 'gimp'])
264     path, ie = checkViewer('a raster image editor', ['gimp-remote', 'gimp'])
265     addToRC(r'''\Format bmp        bmp     BMP                    "" "%s"       "%s"    ""
266 \Format gif        gif     GIF                    "" "%s"       "%s"    ""
267 \Format jpg        jpg     JPEG                   "" "%s"       "%s"    ""
268 \Format pbm        pbm     PBM                    "" "%s"       "%s"    ""
269 \Format pgm        pgm     PGM                    "" "%s"       "%s"    ""
270 \Format png        png     PNG                    "" "%s"       "%s"    ""
271 \Format ppm        ppm     PPM                    "" "%s"       "%s"    ""
272 \Format tiff       tif     TIFF                   "" "%s"       "%s"    ""
273 \Format xbm        xbm     XBM                    "" "%s"       "%s"    ""
274 \Format xpm        xpm     XPM                    "" "%s"       "%s"    ""''' % \
275         (iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie) )
276     #
277     checkViewer('a text editor', ['sensible-editor', 'xemacs', 'gvim', 'kedit', 'kwrite', 'kate', \
278         'nedit', 'gedit', 'notepad'],
279         rc_entry = [r'''\Format asciichess asc    "Plain text (chess output)"  "" ""    "%%"    ""
280 \Format asciiimage asc    "Plain text (image)"         "" ""    "%%"    ""
281 \Format asciixfig  asc    "Plain text (Xfig output)"   "" ""    "%%"    ""
282 \Format dateout    tmp    "date (output)"         "" "" "%%"    ""
283 \Format docbook    sgml    DocBook                B  "" "%%"    "document"
284 \Format docbook-xml xml   "Docbook (XML)"         "" "" "%%"    "document"
285 \Format dot        dot    "Graphviz Dot"          "" "" "%%"    "vector"
286 \Format platex     tex    "LaTeX (pLaTeX)"        "" "" "%%"    "document"
287 \Format literate   nw      NoWeb                  N  "" "%%"    "document"
288 \Format lilypond   ly     "LilyPond music"        "" "" "%%"    "vector"
289 \Format latex      tex    "LaTeX (plain)"         L  "" "%%"    "document"
290 \Format linuxdoc   sgml    LinuxDoc               x  "" "%%"    "document"
291 \Format pdflatex   tex    "LaTeX (pdflatex)"      "" "" "%%"    "document"
292 \Format text       txt    "Plain text"            a  "" "%%"    "document"
293 \Format text2      txt    "Plain text (pstotext)" "" "" "%%"    "document"
294 \Format text3      txt    "Plain text (ps2ascii)" "" "" "%%"    "document"
295 \Format text4      txt    "Plain text (catdvi)"   "" "" "%%"    "document"
296 \Format textparagraph txt "Plain Text, Join Lines" "" ""        "%%"    "document"''' ])
297  #
298     checkViewer('a BibTeX editor', ['sensible-editor', 'jabref', 'JabRef', \
299         'pybliographic', 'bibdesk', 'gbib', 'kbib', \
300         'kbibtex', 'sixpack', 'bibedit', 'tkbibtex' \
301         'xemacs', 'gvim', 'kedit', 'kwrite', 'kate', \
302         'nedit', 'gedit', 'notepad'],
303         rc_entry = [r'''\Format bibtex bib    "BibTeX"         "" ""    "%%"    ""''' ])
304     #
305     #checkProg('a Postscript interpreter', ['gs'],
306     #  rc_entry = [ r'\ps_command "%%"' ])
307     checkViewer('a Postscript previewer', ['kghostview', 'evince', 'gv', 'ghostview -swap'],
308         rc_entry = [r'''\Format eps        eps     EPS                    "" "%%"       ""      "vector"
309 \Format ps         ps      Postscript             t  "%%"       ""      "document,vector"'''])
310     #
311     checkViewer('a PDF previewer', ['kpdf', 'evince', 'kghostview', 'xpdf', 'acrobat', 'acroread', \
312                     'gv', 'ghostview'],
313         rc_entry = [r'''\Format pdf        pdf    "PDF (ps2pdf)"          P  "%%"       ""      "document,vector"
314 \Format pdf2       pdf    "PDF (pdflatex)"        F  "%%"       ""      "document,vector"
315 \Format pdf3       pdf    "PDF (dvipdfm)"         m  "%%"       ""      "document,vector"'''])
316     #
317     checkViewer('a DVI previewer', ['xdvi', 'kdvi'],
318         rc_entry = [r'\Format dvi        dvi     DVI                    D  "%%" ""      "document,vector"'])
319     if dtl_tools:
320         # Windows only: DraftDVI
321         addToRC(r'\Format dvi2       dvi     DraftDVI               ""  ""      ""      "vector"')
322     #
323     checkViewer('an HTML previewer', ['firefox', 'mozilla file://$$p$$i', 'netscape'],
324         rc_entry = [r'\Format html       html    HTML                   H  "%%" ""      "document"'])
325     #
326     checkViewer('Noteedit', ['noteedit'],
327         rc_entry = [r'\Format noteedit   not     Noteedit               "" "%%" "%%"    "vector"'])
328     #
329     checkViewer('an OpenDocument viewer', ['swriter', 'oowriter'],
330         rc_entry = [r'\Format odt        odt     OpenDocument           "" "%%" "%%"    "document,vector"'])
331     #
332     # entried that do not need checkProg
333     addToRC(r'''\Format date       ""     "date command"          "" "" ""      ""
334 \Format csv        csv    "Table (CSV)"  "" ""  ""      "document"
335 \Format fax        ""      Fax                    "" "" ""      "document"
336 \Format lyx        lyx     LyX                    "" "" ""      ""
337 \Format lyx13x     lyx13  "LyX 1.3.x"             "" "" ""      "document"
338 \Format lyx14x     lyx14  "LyX 1.4.x"             "" "" ""      "document"
339 \Format lyx15x     lyx15  "LyX 1.5.x"             "" "" ""      "document"
340 \Format clyx       cjklyx "CJK LyX 1.4.x (big5)"  "" "" ""      "document"
341 \Format jlyx       cjklyx "CJK LyX 1.4.x (euc-jp)" "" ""        ""      "document"
342 \Format klyx       cjklyx "CJK LyX 1.4.x (euc-kr)" "" ""        ""      "document"
343 \Format lyxpreview lyxpreview "LyX Preview"       "" "" ""      ""
344 \Format lyxpreview-platex lyxpreview-platex "LyX Preview (pLaTeX)"       "" ""  ""      ""
345 \Format pdftex     pdftex_t PDFTEX                "" "" ""      ""
346 \Format program    ""      Program                "" "" ""      ""
347 \Format pstex      pstex_t PSTEX                  "" "" ""      ""
348 \Format rtf        rtf    "Rich Text Format"      "" "" ""      "document,vector"
349 \Format sxw        sxw    "OpenOffice.Org (sxw)"  ""  ""        ""      "document,vector"
350 \Format wmf        wmf    "Windows Metafile"      "" "" ""      "vector"
351 \Format emf        emf    "Enhanced Metafile"     "" "" ""      "vector"
352 \Format word       doc    "MS Word"               W  "" ""      "document,vector"
353 \Format wordhtml   html   "HTML (MS Word)"        "" ""        ""       "document"
354 ''')
355
356
357 def checkConverterEntries():
358     ''' Check all converters (\converter entries) '''
359     checkProg('the pdflatex program', ['pdflatex $$i'],
360         rc_entry = [ r'\converter pdflatex   pdf2       "%%"    "latex"' ])
361     
362     ''' If we're running LyX in-place then tex2lyx will be found in
363             ../src/tex2lyx. Add this directory to the PATH temporarily and
364             search for tex2lyx.
365             Use PATH to avoid any problems with paths-with-spaces.
366     '''
367     path_orig = os.environ["PATH"]
368     os.environ["PATH"] = os.path.join('..', 'src', 'tex2lyx') + \
369         os.pathsep + path_orig
370
371     checkProg('a LaTeX/Noweb -> LyX converter', ['tex2lyx', 'tex2lyx' + version_suffix],
372         rc_entry = [r'''\converter latex      lyx        "%% -f $$i $$o"        ""
373 \converter literate   lyx        "%% -n -f $$i $$o"     ""'''])
374
375     os.environ["PATH"] = path_orig
376
377     #
378     checkProg('a Noweb -> LaTeX converter', ['noweave -delay -index $$i > $$o'],
379         rc_entry = [r'''\converter literate   latex      "%%"   ""
380 \converter literate   pdflatex      "%%"        ""'''])
381     #
382     checkProg('an HTML -> LaTeX converter', ['html2latex $$i', 'gnuhtml2latex $$i', \
383         'htmltolatex -input $$i -output $$o', 'java -jar htmltolatex.jar -input $$i -output $$o'],
384         rc_entry = [ r'\converter html       latex      "%%"    ""' ])
385     #
386     checkProg('an MS Word -> LaTeX converter', ['wvCleanLatex $$i $$o'],
387         rc_entry = [ r'\converter word       latex      "%%"    ""' ])
388     # On SuSE the scripts have a .sh suffix, and on debian they are in /usr/share/tex4ht/
389     path, htmlconv = checkProg('a LaTeX -> HTML converter', ['htlatex $$i', 'htlatex.sh $$i', \
390         '/usr/share/tex4ht/htlatex $$i', 'tth  -t -e2 -L$$b < $$i > $$o', \
391         'latex2html -no_subdir -split 0 -show_section_numbers $$i', 'hevea -s $$i'],
392         rc_entry = [ r'\converter latex      html       "%%"    "needaux"' ])
393     if htmlconv.find('htlatex') >= 0 or htmlconv == 'latex2html':
394       addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py -e html,png,css $$i $$o"''')
395     else:
396       addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py $$i $$o"''')
397
398     # On SuSE the scripts have a .sh suffix, and on debian they are in /usr/share/tex4ht/
399     path, htmlconv = checkProg('a LaTeX -> MS Word converter', ["htlatex $$i 'html,word' 'symbol/!' '-cvalidate'", \
400         "htlatex.sh $$i 'html,word' 'symbol/!' '-cvalidate'", \
401         "/usr/share/tex4ht/htlatex $$i 'html,word' 'symbol/!' '-cvalidate'"],
402         rc_entry = [ r'\converter latex      wordhtml   "%%"    "needaux"' ])
403     if htmlconv.find('htlatex') >= 0:
404       addToRC(r'''\copier    wordhtml       "python -tt $$s/scripts/ext_copy.py -e html,png,css $$i $$o"''')
405     #
406     checkProg('an OpenOffice.org -> LaTeX converter', ['w2l -clean $$i'],
407         rc_entry = [ r'\converter sxw        latex      "%%"    ""' ])
408     #
409     checkProg('an OpenDocument -> LaTeX converter', ['w2l -clean $$i'],
410         rc_entry = [ r'\converter odt        latex      "%%"    ""' ])
411     # On SuSE the scripts have a .sh suffix, and on debian they are in /usr/share/tex4ht/
412     # Both SuSE and debian have oolatex
413     checkProg('a LaTeX -> Open Document converter', [
414         'oolatex $$i', 'oolatex.sh $$i', '/usr/share/tex4ht/oolatex $$i'],
415         rc_entry = [ r'\converter latex      odt        "%%"    "needaux"' ])
416     # On windows it is called latex2rt.exe
417     checkProg('a LaTeX -> RTF converter', ['latex2rtf -p -S -o $$o $$i', 'latex2rt -p -S -o $$o $$i'],
418         rc_entry = [ r'\converter latex      rtf        "%%"    "needaux"' ])
419     #
420     checkProg('a RTF -> HTML converter', ['unrtf --html  $$i > $$o'],
421         rc_entry = [ r'\converter rtf      html        "%%"     ""' ])
422     #
423     checkProg('a PS to PDF converter', ['ps2pdf13 $$i $$o'],
424         rc_entry = [ r'\converter ps         pdf        "%%"    ""' ])
425     #
426     checkProg('a PS to TXT converter', ['pstotext $$i > $$o'],
427         rc_entry = [ r'\converter ps         text2      "%%"    ""' ])
428     #
429     checkProg('a PS to TXT converter', ['ps2ascii $$i $$o'],
430         rc_entry = [ r'\converter ps         text3      "%%"    ""' ])
431     #
432     checkProg('a PS to EPS converter', ['ps2eps $$i'],
433         rc_entry = [ r'\converter ps         eps      "%%"      ""' ])
434     #
435     checkProg('a PDF to PS converter', ['pdf2ps $$i $$o', 'pdftops $$i $$o'],
436         rc_entry = [ r'\converter pdf         ps        "%%"    ""' ])
437     #
438     checkProg('a PDF to EPS converter', ['pdftops -eps -f 1 -l 1 $$i $$o'],
439         rc_entry = [ r'\converter pdf         eps        "%%"   ""' ])
440     #
441     checkProg('a DVI to TXT converter', ['catdvi $$i > $$o'],
442         rc_entry = [ r'\converter dvi        text4      "%%"    ""' ])
443     #
444     checkProg('a DVI to PS converter', ['dvips -o $$o $$i'],
445         rc_entry = [ r'\converter dvi        ps         "%%"    ""' ])
446     #
447     checkProg('a DVI to PDF converter', ['dvipdfmx -o $$o $$i', 'dvipdfm -o $$o $$i'],
448         rc_entry = [ r'\converter dvi        pdf3       "%%"    ""' ])
449     #
450     path, dvipng = checkProg('dvipng', ['dvipng'])
451     if dvipng == "dvipng":
452         addToRC(r'\converter lyxpreview png        "python -tt $$s/scripts/lyxpreview2bitmap.py"        ""')
453     else:
454         addToRC(r'\converter lyxpreview png        ""   ""')
455     #  
456     checkProg('a fax program', ['kdeprintfax $$i', 'ksendfax $$i'],
457         rc_entry = [ r'\converter ps         fax        "%%"    ""'])
458     #
459     checkProg('a FIG -> EPS/PPM converter', ['fig2dev'],
460         rc_entry = [
461             r'''\converter fig        eps        "fig2dev -L eps $$i $$o"       ""
462 \converter fig        ppm        "fig2dev -L ppm $$i $$o"       ""
463 \converter fig        png        "fig2dev -L png $$i $$o"       ""''',
464             ''])
465     #
466     checkProg('a TIFF -> PS converter', ['tiff2ps $$i > $$o'],
467         rc_entry = [ r'\converter tiff       eps        "%%"    ""', ''])
468     #
469     checkProg('a TGIF -> EPS/PPM converter', ['tgif'],
470         rc_entry = [
471             r'''\converter tgif       eps        "tgif -stdout -print -color -eps $$i > $$o"    ""
472 \converter tgif       ppm        "tgif -stdout -print -color -ppm $$i > $$o"    ""
473 \converter tgif       png        "tgif -stdout -print -color -png $$i > $$o"    ""
474 \converter tgif       pdf        "tgif -stdout -print -color -pdf $$i > $$o"    ""''',
475             ''])
476     #
477     checkProg('a WMF -> EPS converter', ['metafile2eps $$i $$o', 'wmf2eps -o $$o $$i'],
478         rc_entry = [ r'\converter wmf        eps        "%%"    ""'])
479     #
480     checkProg('an EMF -> EPS converter', ['metafile2eps $$i $$o', 'wmf2eps -o $$o $$i'],
481         rc_entry = [ r'\converter emf        eps        "%%"    ""'])
482     #
483     checkProg('an EPS -> PDF converter', ['epstopdf'],
484         rc_entry = [ r'\converter eps        pdf        "epstopdf --outfile=$$o $$i"    ""', ''])
485     #
486     # no agr -> pdf converter, since the pdf library used by gracebat is not
487     # free software and therefore not compiled in in many installations.
488     # Fortunately, this is not a big problem, because we will use epstopdf to
489     # convert from agr to pdf via eps without loss of quality.
490     checkProg('a Grace -> Image converter', ['gracebat'],
491         rc_entry = [
492             r'''\converter agr        eps        "gracebat -hardcopy -printfile $$o -hdevice EPS $$i 2>/dev/null"       ""
493 \converter agr        png        "gracebat -hardcopy -printfile $$o -hdevice PNG $$i 2>/dev/null"       ""
494 \converter agr        jpg        "gracebat -hardcopy -printfile $$o -hdevice JPEG $$i 2>/dev/null"      ""
495 \converter agr        ppm        "gracebat -hardcopy -printfile $$o -hdevice PNM $$i 2>/dev/null"       ""''',
496             ''])
497     #
498     checkProg('a Dot -> PDF converter', ['dot -Tpdf $$i -o $$o'],
499         rc_entry = [ r'\converter dot        pdf        "%%"    ""'])
500     #
501     #
502     path, lilypond = checkProg('a LilyPond -> EPS/PDF/PNG converter', ['lilypond'])
503     if (lilypond != ''):
504         version_string = cmdOutput("lilypond --version")
505         match = re.match('GNU LilyPond (\S+)', version_string)
506         if match:
507             version_number = match.groups()[0]
508             version = version_number.split('.')
509             if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 6):
510                 addToRC(r'''\converter lilypond   eps        "lilypond -b eps --ps $$i" ""
511 \converter lilypond   png        "lilypond -b eps --png $$i"    ""''')
512                 if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 9):
513                     addToRC(r'\converter lilypond   pdf        "lilypond -b eps --pdf $$i"      ""')
514                 print '+  found LilyPond version %s.' % version_number
515             else:
516                 print '+  found LilyPond, but version %s is too old.' % version_number
517         else:
518             print '+  found LilyPond, but could not extract version number.'
519     #
520     checkProg('a Noteedit -> LilyPond converter', ['noteedit --export-lilypond $$i'],
521         rc_entry = [ r'\converter noteedit   lilypond   "%%"    ""', ''])
522     #
523     # FIXME: no rc_entry? comment it out
524     # checkProg('Image converter', ['convert $$i $$o'])
525     #
526     # Entries that do not need checkProg
527     addToRC(r'''\converter lyxpreview ppm        "python -tt $$s/scripts/lyxpreview2bitmap.py"  ""
528 \converter lyxpreview-platex ppm        "python -tt $$s/scripts/lyxpreview-platex2bitmap.py"    ""
529 \converter csv        lyx        "python -tt $$s/scripts/csv2lyx.py $$i $$o"    ""
530 \converter date       dateout    "python -tt $$s/scripts/date.py %d-%m-%Y > $$o"        ""
531 \converter docbook    docbook-xml "cp $$i $$o"  "xml"
532 \converter fen        asciichess "python -tt $$s/scripts/fen2ascii.py $$i $$o"  ""
533 \converter fig        pdftex     "python -tt $$s/scripts/fig2pdftex.py $$i $$o" ""
534 \converter fig        pstex      "python -tt $$s/scripts/fig2pstex.py $$i $$o"  ""
535 \converter lyx        lyx13x     "python -tt $$s/lyx2lyx/lyx2lyx -t 221 $$i > $$o"      ""
536 \converter lyx        lyx14x     "python -tt $$s/lyx2lyx/lyx2lyx -t 245 $$i > $$o"      ""
537 \converter lyx        lyx15x     "python -tt $$s/lyx2lyx/lyx2lyx -t 276 $$i > $$o"      ""
538 \converter lyx        clyx       "python -tt $$s/lyx2lyx/lyx2lyx -c big5 -t 245 $$i > $$o"      ""
539 \converter lyx        jlyx       "python -tt $$s/lyx2lyx/lyx2lyx -c euc_jp -t 245 $$i > $$o"    ""
540 \converter lyx        klyx       "python -tt $$s/lyx2lyx/lyx2lyx -c euc_kr -t 245 $$i > $$o"    ""
541 \converter clyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c big5 $$i > $$o"     ""
542 \converter jlyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c euc_jp $$i > $$o"   ""
543 \converter klyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c euc_kr $$i > $$o"   ""
544 ''')
545
546
547 def checkLinuxDoc():
548     ''' Check linuxdoc '''
549     #
550     path, LINUXDOC = checkProg('SGML-tools 1.x (LinuxDoc)', ['sgml2lyx'],
551         rc_entry = [
552         r'''\converter linuxdoc   lyx        "sgml2lyx $$i"     ""
553 \converter linuxdoc   latex      "sgml2latex $$i"       ""
554 \converter linuxdoc   dvi        "sgml2latex -o dvi $$i"        ""
555 \converter linuxdoc   html       "sgml2html $$i"        ""''',
556         r'''\converter linuxdoc   lyx        "" ""
557 \converter linuxdoc   latex      ""     ""
558 \converter linuxdoc   dvi        ""     ""
559 \converter linuxdoc   html       ""     ""''' ])
560     if LINUXDOC != '':
561         return ('yes', 'true', '\\def\\haslinuxdoc{yes}')
562     else:
563         return ('no', 'false', '')
564
565
566 def checkDocBook():
567     ''' Check docbook '''
568     path, DOCBOOK = checkProg('SGML-tools 2.x (DocBook) or db2x scripts', ['sgmltools', 'db2dvi'],
569         rc_entry = [
570             r'''\converter docbook    dvi        "sgmltools -b dvi $$i" ""
571 \converter docbook    html       "sgmltools -b html $$i"        ""''',
572             r'''\converter docbook    dvi        "db2dvi $$i"   ""
573 \converter docbook    html       "db2html $$i"  ""''',
574             r'''\converter docbook    dvi        ""     ""
575 \converter docbook    html       ""     ""'''])
576     #
577     if DOCBOOK != '':
578         return ('yes', 'true', '\\def\\hasdocbook{yes}')
579     else:
580         return ('no', 'false', '')
581
582
583 def checkOtherEntries():
584     ''' entries other than Format and Converter '''
585     checkProg('a *roff formatter', ['groff', 'nroff'],
586         rc_entry = [
587             r'\plaintext_roff_command "groff -t -Tlatin1 $$FName"',
588             r'\plaintext_roff_command "tbl $$FName | nroff"',
589             r'\plaintext_roff_command ""' ])
590     checkProg('ChkTeX', ['chktex -n1 -n3 -n6 -n9 -n22 -n25 -n30 -n38'],
591         rc_entry = [ r'\chktex_command "%%"' ])
592     checkProg('BibTeX', ['jbibtex', 'bibtex'],
593         rc_entry = [ r'\bibtex_command "%%"' ])
594     checkProg('an index processor', ['texindy', 'makeindex -c -q'],
595         rc_entry = [ r'\index_command "%%"' ])
596     checkProg('a nomenclature processor', ['makeindex'],
597         rc_entry = [ r'\nomencl_command "makeindex -s nomencl.ist"' ])
598     checkProg('a spellchecker', ['ispell'],
599         rc_entry = [ r'\spell_command "%%"' ])
600     ## FIXME: OCTAVE is not used anywhere
601     # path, OCTAVE = checkProg('Octave', ['octave'])
602     ## FIXME: MAPLE is not used anywhere
603     # path, MAPLE = checkProg('Maple', ['maple'])
604     checkProg('a spool command', ['lp', 'lpr'],
605         rc_entry = [
606             r'''\print_spool_printerprefix "-d "
607 \print_spool_command "lp"''',
608             r'''\print_spool_printerprefix "-P",
609 \print_spool_command "lpr"''',
610             ''])
611     # Add the rest of the entries (no checkProg is required)
612     addToRC(r'''\copier    fig        "python -tt $$s/scripts/fig_copy.py $$i $$o"
613 \copier    pstex      "python -tt $$s/scripts/tex_copy.py $$i $$o $$l"
614 \copier    pdftex     "python -tt $$s/scripts/tex_copy.py $$i $$o $$l"
615 \copier    program    "python -tt $$s/scripts/ext_copy.py $$i $$o"
616 ''')
617
618
619 def processLayoutFile(file, bool_docbook, bool_linuxdoc):
620     ''' process layout file and get a line of result
621         
622         Declare lines look like this: (article.layout, scrbook.layout, svjog.layout)
623         
624         \DeclareLaTeXClass{article}
625         \DeclareLaTeXClass[scrbook]{book (koma-script)}
626         \DeclareLaTeXClass[svjour,svjog.clo]{article (Springer - svjour/jog)}
627
628         we expect output:
629         
630         "article" "article" "article" "false"
631         "scrbook" "scrbook" "book (koma-script)" "false"
632         "svjog" "svjour" "article (Springer - svjour/jog)" "false"
633     '''
634     classname = file.split(os.sep)[-1].split('.')[0]
635     # return ('LaTeX', '[a,b]', 'a', ',b,c', 'article') for \DeclareLaTeXClass[a,b,c]{article}
636     p = re.compile(r'\Declare(LaTeX|DocBook|LinuxDoc)Class\s*(\[([^,]*)(,.*)*\])*\s*{(.*)}')
637     for line in open(file).readlines():
638         res = p.search(line)
639         if res != None:
640             (classtype, optAll, opt, opt1, desc) = res.groups()
641             avai = {'LaTeX':'false', 'DocBook':bool_docbook, 'LinuxDoc':bool_linuxdoc}[classtype]
642             if opt == None:
643                 opt = classname
644             return '"%s" "%s" "%s" "%s"\n' % (classname, opt, desc, avai)
645     print "Layout file without \DeclareXXClass line. "
646     sys.exit(2)
647
648     
649 def checkLatexConfig(check_config, bool_docbook, bool_linuxdoc):
650     ''' Explore the LaTeX configuration 
651         Return None (will be passed to sys.exit()) for success.
652     '''
653     print 'checking LaTeX configuration... ',
654     # if --without-latex-config is forced, or if there is no previous 
655     # version of textclass.lst, re-generate a default file.
656     if not os.path.isfile('textclass.lst') or not check_config:
657         # remove the files only if we want to regenerate
658         removeFiles(['textclass.lst', 'packages.lst'])
659         #
660         # Then, generate a default textclass.lst. In case configure.py
661         # fails, we still have something to start lyx.
662         print ' default values'
663         print '+checking list of textclasses... '
664         tx = open('textclass.lst', 'w')
665         tx.write('''
666 # This file declares layouts and their associated definition files
667 # (include dir. relative to the place where this file is).
668 # It contains only default values, since chkconfig.ltx could not be run
669 # for some reason. Run ./configure.py if you need to update it after a
670 # configuration change.
671 ''')
672         # build the list of available layout files and convert it to commands
673         # for chkconfig.ltx
674         foundClasses = []
675         for file in glob.glob( os.path.join('layouts', '*.layout') ) + \
676             glob.glob( os.path.join(srcdir, 'layouts', '*.layout' ) ) :
677             # valid file?
678             if not os.path.isfile(file): 
679                 continue
680             # get stuff between /xxxx.layout .
681             classname = file.split(os.sep)[-1].split('.')[0]
682             #  tr ' -' '__'`
683             cleanclass = classname.replace(' ', '_')
684             cleanclass = cleanclass.replace('-', '_')
685             # make sure the same class is not considered twice
686             if foundClasses.count(cleanclass) == 0: # not found before
687                 foundClasses.append(cleanclass)
688                 tx.write(processLayoutFile(file, bool_docbook, bool_linuxdoc))
689         tx.close()
690         print '\tdone'
691     if not check_config:
692         return None
693     # the following will generate textclass.lst.tmp, and packages.lst.tmp
694     else:
695         print '\tauto'
696         removeFiles(['wrap_chkconfig.ltx', 'chkconfig.vars', \
697             'chkconfig.classes', 'chklayouts.tex'])
698         rmcopy = False
699         if not os.path.isfile( 'chkconfig.ltx' ):
700             shutil.copyfile( os.path.join(srcdir, 'chkconfig.ltx'), 'chkconfig.ltx' )
701             rmcopy = True
702         writeToFile('wrap_chkconfig.ltx', '%s\n%s\n\\input{chkconfig.ltx}\n' \
703             % (linuxdoc_cmd, docbook_cmd) )
704         # Construct the list of classes to test for.
705         # build the list of available layout files and convert it to commands
706         # for chkconfig.ltx
707         p1 = re.compile(r'\Declare(LaTeX|DocBook)Class')
708         testclasses = list()
709         for file in glob.glob( os.path.join('layouts', '*.layout') ) + \
710             glob.glob( os.path.join(srcdir, 'layouts', '*.layout' ) ) :
711             if not os.path.isfile(file):
712                 continue
713             classname = file.split(os.sep)[-1].split('.')[0]
714             for line in open(file).readlines():
715                 if p1.search(line) == None:
716                     continue
717                 if line[0] != '#':
718                     print "Wrong input layout file with line '" + line
719                     sys.exit(3)
720                 testclasses.append("\\TestDocClass{%s}{%s}" % (classname, line[1:].strip()))
721                 break
722         testclasses.sort()
723         cl = open('chklayouts.tex', 'w')
724         for line in testclasses:
725             cl.write(line + '\n')
726         cl.close()
727         #
728         # we have chklayouts.tex, then process it
729         fout = os.popen(LATEX + ' wrap_chkconfig.ltx')
730         while True:
731             line = fout.readline()
732             if not line:
733                 break;
734             if re.match('^\+', line):
735                 print line,
736         # if the command succeeds, None will be returned
737         ret = fout.close()
738         #
739         # currently, values in chhkconfig are only used to set
740         # \font_encoding
741         values = {}
742         for line in open('chkconfig.vars').readlines():
743             key, val = re.sub('-', '_', line).split('=')
744             val = val.strip()
745             values[key] = val.strip("'")
746         # chk_fontenc may not exist 
747         try:
748             addToRC(r'\font_encoding "%s"' % values["chk_fontenc"])
749         except:
750             pass
751         if rmcopy:   # remove the copied file
752             removeFiles( [ 'chkconfig.ltx' ] )
753         # if configure successed, move textclass.lst.tmp to textclass.lst
754         # and packages.lst.tmp to packages.lst
755         if os.path.isfile('textclass.lst.tmp') and len(open('textclass.lst.tmp').read()) > 0 \
756             and os.path.isfile('packages.lst.tmp') and len(open('packages.lst.tmp').read()) > 0:
757             shutil.move('textclass.lst.tmp', 'textclass.lst')
758             shutil.move('packages.lst.tmp', 'packages.lst')
759         return ret
760
761
762 def checkModulesConfig():
763   removeFiles(['lyxmodules.lst'])
764
765   print '+checking list of modules... '
766   tx = open('lyxmodules.lst', 'w')
767   tx.write('''## This file declares modules and their associated definition files.
768 ## It has been automatically generated by configure
769 ## Use "Options/Reconfigure" if you need to update it after a
770 ## configuration change. 
771 ''')
772   # build the list of available modules
773   foundClasses = []
774   for file in glob.glob( os.path.join('layouts', '*.module') ) + \
775       glob.glob( os.path.join(srcdir, 'layouts', '*.module' ) ) :
776       # valid file?
777       print file
778       if not os.path.isfile(file): 
779           continue
780       tx.write(processModuleFile(file, bool_docbook, bool_linuxdoc))
781   tx.close()
782   print '\tdone'
783
784
785 def processModuleFile(file, bool_docbook, bool_linuxdoc):
786     ''' process module file and get a line of result
787
788         The top of a module file should look like this:
789           #\DeclareLyXModule[LaTeX Packages]{ModuleName}
790           #BeginDescription
791           #...body of description...
792           #EndDescription
793           #Requires: [list of required modules]
794           #Excludes: [list of excluded modules]
795         The last two lines are optional
796         We expect output:
797           "ModuleName" "filename" "Description" "Packages" "Requires" "Excludes"
798     '''
799     p = re.compile(r'\DeclareLyXModule\s*(?:\[([^]]*?)\])?{(.*)}')
800     r = re.compile(r'#+\s*Requires: (.*)')
801     x = re.compile(r'#+\s*Excludes: (.*)')
802     b = re.compile(r'#+\s*DescriptionBegin\s*$')
803     e = re.compile(r'#+\s*DescriptionEnd\s*$')
804
805     modname = desc = pkgs = req = excl = ""
806     readingDescription = False
807     descLines = []
808     filename = file.split(os.sep)[-1]
809     filename = filename[:-7]
810
811     for line in open(file).readlines():
812       if readingDescription:
813         res = e.search(line)
814         if res != None:
815           readingDescription = False
816           desc = " ".join(descLines)
817           continue
818         descLines.append(line[1:].strip())
819         continue
820       res = b.search(line)
821       if res != None:
822         readingDescription = True
823         continue
824       res = p.search(line)
825       if res != None:
826           (pkgs, modname) = res.groups()
827           if pkgs == None:
828             pkgs = ""
829           else:
830             tmp = [s.strip() for s in pkgs.split(",")]
831             pkgs = ",".join(tmp)
832           continue
833       res = r.search(line)
834       if res != None:
835         req = res.group(1)
836         tmp = [s.strip() for s in req.split("|")]
837         req = "|".join(tmp)
838         continue
839       res = x.search(line)
840       if res != None:
841         excl = res.group(1)
842         tmp = [s.strip() for s in excl.split("|")]
843         excl = "|".join(tmp)
844         continue
845     if modname != "":
846         return '"%s" "%s" "%s" "%s" "%s" "%s"\n' % (modname, filename, desc, pkgs, req, excl)
847     print "Module file without \DeclareLyXModule line. "
848     sys.exit(2)
849
850
851 def checkTeXAllowSpaces():
852     ''' Let's check whether spaces are allowed in TeX file names '''
853     tex_allows_spaces = 'false'
854     if lyx_check_config:
855         print "Checking whether TeX allows spaces in file names... ",
856         writeToFile('a b.tex', r'\message{working^^J}' )
857         if os.name == 'nt':
858             latex_out = cmdOutput(LATEX + r""" "\nonstopmode\input{\"a b\"}" """)
859         else:
860             latex_out = cmdOutput(LATEX + r""" '\nonstopmode\input{"a b"}' """)
861         if 'working' in latex_out:
862             print 'yes'
863             tex_allows_spaces = 'true'
864         else:
865             print 'no'
866             tex_allows_spaces = 'false'
867         addToRC(r'\tex_allows_spaces ' + tex_allows_spaces)
868         removeFiles( [ 'a b.tex', 'a b.log', 'texput.log' ])
869
870
871 def removeTempFiles():
872     # Final clean-up
873     if not lyx_keep_temps:
874         removeFiles(['chkconfig.vars',  \
875             'wrap_chkconfig.ltx', 'wrap_chkconfig.log', \
876             'chklayouts.tex', 'missfont.log', 
877             'chklatex.ltx', 'chklatex.log'])
878
879
880 if __name__ == '__main__':
881     lyx_check_config = True
882     outfile = 'lyxrc.defaults'
883     rc_entries = ''
884     lyx_keep_temps = False
885     version_suffix = ''
886     logfile = 'configure.log'
887     ## Parse the command line
888     for op in sys.argv[1:]:   # default shell/for list is $*, the options
889         if op in [ '-help', '--help', '-h' ]:
890             print '''Usage: configure [options]
891 Options:
892     --help                   show this help lines
893     --keep-temps             keep temporary files (for debug. purposes)
894     --without-latex-config   do not run LaTeX to determine configuration
895     --with-version-suffix=suffix suffix of binary installed files
896 '''
897             sys.exit(0)
898         elif op == '--without-latex-config':
899             lyx_check_config = False
900         elif op == '--keep-temps':
901             lyx_keep_temps = True
902         elif op[0:22] == '--with-version-suffix=':  # never mind if op is not long enough
903             version_suffix = op[22:]
904         else:
905             print "Unknown option", op
906             sys.exit(1)
907     #
908     # set up log file for stdout and stderr
909     log = open(logfile, 'w')
910     sys.stdout = Tee(sys.stdout, log)
911     sys.stderr = Tee(sys.stderr, log)
912     # check if we run from the right directory
913     srcdir = os.path.dirname(sys.argv[0])
914     if srcdir == '':
915         srcdir = '.'
916     if not os.path.isfile( os.path.join(srcdir, 'chkconfig.ltx') ):
917         print "configure: error: cannot find chkconfig.ltx script"
918         sys.exit(1)
919     setEnviron()
920     createDirectories()
921     windows_style_tex_paths = checkTeXPaths()
922     dtl_tools = checkDTLtools()
923     ## Write the first part of outfile
924     writeToFile(outfile, '''# This file has been automatically generated by LyX' lib/configure.py
925 # script. It contains default settings that have been determined by
926 # examining your system. PLEASE DO NOT MODIFY ANYTHING HERE! If you
927 # want to customize LyX, use LyX' Preferences dialog or modify directly 
928 # the "preferences" file instead. Any setting in that file will
929 # override the values given here.
930 ''')
931     # check latex
932     LATEX = checkLatex(dtl_tools)
933     checkFormatEntries(dtl_tools)
934     checkConverterEntries()
935     (chk_linuxdoc, bool_linuxdoc, linuxdoc_cmd) = checkLinuxDoc()
936     (chk_docbook, bool_docbook, docbook_cmd) = checkDocBook()
937     checkTeXAllowSpaces()
938     if windows_style_tex_paths != '':
939         addToRC(r'\tex_expects_windows_paths %s' % windows_style_tex_paths)
940     checkOtherEntries()
941     # --without-latex-config can disable lyx_check_config
942     ret = checkLatexConfig(lyx_check_config and LATEX != '',
943         bool_docbook, bool_linuxdoc)
944     checkModulesConfig() #lyx_check_config and LATEX != '')
945     removeTempFiles()
946     # The return error code can be 256. Because most systems expect an error code
947     # in the range 0-127, 256 can be interpretted as 'success'. Because we expect
948     # a None for success, 'ret is not None' is used to exit.
949     sys.exit(ret is not None)