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