]> git.lyx.org Git - lyx.git/blob - lib/configure.py
4bf80fb05a21f9102c9d912a37c35c8d341fbcbb
[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, 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                     pr = re.compile(r'(\\\S+)(.*)$')
210                     m = None
211                     # write rc entries for this command
212                     if found_prime == False:
213                         if len(rc_entry) == 1:
214                             addToRC(rc_entry[0].replace('%%', ac_prog))
215                         elif len(rc_entry) > 1:
216                             addToRC(rc_entry[idx].replace('%%', ac_prog))
217                         real_ac_dir = ac_dir
218                         real_ac_word = ac_word
219                         found_prime = True
220                     if len(alt_rc_entry) == 1:
221                         alt_rc = alt_rc_entry[0]
222                         if alt_rc == "":
223                             # if no explicit alt_rc is given, construct one
224                             m = pr.match(rc_entry[0])
225                             if m:
226                                 alt_rc = m.group(1) + "_alternatives" + m.group(2)
227                         addToRC(alt_rc.replace('%%', ac_prog))
228                     elif len(alt_rc_entry) > 1:
229                         alt_rc = alt_rc_entry[idx]
230                         if alt_rc == "":
231                             # if no explicit alt_rc is given, construct one
232                             m = pr.match(rc_entry[idx])
233                             if m:
234                                 alt_rc = m.group(1) + "_alternatives" + m.group(2)
235                         addToRC(alt_rc.replace('%%', ac_prog))
236                     found_alt = True
237                     break
238             if found_alt:
239                 break
240         if found_alt == False:
241             # if not successful
242             logger.info(msg + ' no')
243     if found_prime:
244         return [real_ac_dir, real_ac_word]
245     # write rc entries for 'not found'
246     if len(rc_entry) > 0:  # the last one.
247         addToRC(rc_entry[-1].replace('%%', not_found))
248     return ['', not_found]
249
250
251 def addViewerAlternatives(rcs):
252     r = re.compile(r'\\Format (\S+).*$')
253     m = None
254     alt = ''
255     for idxx in range(len(rcs)):
256         if len(rcs) == 1:
257             m = r.match(rcs[0])
258             if m:
259                 alt = r'\viewer_alternatives ' + m.group(1) + " %%"
260         elif len(rcs) > 1:
261             m = r.match(rcs[idxx])
262             if m:
263                 if idxx > 0:
264                     alt += '\n'
265                 alt += r'\viewer_alternatives ' + m.group(1) + " %%"
266     return alt
267
268
269 def addEditorAlternatives(rcs):
270     r = re.compile(r'\\Format (\S+).*$')
271     m = None
272     alt = ''
273     for idxx in range(len(rcs)):
274         if len(rcs) == 1:
275             m = r.match(rcs[0])
276             if m:
277                 alt = r'\editor_alternatives ' + m.group(1) + " %%"
278         elif len(rcs) > 1:
279             m = r.match(rcs[idxx])
280             if m:
281                 if idxx > 0:
282                     alt += '\n'
283                 alt += r'\editor_alternatives ' + m.group(1) + " %%"
284     return alt
285
286
287 def checkViewer(description, progs, rc_entry = [], path = []):
288     ''' The same as checkProgAlternatives, but for viewers '''
289     if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1:
290         logger.error("rc entry should have one item or item for each prog and not_found.")
291         sys.exit(2)
292     alt_rc_entry = []
293     for idx in range(len(progs)):
294         if len(rc_entry) == 1:
295             rcs = rc_entry[0].split('\n')
296             alt = addViewerAlternatives(rcs)
297             alt_rc_entry.insert(0, alt)
298         elif len(rc_entry) > 1:
299             rcs = rc_entry[idx].split('\n')
300             alt = addViewerAlternatives(rcs)
301             alt_rc_entry.insert(idx, alt)
302     return checkProgAlternatives(description, progs, rc_entry, alt_rc_entry, path, not_found = 'auto')
303
304
305 def checkEditor(description, progs, rc_entry = [], path = []):
306     ''' The same as checkProgAlternatives, but for editors '''
307     if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1:
308         logger.error("rc entry should have one item or item for each prog and not_found.")
309         sys.exit(2)
310     alt_rc_entry = []
311     for idx in range(len(progs)):
312         if len(rc_entry) == 1:
313             rcs = rc_entry[0].split('\n')
314             alt = addEditorAlternatives(rcs)
315             alt_rc_entry.insert(0, alt)
316         elif len(rc_entry) > 1:
317             rcs = rc_entry[idx].split('\n')
318             alt = addEditorAlternatives(rcs)
319             alt_rc_entry.insert(idx, alt)
320     return checkProgAlternatives(description, progs, rc_entry, alt_rc_entry, path, not_found = 'auto')
321
322
323 def checkViewerNoRC(description, progs, rc_entry = [], path = []):
324     ''' The same as checkViewer, but do not add rc entry '''
325     if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1:
326         logger.error("rc entry should have one item or item for each prog and not_found.")
327         sys.exit(2)
328     alt_rc_entry = []
329     for idx in range(len(progs)):
330         if len(rc_entry) == 1:
331             rcs = rc_entry[0].split('\n')
332             alt = addViewerAlternatives(rcs)
333             alt_rc_entry.insert(0, alt)
334         elif len(rc_entry) > 1:
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             rcs = rc_entry[0].split('\n')
351             alt = addEditorAlternatives(rcs)
352             alt_rc_entry.insert(0, alt)
353         elif len(rc_entry) > 1:
354             rcs = rc_entry[idx].split('\n')
355             alt = addEditorAlternatives(rcs)
356             alt_rc_entry.insert(idx, alt)
357     rc_entry = []
358     return checkProgAlternatives(description, progs, rc_entry, alt_rc_entry, path, not_found = 'auto')
359
360
361 def checkViewerEditor(description, progs, rc_entry = [], path = []):
362     ''' The same as checkProgAlternatives, but for viewers and editors '''
363     checkEditorNoRC(description, progs, rc_entry, path)
364     return checkViewer(description, progs, rc_entry, path)
365
366
367 def checkDTLtools():
368     ''' Check whether DTL tools are available (Windows only) '''
369     # Find programs! Returned path is not used now
370     if ((os.name == 'nt' or sys.platform == 'cygwin') and
371             checkProg('DVI to DTL converter', ['dv2dt']) != ['', ''] and
372             checkProg('DTL to DVI converter', ['dt2dv']) != ['', '']):
373         dtl_tools = True
374     else:
375         dtl_tools = False
376     return dtl_tools
377
378
379 def checkLatex(dtl_tools):
380     ''' Check latex, return lyx_check_config '''
381     path, LATEX = checkProg('a Latex2e program', ['latex $$i', 'latex2e $$i'])
382     path, PPLATEX = checkProg('a DVI postprocessing program', ['pplatex $$i'])
383     #-----------------------------------------------------------------
384     path, PLATEX = checkProg('pLaTeX, the Japanese LaTeX', ['platex $$i'])
385     if PLATEX != '':
386         # check if PLATEX is pLaTeX2e
387         writeToFile('chklatex.ltx', '''
388 \\nonstopmode
389 \\@@end
390 ''')
391         # run platex on chklatex.ltx and check result
392         if cmdOutput(PLATEX + ' chklatex.ltx').find('pLaTeX2e') != -1:
393             # We have the Japanese pLaTeX2e
394             addToRC(r'\converter platex   dvi       "%s"   "latex=platex"' % PLATEX)
395         else:
396             PLATEX = ''
397             removeFiles(['chklatex.ltx', 'chklatex.log'])
398     #-----------------------------------------------------------------
399     # use LATEX to convert from latex to dvi if PPLATEX is not available    
400     if PPLATEX == '':
401         PPLATEX = LATEX
402     if dtl_tools:
403         # Windows only: DraftDVI
404         addToRC(r'''\converter latex      dvi2       "%s"       "latex"
405 \converter dvi2       dvi        "python -tt $$s/scripts/clean_dvi.py $$i $$o"  ""''' % PPLATEX)
406     else:
407         addToRC(r'\converter latex      dvi        "%s" "latex"' % PPLATEX)
408     # no latex
409     if LATEX != '':
410         # Check if latex is usable
411         writeToFile('chklatex.ltx', '''
412 \\nonstopmode\\makeatletter
413 \\ifx\\undefined\\documentclass\\else
414   \\message{ThisIsLaTeX2e}
415 \\fi
416 \\@@end
417 ''')
418         # run latex on chklatex.ltx and check result
419         if cmdOutput(LATEX + ' chklatex.ltx').find('ThisIsLaTeX2e') != -1:
420             # valid latex2e
421             return LATEX
422         else:
423             logger.warning("Latex not usable (not LaTeX2e) ")
424         # remove temporary files
425         removeFiles(['chklatex.ltx', 'chklatex.log'])
426     return ''
427
428
429 def checkModule(module):
430     ''' Check for a Python module, return the status '''
431     msg = 'checking for "' + module + ' module"... '
432     try:
433       __import__(module)
434       logger.info(msg + ' yes')
435       return True
436     except ImportError:
437       logger.info(msg + ' no')
438       return False
439
440
441 def checkFormatEntries(dtl_tools):  
442     ''' Check all formats (\Format entries) '''
443     checkViewerEditor('a Tgif viewer and editor', ['tgif'],
444         rc_entry = [r'\Format tgif       obj     Tgif                   "" "%%" "%%"    "vector"'])
445     #
446     checkViewerEditor('a FIG viewer and editor', ['xfig', 'jfig3-itext.jar', 'jfig3.jar'],
447         rc_entry = [r'\Format fig        fig     FIG                    "" "%%" "%%"    "vector"'])
448     #
449     checkViewerEditor('a Dia viewer and editor', ['dia'],
450         rc_entry = [r'\Format dia        dia     DIA                    "" "%%" "%%"    "vector"'])
451     #
452     checkViewerEditor('a Grace viewer and editor', ['xmgrace'],
453         rc_entry = [r'\Format agr        agr     Grace                  "" "%%" "%%"    "vector"'])
454     #
455     checkViewerEditor('a FEN viewer and editor', ['xboard -lpf $$i -mode EditPosition'],
456         rc_entry = [r'\Format fen        fen     FEN                    "" "%%" "%%"    ""'])
457     #
458     checkViewerEditor('a SVG viewer and editor', ['inkscape'],
459         rc_entry = [r'\Format svg        svg     SVG                    "" "%%" "%%"    "vector"'])
460     #
461     path, iv = checkViewerNoRC('a raster image viewer', ['xv', 'kview', 'gimp-remote', 'gimp'],
462         rc_entry = [r'''\Format bmp        bmp     BMP                    "" "%s"       "%s"    ""
463 \Format gif        gif     GIF                    "" "%s"       "%s"    ""
464 \Format jpg        jpg     JPEG                   "" "%s"       "%s"    ""
465 \Format pbm        pbm     PBM                    "" "%s"       "%s"    ""
466 \Format pgm        pgm     PGM                    "" "%s"       "%s"    ""
467 \Format png        png     PNG                    "" "%s"       "%s"    ""
468 \Format ppm        ppm     PPM                    "" "%s"       "%s"    ""
469 \Format tiff       tif     TIFF                   "" "%s"       "%s"    ""
470 \Format xbm        xbm     XBM                    "" "%s"       "%s"    ""
471 \Format xpm        xpm     XPM                    "" "%s"       "%s"    ""'''])
472     path, ie = checkEditorNoRC('a raster image editor', ['gimp-remote', 'gimp'],
473         rc_entry = [r'''\Format bmp        bmp     BMP                    "" "%s"       "%s"    ""
474 \Format gif        gif     GIF                    "" "%s"       "%s"    ""
475 \Format jpg        jpg     JPEG                   "" "%s"       "%s"    ""
476 \Format pbm        pbm     PBM                    "" "%s"       "%s"    ""
477 \Format pgm        pgm     PGM                    "" "%s"       "%s"    ""
478 \Format png        png     PNG                    "" "%s"       "%s"    ""
479 \Format ppm        ppm     PPM                    "" "%s"       "%s"    ""
480 \Format tiff       tif     TIFF                   "" "%s"       "%s"    ""
481 \Format xbm        xbm     XBM                    "" "%s"       "%s"    ""
482 \Format xpm        xpm     XPM                    "" "%s"       "%s"    ""'''])
483     addToRC(r'''\Format bmp        bmp     BMP                    "" "%s"       "%s"    ""
484 \Format gif        gif     GIF                    "" "%s"       "%s"    ""
485 \Format jpg        jpg     JPEG                   "" "%s"       "%s"    ""
486 \Format pbm        pbm     PBM                    "" "%s"       "%s"    ""
487 \Format pgm        pgm     PGM                    "" "%s"       "%s"    ""
488 \Format png        png     PNG                    "" "%s"       "%s"    ""
489 \Format ppm        ppm     PPM                    "" "%s"       "%s"    ""
490 \Format tiff       tif     TIFF                   "" "%s"       "%s"    ""
491 \Format xbm        xbm     XBM                    "" "%s"       "%s"    ""
492 \Format xpm        xpm     XPM                    "" "%s"       "%s"    ""''' % \
493         (iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie) )
494     #
495     checkViewerEditor('a text editor', ['sensible-editor', 'xemacs', 'gvim', 'kedit', 'kwrite', 'kate', \
496         'nedit', 'gedit', 'notepad'],
497         rc_entry = [r'''\Format asciichess asc    "Plain text (chess output)"  "" ""    "%%"    ""
498 \Format asciiimage asc    "Plain text (image)"         "" ""    "%%"    ""
499 \Format asciixfig  asc    "Plain text (Xfig output)"   "" ""    "%%"    ""
500 \Format dateout    tmp    "date (output)"         "" "" "%%"    ""
501 \Format docbook    sgml    DocBook                B  "" "%%"    "document"
502 \Format docbook-xml xml   "Docbook (XML)"         "" "" "%%"    "document"
503 \Format dot        dot    "Graphviz Dot"          "" "" "%%"    "vector"
504 \Format platex     tex    "LaTeX (pLaTeX)"        "" "" "%%"    "document"
505 \Format literate   nw      NoWeb                  N  "" "%%"    "document"
506 \Format sweave     Rnw    "Sweave"                S  "" "%%"    "document"
507 \Format lilypond   ly     "LilyPond music"        "" "" "%%"    "vector"
508 \Format lilypond-book    lytex "LilyPond book (LaTeX)"   "" ""  "%%"    "document"
509 \Format latex      tex    "LaTeX (plain)"         L  "" "%%"    "document"
510 \Format luatex     tex    "LaTeX (LuaTeX)"        "" "" "%%"    "document"
511 \Format pdflatex   tex    "LaTeX (pdflatex)"      "" "" "%%"    "document"
512 \Format xetex      tex    "LaTeX (XeTeX)"         "" "" "%%"    "document"
513 \Format text       txt    "Plain text"            a  "" "%%"    "document"
514 \Format text2      txt    "Plain text (pstotext)" "" "" "%%"    "document"
515 \Format text3      txt    "Plain text (ps2ascii)" "" "" "%%"    "document"
516 \Format text4      txt    "Plain text (catdvi)"   "" "" "%%"    "document"
517 \Format textparagraph txt "Plain Text, Join Lines" "" ""        "%%"    "document"''' ])
518    #Spreadsheets using ssconvert from gnumeric
519     checkViewer('gnumeric spreadsheet software', ['gnumeric'], 
520       rc_entry = [r'''\Format gnumeric gnumeric "Gnumeric spreadsheet" "" ""    "%%"   "document" 
521 \Format excel      xls    "Excel spreadsheet"      "" "" "%%"    "document" 
522 \Format oocalc     ods    "OpenOffice spreadsheet" "" "" "%%"    "document"''']) 
523  #
524     path, xhtmlview = checkViewer('an HTML previewer', ['firefox', 'mozilla file://$$p$$i', 'netscape'],
525         rc_entry = [r'\Format xhtml      xhtml   "LyXHTML"              y "%%" ""    "document"'])
526     if xhtmlview == "":
527         addToRC(r'\Format xhtml      xhtml   "LyXHTML"              y "" ""  "document"')
528  #
529     checkEditor('a BibTeX editor', ['sensible-editor', 'jabref', 'JabRef', \
530         'pybliographic', 'bibdesk', 'gbib', 'kbib', \
531         'kbibtex', 'sixpack', 'bibedit', 'tkbibtex' \
532         'xemacs', 'gvim', 'kedit', 'kwrite', 'kate', \
533         'nedit', 'gedit', 'notepad'],
534         rc_entry = [r'''\Format bibtex bib    "BibTeX"         "" ""    "%%"    ""''' ])
535     #
536     #checkProg('a Postscript interpreter', ['gs'],
537     #  rc_entry = [ r'\ps_command "%%"' ])
538     checkViewer('a Postscript previewer', ['kghostview', 'okular', 'evince', 'gv', 'ghostview -swap'],
539         rc_entry = [r'''\Format eps        eps     EPS                    "" "%%"       ""      "vector"
540 \Format ps         ps      Postscript             t  "%%"       ""      "document,vector"'''])
541     # for xdg-open issues look here: http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg151818.html
542     checkViewer('a PDF previewer', ['kpdf', 'okular', 'evince', 'kghostview', 'xpdf', 'acrobat', 'acroread', \
543                     'gv', 'ghostview'],
544         rc_entry = [r'''\Format pdf        pdf    "PDF (ps2pdf)"          P  "%%"       ""      "document,vector"
545 \Format pdf2       pdf    "PDF (pdflatex)"        F  "%%"       ""      "document,vector"
546 \Format pdf3       pdf    "PDF (dvipdfm)"         m  "%%"       ""      "document,vector"
547 \Format pdf4       pdf    "PDF (XeTeX)"           X  "%%"       ""      "document,vector"
548 \Format pdf5       pdf    "PDF (LuaTeX)"          u  "%%"       ""      "document,vector"'''])
549     #
550     checkViewer('a DVI previewer', ['xdvi', 'kdvi', 'okular', 'yap', 'dviout -Set=!m'],
551         rc_entry = [r'''\Format dvi        dvi     DVI                    D  "%%"       ""      "document,vector"
552 \Format dvi3       dvi     "DVI (LuaTeX)"          V  "%%"      ""      "document,vector"'''])
553     if dtl_tools:
554         # Windows only: DraftDVI
555         addToRC(r'\Format dvi2       dvi     DraftDVI               ""  ""      ""      "vector"')
556     #
557     checkViewer('an HTML previewer', ['firefox', 'mozilla file://$$p$$i', 'netscape'],
558         rc_entry = [r'\Format html       html    HTML                   H  "%%" ""      "document"'])
559     #
560     checkViewerEditor('Noteedit', ['noteedit'],
561         rc_entry = [r'\Format noteedit   not     Noteedit               "" "%%" "%%"    "vector"'])
562     #
563     checkViewerEditor('an OpenDocument/OpenOffice viewer', ['swriter', 'oowriter', 'abiword'],
564         rc_entry = [r'''\Format odt        odt     OpenDocument           "" "%%"       "%%"    "document,vector"
565 \Format sxw        sxw    "OpenOffice.Org (sxw)"  "" "" ""      "document,vector"'''])
566     # 
567     checkViewerEditor('a Rich Text and Word viewer', ['swriter', 'oowriter', 'abiword'],
568         rc_entry = [r'''\Format rtf        rtf    "Rich Text Format"      "" "%%"       "%%"    "document,vector"
569 \Format word       doc    "MS Word"               W  "%%"       "%%"    "document,vector"'''])
570     #
571     # entries that do not need checkProg
572     addToRC(r'''\Format date       ""     "date command"          "" "" ""      ""
573 \Format csv        csv    "Table (CSV)"  "" ""  ""      "document"
574 \Format fax        ""      Fax                    "" "" ""      "document"
575 \Format lyx        lyx     LyX                    "" "" ""      ""
576 \Format lyx13x     13.lyx  "LyX 1.3.x"             "" ""        ""      "document"
577 \Format lyx14x     14.lyx  "LyX 1.4.x"             "" ""        ""      "document"
578 \Format lyx15x     15.lyx  "LyX 1.5.x"             "" ""        ""      "document"
579 \Format lyx16x     16.lyx  "LyX 1.6.x"             "" ""        ""      "document"
580 \Format clyx       cjklyx "CJK LyX 1.4.x (big5)"  "" "" ""      "document"
581 \Format jlyx       cjklyx "CJK LyX 1.4.x (euc-jp)" "" ""        ""      "document"
582 \Format klyx       cjklyx "CJK LyX 1.4.x (euc-kr)" "" ""        ""      "document"
583 \Format lyxpreview lyxpreview "LyX Preview"       "" "" ""      ""
584 \Format lyxpreview-lytex  lyxpreview-lytex  "LyX Preview (LilyPond book)" "" "" ""      ""
585 \Format lyxpreview-platex lyxpreview-platex "LyX Preview (pLaTeX)"       "" ""  ""      ""
586 \Format pdftex     pdftex_t PDFTEX                "" "" ""      ""
587 \Format program    ""      Program                "" "" ""      ""
588 \Format pstex      pstex_t PSTEX                  "" "" ""      ""
589 \Format wmf        wmf    "Windows Metafile"      "" "" ""      "vector"
590 \Format emf        emf    "Enhanced Metafile"     "" "" ""      "vector"
591 \Format wordhtml   html   "HTML (MS Word)"        "" "" ""      "document"
592 ''')
593
594
595 def checkConverterEntries():
596     ''' Check all converters (\converter entries) '''
597     checkProg('the pdflatex program', ['pdflatex $$i'],
598         rc_entry = [ r'\converter pdflatex   pdf2       "%%"    "latex=pdflatex"' ])
599
600     checkProg('XeTeX', ['xelatex $$i'],
601         rc_entry = [ r'\converter xetex      pdf4       "%%"    "latex=xelatex"' ])
602
603     checkProg('LuaTeX', ['lualatex $$i'],
604         rc_entry = [ r'\converter luatex      pdf5       "%%"   "latex=lualatex"' ])
605
606     checkProg('LuaTeX (DVI)', ['dvilualatex $$i'],
607         rc_entry = [ r'\converter luatex      dvi3        "%%"  "latex=lualatex"' ])
608     
609     ''' If we're running LyX in-place then tex2lyx will be found in
610             ../src/tex2lyx. Add this directory to the PATH temporarily and
611             search for tex2lyx.
612             Use PATH to avoid any problems with paths-with-spaces.
613     '''
614     path_orig = os.environ["PATH"]
615     os.environ["PATH"] = os.path.join('..', 'src', 'tex2lyx') + \
616         os.pathsep + path_orig
617
618 # First search for tex2lyx with version suffix (bug 6986)
619     checkProg('a LaTeX/Noweb -> LyX converter', ['tex2lyx' + version_suffix, 'tex2lyx'],
620         rc_entry = [r'''\converter latex      lyx        "%% -f $$i $$o"        ""
621 \converter literate   lyx        "%% -n -f $$i $$o"     ""'''])
622
623     os.environ["PATH"] = path_orig
624
625     #
626     checkProg('a Noweb -> LaTeX converter', ['noweave -delay -index $$i > $$o'],
627         rc_entry = [r'''\converter literate   latex      "%%"   ""
628 \converter literate   pdflatex      "%%"        ""'''])
629     #
630     checkProg('a Sweave -> LaTeX converter', ['Rscript --no-save --no-restore $$s/scripts/lyxsweave.R $$p$$i $$e $$r'],
631         rc_entry = [r'''\converter sweave   latex      "%%"     ""
632 \converter sweave   pdflatex      "%%"  ""'''])
633     #
634     checkProg('an HTML -> LaTeX converter', ['html2latex $$i', 'gnuhtml2latex $$i', \
635         'htmltolatex -input $$i -output $$o', 'java -jar htmltolatex.jar -input $$i -output $$o'],
636         rc_entry = [ r'\converter html       latex      "%%"    ""' ])
637     #
638     checkProg('an MS Word -> LaTeX converter', ['wvCleanLatex $$i $$o'],
639         rc_entry = [ r'\converter word       latex      "%%"    ""' ])
640
641     # eLyXer: search as an executable (elyxer.py, elyxer)
642     path, elyxer = checkProg('a LyX -> HTML converter',
643         ['elyxer.py --directory $$r $$i $$o', 'elyxer --directory $$r $$i $$o'],
644         rc_entry = [ r'\converter lyx      html       "%%"      ""' ])
645     path, elyxer = checkProg('a LyX -> HTML (MS Word) converter',
646         ['elyxer.py --html --directory $$r $$i $$o', 'elyxer --html --directory $$r $$i $$o'],
647         rc_entry = [ r'\converter lyx      wordhtml       "%%"  ""' ])
648     if elyxer.find('elyxer') >= 0:
649       addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py -e html,png,jpg,jpeg,css $$i $$o"''')
650       addToRC(r'''\copier    wordhtml       "python -tt $$s/scripts/ext_copy.py -e html,png,jpg,jpeg,css $$i $$o"''')
651     else:
652       # search for HTML converters other than eLyXer
653       # On SuSE the scripts have a .sh suffix, and on debian they are in /usr/share/tex4ht/
654       path, htmlconv = checkProg('a LaTeX -> HTML converter', ['htlatex $$i', 'htlatex.sh $$i', \
655           '/usr/share/tex4ht/htlatex $$i', 'tth  -t -e2 -L$$b < $$i > $$o', \
656           'latex2html -no_subdir -split 0 -show_section_numbers $$i', 'hevea -s $$i'],
657           rc_entry = [ r'\converter latex      html       "%%"  "needaux"' ])
658       if htmlconv.find('htlatex') >= 0 or htmlconv == 'latex2html':
659         addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py -e html,png,css $$i $$o"''')
660       else:
661         addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py $$i $$o"''')
662       path, htmlconv = checkProg('a LaTeX -> HTML (MS Word) converter', ["htlatex $$i 'html,word' 'symbol/!' '-cvalidate'", \
663           "htlatex.sh $$i 'html,word' 'symbol/!' '-cvalidate'", \
664           "/usr/share/tex4ht/htlatex $$i 'html,word' 'symbol/!' '-cvalidate'"],
665           rc_entry = [ r'\converter latex      wordhtml   "%%"  "needaux"' ])
666       if htmlconv.find('htlatex') >= 0:
667         addToRC(r'''\copier    wordhtml       "python -tt $$s/scripts/ext_copy.py -e html,png,css $$i $$o"''')
668       else:
669         addToRC(r'''\copier    wordhtml       "python -tt $$s/scripts/ext_copy.py $$i $$o"''')
670
671
672     # Check if LyXBlogger is installed
673     lyxblogger_found = checkModule('lyxblogger')
674     if lyxblogger_found:
675       addToRC(r'\Format    blog       blog       "LyXBlogger"           "" "" ""  "document"')
676       addToRC(r'\converter xhtml      blog       "python -m lyxblogger $$i"       ""')
677
678     #
679     checkProg('an OpenOffice.org -> LaTeX converter', ['w2l -clean $$i'],
680         rc_entry = [ r'\converter sxw        latex      "%%"    ""' ])
681     #
682     checkProg('an OpenDocument -> LaTeX converter', ['w2l -clean $$i'],
683         rc_entry = [ r'\converter odt        latex      "%%"    ""' ])
684     # According to http://www.tug.org/applications/tex4ht/mn-commands.html
685     # the command mk4ht oolatex $$i has to be used as default,
686     # but as this would require to have Perl installed, in MiKTeX oolatex is
687     # directly available as application.
688     # On SuSE the scripts have a .sh suffix, and on debian they are in /usr/share/tex4ht/
689     # Both SuSE and debian have oolatex
690     checkProg('a LaTeX -> Open Document converter', [
691         'oolatex $$i', 'mk4ht oolatex $$i', 'oolatex.sh $$i', '/usr/share/tex4ht/oolatex $$i',
692         'htlatex $$i \'xhtml,ooffice\' \'ooffice/! -cmozhtf\' \'-coo\' \'-cvalidate\''],
693         rc_entry = [ r'\converter latex      odt        "%%"    "needaux"' ])
694     # On windows it is called latex2rt.exe
695     checkProg('a LaTeX -> RTF converter', ['latex2rtf -p -S -o $$o $$i', 'latex2rt -p -S -o $$o $$i'],
696         rc_entry = [ r'\converter latex      rtf        "%%"    "needaux"' ])
697     #
698     checkProg('a RTF -> HTML converter', ['unrtf --html  $$i > $$o'],
699         rc_entry = [ r'\converter rtf      html        "%%"     ""' ])
700     #
701     checkProg('a PS to PDF converter', ['ps2pdf13 $$i $$o'],
702         rc_entry = [ r'\converter ps         pdf        "%%"    ""' ])
703     #
704     checkProg('a PS to TXT converter', ['pstotext $$i > $$o'],
705         rc_entry = [ r'\converter ps         text2      "%%"    ""' ])
706     #
707     checkProg('a PS to TXT converter', ['ps2ascii $$i $$o'],
708         rc_entry = [ r'\converter ps         text3      "%%"    ""' ])
709     #
710     checkProg('a PS to EPS converter', ['ps2eps $$i'],
711         rc_entry = [ r'\converter ps         eps      "%%"      ""' ])
712     #
713     checkProg('a PDF to PS converter', ['pdf2ps $$i $$o', 'pdftops $$i $$o'],
714         rc_entry = [ r'\converter pdf         ps        "%%"    ""' ])
715     #
716     checkProg('a PDF to EPS converter', ['pdftops -eps -f 1 -l 1 $$i $$o'],
717         rc_entry = [ r'\converter pdf         eps        "%%"   ""' ])
718     #
719     checkProg('a DVI to TXT converter', ['catdvi $$i > $$o'],
720         rc_entry = [ r'\converter dvi        text4      "%%"    ""' ])
721     #
722     checkProg('a DVI to PS converter', ['dvips -o $$o $$i'],
723         rc_entry = [ r'\converter dvi        ps         "%%"    ""' ])
724     #
725     checkProg('a DVI to PDF converter', ['dvipdfmx -o $$o $$i', 'dvipdfm -o $$o $$i'],
726         rc_entry = [ r'\converter dvi        pdf3       "%%"    ""' ])
727     #
728     path, dvipng = checkProg('dvipng', ['dvipng'])
729     path, dv2dt  = checkProg('DVI to DTL converter', ['dv2dt'])
730     if dvipng == "dvipng" and dv2dt == 'dv2dt':
731         addToRC(r'\converter lyxpreview png        "python -tt $$s/scripts/lyxpreview2bitmap.py"        ""')
732     else:
733         # set empty converter to override the default imagemagick
734         addToRC(r'\converter lyxpreview png        ""   ""')
735     if dv2dt == 'dv2dt':
736         addToRC(r'\converter lyxpreview ppm        "python -tt $$s/scripts/lyxpreview2bitmap.py"        ""')
737     else:
738         # set empty converter to override the default imagemagick
739         addToRC(r'\converter lyxpreview ppm        ""   ""')
740     #  
741     checkProg('a fax program', ['kdeprintfax $$i', 'ksendfax $$i', 'hylapex $$i'],
742         rc_entry = [ r'\converter ps         fax        "%%"    ""'])
743     #
744     path, fig2dev = checkProg('a FIG -> Image converter', ['fig2dev'])
745     if fig2dev == "fig2dev":
746         addToRC(r'''\converter fig        eps        "fig2dev -L eps $$i $$o"   ""
747 \converter fig        ppm        "fig2dev -L ppm $$i $$o"       ""
748 \converter fig        png        "fig2dev -L png $$i $$o"       ""
749 \converter fig        pdftex     "python -tt $$s/scripts/fig2pdftex.py $$i $$o" ""
750 \converter fig        pstex      "python -tt $$s/scripts/fig2pstex.py $$i $$o"  ""''')
751     #
752     checkProg('a TIFF -> PS converter', ['tiff2ps $$i > $$o'],
753         rc_entry = [ r'\converter tiff       eps        "%%"    ""', ''])
754     #
755     checkProg('a TGIF -> EPS/PPM converter', ['tgif'],
756         rc_entry = [
757             r'''\converter tgif       eps        "tgif -print -color -eps -stdout $$i > $$o"    ""
758 \converter tgif       png        "tgif -print -color -png -o $$d $$i"   ""
759 \converter tgif       pdf        "tgif -print -color -pdf -stdout $$i > $$o"    ""''',
760             ''])
761     #
762     checkProg('a WMF -> EPS converter', ['metafile2eps $$i $$o', 'wmf2eps -o $$o $$i'],
763         rc_entry = [ r'\converter wmf        eps        "%%"    ""'])
764     #
765     checkProg('an EMF -> EPS converter', ['metafile2eps $$i $$o', 'wmf2eps -o $$o $$i'],
766         rc_entry = [ r'\converter emf        eps        "%%"    ""'])
767     #
768     checkProg('an EPS -> PDF converter', ['epstopdf'],
769         rc_entry = [ r'\converter eps        pdf        "epstopdf --outfile=$$o $$i"    ""', ''])
770     #
771     # no agr -> pdf converter, since the pdf library used by gracebat is not
772     # free software and therefore not compiled in in many installations.
773     # Fortunately, this is not a big problem, because we will use epstopdf to
774     # convert from agr to pdf via eps without loss of quality.
775     checkProg('a Grace -> Image converter', ['gracebat'],
776         rc_entry = [
777             r'''\converter agr        eps        "gracebat -hardcopy -printfile $$o -hdevice EPS $$i 2>/dev/null"       ""
778 \converter agr        png        "gracebat -hardcopy -printfile $$o -hdevice PNG $$i 2>/dev/null"       ""
779 \converter agr        jpg        "gracebat -hardcopy -printfile $$o -hdevice JPEG $$i 2>/dev/null"      ""
780 \converter agr        ppm        "gracebat -hardcopy -printfile $$o -hdevice PNM $$i 2>/dev/null"       ""''',
781             ''])
782     #
783     checkProg('a Dot -> Image converter', ['dot'],
784         rc_entry = [
785             r'''\converter dot        eps        "dot -Teps $$i -o $$o" ""
786 \converter dot        png        "dot -Tpng $$i -o $$o" ""''',
787             ''])
788     #
789     checkProg('a Dia -> PNG converter', ['dia -e $$o -t png $$i'],
790         rc_entry = [ r'\converter dia        png        "%%"    ""'])
791     #
792     checkProg('a Dia -> EPS converter', ['dia -e $$o -t eps $$i'],
793         rc_entry = [ r'\converter dia        eps        "%%"    ""'])
794     #
795     checkProg('a SVG -> PDF converter', ['rsvg-convert -f pdf -o $$o $$i', 'inkscape --file=$$p/$$i --export-area-drawing --without-gui --export-pdf=$$p/$$o'],
796         rc_entry = [ r'\converter svg        pdf        "%%"    ""'])
797     #
798     checkProg('a SVG -> EPS converter', ['rsvg-convert -f ps -o $$o $$i', 'inkscape --file=$$p/$$i --export-area-drawing --without-gui --export-eps=$$p/$$o'],
799         rc_entry = [ r'\converter svg        eps        "%%"    ""'])
800     # the PNG export via Inkscape must not have the full path ($$p) for the file
801     checkProg('a SVG -> PNG converter', ['rsvg-convert -f png -o $$o $$i', 'inkscape --without-gui --file=$$i --export-png=$$o'],
802         rc_entry = [ r'\converter svg        png        "%%"    ""'])
803     
804     #
805     # gnumeric/xls/ods to tex
806     checkProg('a spreadsheet -> latex converter', ['ssconvert'], 
807        rc_entry = [ r'''\converter gnumeric latex "ssconvert --export-type=Gnumeric_html:latex $$i $$o" "" 
808 \converter ods latex "ssconvert --export-type=Gnumeric_html:latex $$i $$o" "" 
809 \converter xls latex "ssconvert --export-type=Gnumeric_html:latex $$i $$o" ""''', 
810 '']) 
811
812     path, lilypond = checkProg('a LilyPond -> EPS/PDF/PNG converter', ['lilypond'])
813     if (lilypond != ''):
814         version_string = cmdOutput("lilypond --version")
815         match = re.match('GNU LilyPond (\S+)', version_string)
816         if match:
817             version_number = match.groups()[0]
818             version = version_number.split('.')
819             if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 11):
820                 addToRC(r'''\converter lilypond   eps        "lilypond -dbackend=eps -dsafe --ps $$i"   ""
821 \converter lilypond   png        "lilypond -dbackend=eps -dsafe --png $$i"      ""''')
822                 addToRC(r'\converter lilypond   pdf        "lilypond -dbackend=eps -dsafe --pdf $$i"    ""')
823                 logger.info('+  found LilyPond version %s.' % version_number)
824             elif int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 6):
825                 addToRC(r'''\converter lilypond   eps        "lilypond -b eps --ps --safe $$i"  ""
826 \converter lilypond   png        "lilypond -b eps --png $$i"    ""''')
827                 if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 9):
828                     addToRC(r'\converter lilypond   pdf        "lilypond -b eps --pdf --safe $$i"       ""')
829                 logger.info('+  found LilyPond version %s.' % version_number)
830             else:
831                 logger.info('+  found LilyPond, but version %s is too old.' % version_number)
832         else:
833             logger.info('+  found LilyPond, but could not extract version number.')
834     #
835     path, lilypond_book = checkProg('a LilyPond book (LaTeX) -> LaTeX converter', ['lilypond-book'])
836     if (lilypond_book != ''):
837         version_string = cmdOutput("lilypond-book --version")
838         match = re.match('^(\S+)$', version_string)
839         if match:
840             version_number = match.groups()[0]
841             version = version_number.split('.')
842             if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 13):
843                 if dv2dt == 'dv2dt':
844                     addToRC(r'\converter lyxpreview-lytex ppm "python -tt $$s/scripts/lyxpreview-lytex2bitmap.py" ""')
845                 else:
846                     # set empty converter to override the default imagemagick
847                     addToRC(r'\converter lyxpreview-lytex ppm "" ""')
848                 if dvipng == "dvipng" and dv2dt == 'dv2dt':
849                     addToRC(r'\converter lyxpreview-lytex png "python -tt $$s/scripts/lyxpreview-lytex2bitmap.py" ""')
850                 else:
851                     # set empty converter to override the default imagemagick
852                     addToRC(r'\converter lyxpreview-lytex png "" ""')
853                 # Note: The --lily-output-dir flag is required because lilypond-book
854                 #       does not process input again unless the input has changed,
855                 #       even if the output format being requested is different. So
856                 #       once a .eps file exists, lilypond-book won't create a .pdf
857                 #       even when requested with --pdf. This is a problem if a user
858                 #       clicks View PDF after having done a View DVI. To circumvent
859                 #       this, use different output folders for eps and pdf outputs.
860                 addToRC(r'\converter lilypond-book latex    "lilypond-book --safe --lily-output-dir=ly-eps $$i"                                ""')
861                 addToRC(r'\converter lilypond-book pdflatex "lilypond-book --safe --pdf --latex-program=pdflatex --lily-output-dir=ly-pdf $$i" ""')
862                 logger.info('+  found LilyPond-book version %s.' % version_number)
863             else:
864                 logger.info('+  found LilyPond-book, but version %s is too old.' % version_number)
865         else:
866             logger.info('+  found LilyPond-book, but could not extract version number.')
867     #
868     checkProg('a Noteedit -> LilyPond converter', ['noteedit --export-lilypond $$i'],
869         rc_entry = [ r'\converter noteedit   lilypond   "%%"    ""', ''])
870     #
871     # currently (as of 2/5/11), lyxpak outputs a tar archive if tar is found,
872     # and a zip archive if tar is not found but zip is. if tar is found, then
873     # it looks for gzip and bzip2, in that order, and compresses the archive
874     # using the one it finds.
875     # so, we mimic this search, and configure the appropriate version.
876     path, prog = checkProg('the LyX packing script', ['tar', 'zip'])
877     if prog:
878         cmd = r'\converter lyx %s "python -tt $$s/scripts/lyxpak.py $$r/$$i" ""'
879         if prog == "zip":
880             addToRC(r'\Format lyxzip     zip    "LyX Archive (zip)"     "" "" ""  "document"')
881             addToRC(cmd % "lyxzip")
882         elif prog == "tar":
883             path, prog = checkProg('gzip or bzip2', ['gzip', 'bzip2'])
884             if prog == "gzip":
885                 addToRC(r'\Format lyxgz      gz     "LyX Archive (tar.gz)"  "" "" ""  "document"')
886                 outfmt = "lyxgz"
887             elif prog == "bzip2":
888                 addToRC(r'\Format lyxbz2     bz2    "LyX Archive (tar.bz2)" "" "" ""  "document"')
889                 outfmt = "lyxbz2"
890             else:
891                 addToRC(r'\Format lyxtar     tar    "LyX Archive (tar)"     "" "" ""  "document"')
892                 outfmt = "lyxtar"
893             addToRC(cmd % outfmt)
894         
895     #
896     # FIXME: no rc_entry? comment it out
897     # checkProg('Image converter', ['convert $$i $$o'])
898     #
899     # Entries that do not need checkProg
900     addToRC(r'''\converter lyxpreview-platex ppm        "python -tt $$s/scripts/lyxpreview-platex2bitmap.py"    ""
901 \converter csv        lyx        "python -tt $$s/scripts/csv2lyx.py $$i $$o"    ""
902 \converter date       dateout    "python -tt $$s/scripts/date.py %d-%m-%Y > $$o"        ""
903 \converter docbook    docbook-xml "cp $$i $$o"  "xml"
904 \converter fen        asciichess "python -tt $$s/scripts/fen2ascii.py $$i $$o"  ""
905 \converter lyx        lyx13x     "python -tt $$s/lyx2lyx/lyx2lyx -t 221 $$i > $$o"      ""
906 \converter lyx        lyx14x     "python -tt $$s/lyx2lyx/lyx2lyx -t 245 $$i > $$o"      ""
907 \converter lyx        lyx15x     "python -tt $$s/lyx2lyx/lyx2lyx -t 276 $$i > $$o"      ""
908 \converter lyx        lyx16x     "python -tt $$s/lyx2lyx/lyx2lyx -t 345 $$i > $$o"      ""
909 \converter lyx        clyx       "python -tt $$s/lyx2lyx/lyx2lyx -c big5 -t 245 $$i > $$o"      ""
910 \converter lyx        jlyx       "python -tt $$s/lyx2lyx/lyx2lyx -c euc_jp -t 245 $$i > $$o"    ""
911 \converter lyx        klyx       "python -tt $$s/lyx2lyx/lyx2lyx -c euc_kr -t 245 $$i > $$o"    ""
912 \converter clyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c big5 $$i > $$o"     ""
913 \converter jlyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c euc_jp $$i > $$o"   ""
914 \converter klyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c euc_kr $$i > $$o"   ""
915 ''')
916
917
918 def checkDocBook():
919     ''' Check docbook '''
920     path, DOCBOOK = checkProg('SGML-tools 2.x (DocBook), db2x scripts or xsltproc', ['sgmltools', 'db2dvi', 'xsltproc'],
921         rc_entry = [
922             r'''\converter docbook    dvi        "sgmltools -b dvi $$i" ""
923 \converter docbook    html       "sgmltools -b html $$i"        ""''',
924             r'''\converter docbook    dvi        "db2dvi $$i"   ""
925 \converter docbook    html       "db2html $$i"  ""''',
926             r'''\converter docbook    dvi        ""     ""
927 \converter docbook    html       "" ""''',
928             r'''\converter docbook    dvi        ""     ""
929 \converter docbook    html       ""     ""'''])
930     #
931     if DOCBOOK != '':
932         return ('yes', 'true', '\\def\\hasdocbook{yes}')
933     else:
934         return ('no', 'false', '')
935
936
937 def checkOtherEntries():
938     ''' entries other than Format and Converter '''
939     checkProg('ChkTeX', ['chktex -n1 -n3 -n6 -n9 -n22 -n25 -n30 -n38'],
940         rc_entry = [ r'\chktex_command "%%"' ])
941     checkProgAlternatives('BibTeX or alternative programs', ['bibtex', 'bibtex8', 'biber'],
942         rc_entry = [ r'\bibtex_command "%%"' ],
943         alt_rc_entry = [ r'\bibtex_alternatives "%%"' ])
944     checkProg('a specific Japanese BibTeX variant', ['pbibtex', 'jbibtex', 'bibtex'],
945         rc_entry = [ r'\jbibtex_command "%%"' ])
946     checkProgAlternatives('available index processors', ['texindy', 'makeindex -c -q', 'xindy'],
947         rc_entry = [ r'\index_command "%%"' ],
948         alt_rc_entry = [ r'\index_alternatives "%%"' ])
949     checkProg('an index processor appropriate to Japanese', ['mendex -c -q', 'jmakeindex -c -q', 'makeindex -c -q'],
950         rc_entry = [ r'\jindex_command "%%"' ])
951     path, splitindex = checkProg('the splitindex processor', ['splitindex.pl', 'splitindex'],
952         rc_entry = [ r'\splitindex_command "%%"' ])
953     if splitindex == '':
954         checkProg('the splitindex processor (java version)', ['splitindex.class'],
955             rc_entry = [ r'\splitindex_command "java splitindex"' ])
956     checkProg('a nomenclature processor', ['makeindex'],
957         rc_entry = [ r'\nomencl_command "makeindex -s nomencl.ist"' ])
958     ## FIXME: OCTAVE is not used anywhere
959     # path, OCTAVE = checkProg('Octave', ['octave'])
960     ## FIXME: MAPLE is not used anywhere
961     # path, MAPLE = checkProg('Maple', ['maple'])
962     checkProg('a spool command', ['lp', 'lpr'],
963         rc_entry = [
964             r'''\print_spool_printerprefix "-d "
965 \print_spool_command "lp"''',
966             r'''\print_spool_printerprefix "-P",
967 \print_spool_command "lpr"''',
968             ''])
969     # Add the rest of the entries (no checkProg is required)
970     addToRC(r'''\copier    fig        "python -tt $$s/scripts/fig_copy.py $$i $$o"
971 \copier    pstex      "python -tt $$s/scripts/tex_copy.py $$i $$o $$l"
972 \copier    pdftex     "python -tt $$s/scripts/tex_copy.py $$i $$o $$l"
973 \copier    program    "python -tt $$s/scripts/ext_copy.py $$i $$o"
974 ''')
975
976
977 def processLayoutFile(file, bool_docbook):
978     ''' process layout file and get a line of result
979         
980         Declare lines look like this: (article.layout, scrbook.layout, svjog.layout)
981         
982         \DeclareLaTeXClass{article}
983         \DeclareLaTeXClass[scrbook]{book (koma-script)}
984         \DeclareLaTeXClass[svjour,svjog.clo]{article (Springer - svjour/jog)}
985
986         we expect output:
987         
988         "article" "article" "article" "false" "article.cls"
989         "scrbook" "scrbook" "book (koma-script)" "false" "scrbook.cls"
990         "svjog" "svjour" "article (Springer - svjour/jog)" "false" "svjour.cls,svjog.clo"
991     '''
992     def checkForClassExtension(x):
993         '''if the extension for a latex class is not
994            provided, add .cls to the classname'''
995         if not '.' in x:
996             return x.strip() + '.cls'
997         else:
998             return x.strip()
999     classname = file.split(os.sep)[-1].split('.')[0]
1000     # return ('LaTeX', '[a,b]', 'a', ',b,c', 'article') for \DeclareLaTeXClass[a,b,c]{article}
1001     p = re.compile(r'\Declare(LaTeX|DocBook)Class\s*(\[([^,]*)(,.*)*\])*\s*{(.*)}')
1002     for line in open(file).readlines():
1003         res = p.search(line)
1004         if res != None:
1005             (classtype, optAll, opt, opt1, desc) = res.groups()
1006             avai = {'LaTeX':'false', 'DocBook':bool_docbook}[classtype]
1007             if opt == None:
1008                 opt = classname
1009                 prereq_latex = checkForClassExtension(classname)
1010             else:
1011                 prereq_list = optAll[1:-1].split(',')
1012                 prereq_list = map(checkForClassExtension, prereq_list)
1013                 prereq_latex = ','.join(prereq_list)
1014             prereq_docbook = {'true':'', 'false':'docbook'}[bool_docbook]
1015             prereq = {'LaTeX':prereq_latex, 'DocBook':prereq_docbook}[classtype]
1016             return '"%s" "%s" "%s" "%s" "%s"\n' % (classname, opt, desc, avai, prereq)
1017     logger.warning("Layout file " + file + " has no \DeclareXXClass line. ")
1018     return ""
1019
1020
1021 def checkLatexConfig(check_config, bool_docbook):
1022     ''' Explore the LaTeX configuration 
1023         Return None (will be passed to sys.exit()) for success.
1024     '''
1025     msg = 'checking LaTeX configuration... '
1026     # if --without-latex-config is forced, or if there is no previous 
1027     # version of textclass.lst, re-generate a default file.
1028     if not os.path.isfile('textclass.lst') or not check_config:
1029         # remove the files only if we want to regenerate
1030         removeFiles(['textclass.lst', 'packages.lst'])
1031         #
1032         # Then, generate a default textclass.lst. In case configure.py
1033         # fails, we still have something to start lyx.
1034         logger.info(msg + ' default values')
1035         logger.info('+checking list of textclasses... ')
1036         tx = open('textclass.lst', 'w')
1037         tx.write('''
1038 # This file declares layouts and their associated definition files
1039 # (include dir. relative to the place where this file is).
1040 # It contains only default values, since chkconfig.ltx could not be run
1041 # for some reason. Run ./configure.py if you need to update it after a
1042 # configuration change.
1043 ''')
1044         # build the list of available layout files and convert it to commands
1045         # for chkconfig.ltx
1046         foundClasses = []
1047         for file in glob.glob( os.path.join('layouts', '*.layout') ) + \
1048             glob.glob( os.path.join(srcdir, 'layouts', '*.layout' ) ) :
1049             # valid file?
1050             if not os.path.isfile(file): 
1051                 continue
1052             # get stuff between /xxxx.layout .
1053             classname = file.split(os.sep)[-1].split('.')[0]
1054             #  tr ' -' '__'`
1055             cleanclass = classname.replace(' ', '_')
1056             cleanclass = cleanclass.replace('-', '_')
1057             # make sure the same class is not considered twice
1058             if foundClasses.count(cleanclass) == 0: # not found before
1059                 foundClasses.append(cleanclass)
1060                 retval = processLayoutFile(file, bool_docbook)
1061                 if retval != "":
1062                     tx.write(retval)
1063         tx.close()
1064         logger.info('\tdone')
1065     if not check_config:
1066         return None
1067     # the following will generate textclass.lst.tmp, and packages.lst.tmp
1068     logger.info(msg + '\tauto')
1069     removeFiles(['wrap_chkconfig.ltx', 'chkconfig.vars', \
1070         'chkconfig.classes', 'chklayouts.tex'])
1071     rmcopy = False
1072     if not os.path.isfile( 'chkconfig.ltx' ):
1073         shutil.copyfile( os.path.join(srcdir, 'chkconfig.ltx'), 'chkconfig.ltx' )
1074         rmcopy = True
1075     writeToFile('wrap_chkconfig.ltx', '%s\n\\input{chkconfig.ltx}\n' % docbook_cmd)
1076     # Construct the list of classes to test for.
1077     # build the list of available layout files and convert it to commands
1078     # for chkconfig.ltx
1079     declare = re.compile(r'\Declare(LaTeX|DocBook)Class\s*(\[([^,]*)(,.*)*\])*\s*{(.*)}')
1080     empty = re.compile(r'^\s*$')
1081     testclasses = list()
1082     for file in glob.glob( os.path.join('layouts', '*.layout') ) + \
1083         glob.glob( os.path.join(srcdir, 'layouts', '*.layout' ) ) :
1084         if not os.path.isfile(file):
1085             continue
1086         classname = file.split(os.sep)[-1].split('.')[0]
1087         for line in open(file).readlines():
1088             if not empty.match(line) and line[0] != '#':
1089                 logger.error("Failed to find \Declare line for layout file `" + file + "'")
1090                 sys.exit(3)
1091             if declare.search(line) == None:
1092                 continue
1093             testclasses.append("\\TestDocClass{%s}{%s}" % (classname, line[1:].strip()))
1094             break
1095     testclasses.sort()
1096     cl = open('chklayouts.tex', 'w')
1097     for line in testclasses:
1098         cl.write(line + '\n')
1099     cl.close()
1100     #
1101     # we have chklayouts.tex, then process it
1102     fout = os.popen(LATEX + ' wrap_chkconfig.ltx')
1103     while True:
1104         line = fout.readline()
1105         if not line:
1106             break;
1107         if re.match('^\+', line):
1108             logger.info(line.strip())
1109     # if the command succeeds, None will be returned
1110     ret = fout.close()
1111     #
1112     # currently, values in chhkconfig are only used to set
1113     # \font_encoding
1114     values = {}
1115     for line in open('chkconfig.vars').readlines():
1116         key, val = re.sub('-', '_', line).split('=')
1117         val = val.strip()
1118         values[key] = val.strip("'")
1119     # chk_fontenc may not exist 
1120     try:
1121         addToRC(r'\font_encoding "%s"' % values["chk_fontenc"])
1122     except:
1123         pass
1124     if rmcopy:   # remove the copied file
1125         removeFiles( [ 'chkconfig.ltx' ] )
1126     # if configure successed, move textclass.lst.tmp to textclass.lst
1127     # and packages.lst.tmp to packages.lst
1128     if os.path.isfile('textclass.lst.tmp') and len(open('textclass.lst.tmp').read()) > 0 \
1129         and os.path.isfile('packages.lst.tmp') and len(open('packages.lst.tmp').read()) > 0:
1130         shutil.move('textclass.lst.tmp', 'textclass.lst')
1131         shutil.move('packages.lst.tmp', 'packages.lst')
1132     return ret
1133
1134
1135 def checkModulesConfig():
1136   removeFiles(['lyxmodules.lst', 'chkmodules.tex'])
1137
1138   logger.info('+checking list of modules... ')
1139   tx = open('lyxmodules.lst', 'w')
1140   tx.write('''## This file declares modules and their associated definition files.
1141 ## It has been automatically generated by configure
1142 ## Use "Options/Reconfigure" if you need to update it after a
1143 ## configuration change. 
1144 ## "ModuleName" "filename" "Description" "Packages" "Requires" "Excludes" "Category"
1145 ''')
1146   # build the list of available modules
1147   foundClasses = []
1148   for file in glob.glob( os.path.join('layouts', '*.module') ) + \
1149       glob.glob( os.path.join(srcdir, 'layouts', '*.module' ) ) :
1150       # valid file?
1151       logger.info(file)
1152       if not os.path.isfile(file): 
1153           continue
1154       retval = processModuleFile(file, bool_docbook)
1155       if retval != "":
1156           tx.write(retval)
1157   tx.close()
1158   logger.info('\tdone')
1159
1160
1161 def processModuleFile(file, bool_docbook):
1162     ''' process module file and get a line of result
1163
1164         The top of a module file should look like this:
1165           #\DeclareLyXModule[LaTeX Packages]{ModuleName}
1166           #DescriptionBegin
1167           #...body of description...
1168           #DescriptionEnd
1169           #Requires: [list of required modules]
1170           #Excludes: [list of excluded modules]
1171           #Category: [category name]
1172         The last three lines are optional (though do give a category).
1173         We expect output:
1174           "ModuleName" "filename" "Description" "Packages" "Requires" "Excludes" "Category"
1175     '''
1176     remods = re.compile(r'\DeclareLyXModule\s*(?:\[([^]]*?)\])?{(.*)}')
1177     rereqs = re.compile(r'#+\s*Requires: (.*)')
1178     reexcs = re.compile(r'#+\s*Excludes: (.*)')
1179     recaty = re.compile(r'#+\s*Category: (.*)')
1180     redbeg = re.compile(r'#+\s*DescriptionBegin\s*$')
1181     redend = re.compile(r'#+\s*DescriptionEnd\s*$')
1182
1183     modname = desc = pkgs = req = excl = catgy = ""
1184     readingDescription = False
1185     descLines = []
1186     filename = file.split(os.sep)[-1]
1187     filename = filename[:-7]
1188
1189     for line in open(file).readlines():
1190       if readingDescription:
1191         res = redend.search(line)
1192         if res != None:
1193           readingDescription = False
1194           desc = " ".join(descLines)
1195           # Escape quotes.
1196           desc = desc.replace('"', '\\"')
1197           continue
1198         descLines.append(line[1:].strip())
1199         continue
1200       res = redbeg.search(line)
1201       if res != None:
1202         readingDescription = True
1203         continue
1204       res = remods.search(line)
1205       if res != None:
1206           (pkgs, modname) = res.groups()
1207           if pkgs == None:
1208             pkgs = ""
1209           else:
1210             tmp = [s.strip() for s in pkgs.split(",")]
1211             pkgs = ",".join(tmp)
1212           continue
1213       res = rereqs.search(line)
1214       if res != None:
1215         req = res.group(1)
1216         tmp = [s.strip() for s in req.split("|")]
1217         req = "|".join(tmp)
1218         continue
1219       res = reexcs.search(line)
1220       if res != None:
1221         excl = res.group(1)
1222         tmp = [s.strip() for s in excl.split("|")]
1223         excl = "|".join(tmp)
1224         continue
1225       res = recaty.search(line)
1226       if res != None:
1227         catgy = res.group(1)
1228         continue
1229
1230     if modname == "":
1231       logger.warning("Module file without \DeclareLyXModule line. ")
1232       return ""
1233
1234     if pkgs != "":
1235         # this module has some latex dependencies:
1236         # append the dependencies to chkmodules.tex,
1237         # which is \input'ed by chkconfig.ltx
1238         testpackages = list()
1239         for pkg in pkgs.split(","):
1240             if "->" in pkg:
1241                 # this is a converter dependency: skip
1242                 continue
1243             if pkg.endswith(".sty"):
1244                 pkg = pkg[:-4]
1245             testpackages.append("\\TestPackage{%s}" % (pkg,))
1246         cm = open('chkmodules.tex', 'a')
1247         for line in testpackages:
1248             cm.write(line + '\n')
1249         cm.close()
1250
1251     return '"%s" "%s" "%s" "%s" "%s" "%s" "%s"\n' % (modname, filename, desc, pkgs, req, excl, catgy)
1252     
1253
1254
1255 def checkTeXAllowSpaces():
1256     ''' Let's check whether spaces are allowed in TeX file names '''
1257     tex_allows_spaces = 'false'
1258     if lyx_check_config:
1259         msg = "Checking whether TeX allows spaces in file names... "
1260         writeToFile('a b.tex', r'\message{working^^J}' )
1261         if LATEX != '':
1262             if os.name == 'nt':
1263                 latex_out = cmdOutput(LATEX + r""" "\nonstopmode\input{\"a b\"}" """)
1264             else:
1265                 latex_out = cmdOutput(LATEX + r""" '\nonstopmode\input{"a b"}' """)
1266         else:
1267             latex_out = ''
1268         if 'working' in latex_out:
1269             logger.info(msg + 'yes')
1270             tex_allows_spaces = 'true'
1271         else:
1272             logger.info(msg + 'no')
1273             tex_allows_spaces = 'false'
1274         addToRC(r'\tex_allows_spaces ' + tex_allows_spaces)
1275         removeFiles( [ 'a b.tex', 'a b.log', 'texput.log' ])
1276
1277
1278 def removeTempFiles():
1279     # Final clean-up
1280     if not lyx_keep_temps:
1281         removeFiles(['chkconfig.vars',  \
1282             'wrap_chkconfig.ltx', 'wrap_chkconfig.log', \
1283             'chklayouts.tex', 'chkmodules.tex', 'missfont.log', 
1284             'chklatex.ltx', 'chklatex.log'])
1285
1286
1287 if __name__ == '__main__':
1288     lyx_check_config = True
1289     outfile = 'lyxrc.defaults'
1290     rc_entries = ''
1291     lyx_keep_temps = False
1292     version_suffix = ''
1293     ## Parse the command line
1294     for op in sys.argv[1:]:   # default shell/for list is $*, the options
1295         if op in [ '-help', '--help', '-h' ]:
1296             print '''Usage: configure [options]
1297 Options:
1298     --help                   show this help lines
1299     --keep-temps             keep temporary files (for debug. purposes)
1300     --without-latex-config   do not run LaTeX to determine configuration
1301     --with-version-suffix=suffix suffix of binary installed files
1302 '''
1303             sys.exit(0)
1304         elif op == '--without-latex-config':
1305             lyx_check_config = False
1306         elif op == '--keep-temps':
1307             lyx_keep_temps = True
1308         elif op[0:22] == '--with-version-suffix=':  # never mind if op is not long enough
1309             version_suffix = op[22:]
1310         else:
1311             print "Unknown option", op
1312             sys.exit(1)
1313     #
1314     # check if we run from the right directory
1315     srcdir = os.path.dirname(sys.argv[0])
1316     if srcdir == '':
1317         srcdir = '.'
1318     if not os.path.isfile( os.path.join(srcdir, 'chkconfig.ltx') ):
1319         logger.error("configure: error: cannot find chkconfig.ltx script")
1320         sys.exit(1)
1321     setEnviron()
1322     createDirectories()
1323     windows_style_tex_paths = checkTeXPaths()
1324     dtl_tools = checkDTLtools()
1325     ## Write the first part of outfile
1326     writeToFile(outfile, '''# This file has been automatically generated by LyX' lib/configure.py
1327 # script. It contains default settings that have been determined by
1328 # examining your system. PLEASE DO NOT MODIFY ANYTHING HERE! If you
1329 # want to customize LyX, use LyX' Preferences dialog or modify directly 
1330 # the "preferences" file instead. Any setting in that file will
1331 # override the values given here.
1332 ''')
1333     # check latex
1334     LATEX = checkLatex(dtl_tools)
1335     checkFormatEntries(dtl_tools)
1336     checkConverterEntries()
1337     (chk_docbook, bool_docbook, docbook_cmd) = checkDocBook()
1338     checkTeXAllowSpaces()
1339     if windows_style_tex_paths != '':
1340         addToRC(r'\tex_expects_windows_paths %s' % windows_style_tex_paths)
1341     checkOtherEntries()
1342     checkModulesConfig()
1343     # --without-latex-config can disable lyx_check_config
1344     ret = checkLatexConfig(lyx_check_config and LATEX != '', bool_docbook)
1345     removeTempFiles()
1346     # The return error code can be 256. Because most systems expect an error code
1347     # in the range 0-127, 256 can be interpretted as 'success'. Because we expect
1348     # a None for success, 'ret is not None' is used to exit.
1349     sys.exit(ret is not None)