]> git.lyx.org Git - lyx.git/blob - lib/configure.py
Re-remerge po files
[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)
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 latex      tex    "LaTeX (plain)"         L  "" "%%"    "document"
509 \Format pdflatex   tex    "LaTeX (pdflatex)"      "" "" "%%"    "document"
510 \Format xetex      tex    "LaTeX (XeTeX)"         "" "" "%%"    "document"
511 \Format text       txt    "Plain text"            a  "" "%%"    "document"
512 \Format text2      txt    "Plain text (pstotext)" "" "" "%%"    "document"
513 \Format text3      txt    "Plain text (ps2ascii)" "" "" "%%"    "document"
514 \Format text4      txt    "Plain text (catdvi)"   "" "" "%%"    "document"
515 \Format textparagraph txt "Plain Text, Join Lines" "" ""        "%%"    "document"''' ])
516  #
517     path, xhtmlview = checkViewer('an HTML previewer', ['firefox', 'mozilla file://$$p$$i', 'netscape'],
518         rc_entry = [r'\Format xhtml      xhtml   "LyXHTML"              X "%%" ""    "document"'])
519     if xhtmlview == "":
520         addToRC(r'\Format xhtml      xhtml   "LyXHTML"              X "" ""  "document"')
521  #
522     checkEditor('a BibTeX editor', ['sensible-editor', 'jabref', 'JabRef', \
523         'pybliographic', 'bibdesk', 'gbib', 'kbib', \
524         'kbibtex', 'sixpack', 'bibedit', 'tkbibtex' \
525         'xemacs', 'gvim', 'kedit', 'kwrite', 'kate', \
526         'nedit', 'gedit', 'notepad'],
527         rc_entry = [r'''\Format bibtex bib    "BibTeX"         "" ""    "%%"    ""''' ])
528     #
529     #checkProg('a Postscript interpreter', ['gs'],
530     #  rc_entry = [ r'\ps_command "%%"' ])
531     checkViewer('a Postscript previewer', ['kghostview', 'okular', 'evince', 'gv', 'ghostview -swap'],
532         rc_entry = [r'''\Format eps        eps     EPS                    "" "%%"       ""      "vector"
533 \Format ps         ps      Postscript             t  "%%"       ""      "document,vector"'''])
534     # for xdg-open issues look here: http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg151818.html
535     checkViewer('a PDF previewer', ['kpdf', 'okular', 'evince', 'kghostview', 'xpdf', 'acrobat', 'acroread', \
536                     'gv', 'ghostview'],
537         rc_entry = [r'''\Format pdf        pdf    "PDF (ps2pdf)"          P  "%%"       ""      "document,vector"
538 \Format pdf2       pdf    "PDF (pdflatex)"        F  "%%"       ""      "document,vector"
539 \Format pdf3       pdf    "PDF (dvipdfm)"         m  "%%"       ""      "document,vector"
540 \Format pdf4       pdf    "PDF (XeTeX)"           X  "%%"       ""      "document,vector"'''])
541     #
542     checkViewer('a DVI previewer', ['xdvi', 'kdvi', 'okular', 'yap', 'dviout -Set=!m'],
543         rc_entry = [r'\Format dvi        dvi     DVI                    D  "%%" ""      "document,vector"'])
544     if dtl_tools:
545         # Windows only: DraftDVI
546         addToRC(r'\Format dvi2       dvi     DraftDVI               ""  ""      ""      "vector"')
547     #
548     checkViewer('an HTML previewer', ['firefox', 'mozilla file://$$p$$i', 'netscape'],
549         rc_entry = [r'\Format html       html    HTML                   H  "%%" ""      "document"'])
550     #
551     checkViewerEditor('Noteedit', ['noteedit'],
552         rc_entry = [r'\Format noteedit   not     Noteedit               "" "%%" "%%"    "vector"'])
553     #
554     checkViewerEditor('an OpenDocument/OpenOffice viewer', ['swriter', 'oowriter', 'abiword'],
555         rc_entry = [r'''\Format odt        odt     OpenDocument           "" "%%"       "%%"    "document,vector"
556 \Format sxw        sxw    "OpenOffice.Org (sxw)"  "" "" ""      "document,vector"'''])
557     # 
558     checkViewerEditor('a Rich Text and Word viewer', ['swriter', 'oowriter', 'abiword'],
559         rc_entry = [r'''\Format rtf        rtf    "Rich Text Format"      "" "" ""      "document,vector"
560 \Format word       doc    "MS Word"               W  "" ""      "document,vector"'''])
561     #
562     # entried that do not need checkProg
563     addToRC(r'''\Format date       ""     "date command"          "" "" ""      ""
564 \Format csv        csv    "Table (CSV)"  "" ""  ""      "document"
565 \Format fax        ""      Fax                    "" "" ""      "document"
566 \Format lyx        lyx     LyX                    "" "" ""      ""
567 \Format lyx13x     lyx13  "LyX 1.3.x"             "" "" ""      "document"
568 \Format lyx14x     lyx14  "LyX 1.4.x"             "" "" ""      "document"
569 \Format lyx15x     lyx15  "LyX 1.5.x"             "" "" ""      "document"
570 \Format lyx16x     lyx16  "LyX 1.6.x"             "" "" ""      "document"
571 \Format clyx       cjklyx "CJK LyX 1.4.x (big5)"  "" "" ""      "document"
572 \Format jlyx       cjklyx "CJK LyX 1.4.x (euc-jp)" "" ""        ""      "document"
573 \Format klyx       cjklyx "CJK LyX 1.4.x (euc-kr)" "" ""        ""      "document"
574 \Format lyxpreview lyxpreview "LyX Preview"       "" "" ""      ""
575 \Format lyxpreview-platex lyxpreview-platex "LyX Preview (pLaTeX)"       "" ""  ""      ""
576 \Format pdftex     pdftex_t PDFTEX                "" "" ""      ""
577 \Format program    ""      Program                "" "" ""      ""
578 \Format pstex      pstex_t PSTEX                  "" "" ""      ""
579 \Format wmf        wmf    "Windows Metafile"      "" "" ""      "vector"
580 \Format emf        emf    "Enhanced Metafile"     "" "" ""      "vector"
581 \Format wordhtml   html   "HTML (MS Word)"        "" "" ""      "document"
582 ''')
583
584
585 def checkConverterEntries():
586     ''' Check all converters (\converter entries) '''
587     checkProg('the pdflatex program', ['pdflatex $$i'],
588         rc_entry = [ r'\converter pdflatex   pdf2       "%%"    "latex"' ])
589
590     checkProg('XeTeX', ['xelatex $$i'],
591         rc_entry = [ r'\converter xetex      pdf4       "%%"    "latex"' ])
592     
593     ''' If we're running LyX in-place then tex2lyx will be found in
594             ../src/tex2lyx. Add this directory to the PATH temporarily and
595             search for tex2lyx.
596             Use PATH to avoid any problems with paths-with-spaces.
597     '''
598     path_orig = os.environ["PATH"]
599     os.environ["PATH"] = os.path.join('..', 'src', 'tex2lyx') + \
600         os.pathsep + path_orig
601
602     checkProg('a LaTeX/Noweb -> LyX converter', ['tex2lyx', 'tex2lyx' + version_suffix],
603         rc_entry = [r'''\converter latex      lyx        "%% -f $$i $$o"        ""
604 \converter literate   lyx        "%% -n -f $$i $$o"     ""'''])
605
606     os.environ["PATH"] = path_orig
607
608     #
609     checkProg('a Noweb -> LaTeX converter', ['noweave -delay -index $$i > $$o'],
610         rc_entry = [r'''\converter literate   latex      "%%"   ""
611 \converter literate   pdflatex      "%%"        ""'''])
612     #
613     checkProg('a Sweave -> LaTeX converter', ['R CMD Sweave $$i'],
614         rc_entry = [r'''\converter sweave   latex      "%%"     ""
615 \converter sweave   pdflatex      "%%"  ""'''])
616     #
617     checkProg('an HTML -> LaTeX converter', ['html2latex $$i', 'gnuhtml2latex $$i', \
618         'htmltolatex -input $$i -output $$o', 'java -jar htmltolatex.jar -input $$i -output $$o'],
619         rc_entry = [ r'\converter html       latex      "%%"    ""' ])
620     #
621     checkProg('an MS Word -> LaTeX converter', ['wvCleanLatex $$i $$o'],
622         rc_entry = [ r'\converter word       latex      "%%"    ""' ])
623
624     # eLyXer: search as a Python module and then as an executable (elyxer.py, elyxer)
625     elyxerfound = checkModule('elyxer')
626     if elyxerfound:
627       addToRC(r'''\converter lyx      html       "python -m elyxer --directory $$r $$i $$o"     ""''')
628     else:
629       path, elyxer = checkProg('a LyX -> HTML converter',
630         ['elyxer.py --directory $$r $$i $$o', 'elyxer --directory $$r $$i $$o'],
631         rc_entry = [ r'\converter lyx      html       "%%"      ""' ])
632       if elyxer.find('elyxer') >= 0:
633         elyxerfound = True
634
635     if elyxerfound:
636       addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py -e html,png,jpg,jpeg,css $$i $$o"''')
637     else:
638       # search for other converters than eLyXer
639       # On SuSE the scripts have a .sh suffix, and on debian they are in /usr/share/tex4ht/
640       path, htmlconv = checkProg('a LaTeX -> HTML converter', ['htlatex $$i', 'htlatex.sh $$i', \
641           '/usr/share/tex4ht/htlatex $$i', 'tth  -t -e2 -L$$b < $$i > $$o', \
642           'latex2html -no_subdir -split 0 -show_section_numbers $$i', 'hevea -s $$i'],
643           rc_entry = [ r'\converter latex      html       "%%"  "needaux"' ])
644       if htmlconv.find('htlatex') >= 0 or htmlconv == 'latex2html':
645         addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py -e html,png,css $$i $$o"''')
646       else:
647         addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py $$i $$o"''')
648
649     # Check if LyxBlogger is installed.
650     path, lyxblogger = checkProg('A LyX to WordPress Blog Publishing Tool',
651       ['lyxblogger $$i'], rc_entry = [])
652     if lyxblogger.find('lyxblogger') >= 0:
653       addToRC(r'\Format    blog       blog       "LyxBlogger"           "" "" ""  "document"')
654       addToRC(r'\converter xhtml      blog       "lyxblogger $$i"       ""')
655
656     if elyxerfound:
657       addToRC(r'''\converter lyx      wordhtml       "python -m elyxer --html --directory $$r $$i $$o"  ""''')
658     else:
659       path, elyxer = checkProg('a LyX -> MS Word converter',
660         ['elyxer.py --directory $$r $$i $$o', 'elyxer --html --directory $$r $$i $$o'],
661         rc_entry = [ r'\converter lyx      wordhtml       "%%"  ""' ])
662       if elyxer.find('elyxer') >= 0:
663         elyxerfound = True
664
665     if elyxerfound:
666       addToRC(r'''\copier    wordhtml       "python -tt $$s/scripts/ext_copy.py -e html,png,jpg,jpeg,css $$i $$o"''')
667     else:
668       # search for other converters than eLyXer
669       # On SuSE the scripts have a .sh suffix, and on debian they are in /usr/share/tex4ht/
670       path, htmlconv = checkProg('a LaTeX -> MS Word converter', ["htlatex $$i 'html,word' 'symbol/!' '-cvalidate'", \
671           "htlatex.sh $$i 'html,word' 'symbol/!' '-cvalidate'", \
672           "/usr/share/tex4ht/htlatex $$i 'html,word' 'symbol/!' '-cvalidate'"],
673           rc_entry = [ r'\converter latex      wordhtml   "%%"  "needaux"' ])
674       if htmlconv.find('htlatex') >= 0:
675         addToRC(r'''\copier    wordhtml       "python -tt $$s/scripts/ext_copy.py -e html,png,css $$i $$o"''')
676       else:
677         addToRC(r'''\copier    wordhtml       "python -tt $$s/scripts/ext_copy.py $$i $$o"''')
678
679     #
680     checkProg('an OpenOffice.org -> LaTeX converter', ['w2l -clean $$i'],
681         rc_entry = [ r'\converter sxw        latex      "%%"    ""' ])
682     #
683     checkProg('an OpenDocument -> LaTeX converter', ['w2l -clean $$i'],
684         rc_entry = [ r'\converter odt        latex      "%%"    ""' ])
685     # According to http://www.tug.org/applications/tex4ht/mn-commands.html
686     # the command mk4ht oolatex $$i has to be used as default,
687     # but as this would require to have Perl installed, in MiKTeX oolatex is
688     # directly available as application.
689     # On SuSE the scripts have a .sh suffix, and on debian they are in /usr/share/tex4ht/
690     # Both SuSE and debian have oolatex
691     checkProg('a LaTeX -> Open Document converter', [
692         'oolatex $$i', 'mk4ht oolatex $$i', 'oolatex.sh $$i', '/usr/share/tex4ht/oolatex $$i',
693         'htlatex $$i \'xhtml,ooffice\' \'ooffice/! -cmozhtf\' \'-coo\' \'-cvalidate\''],
694         rc_entry = [ r'\converter latex      odt        "%%"    "needaux"' ])
695     # On windows it is called latex2rt.exe
696     checkProg('a LaTeX -> RTF converter', ['latex2rtf -p -S -o $$o $$i', 'latex2rt -p -S -o $$o $$i'],
697         rc_entry = [ r'\converter latex      rtf        "%%"    "needaux"' ])
698     #
699     checkProg('a RTF -> HTML converter', ['unrtf --html  $$i > $$o'],
700         rc_entry = [ r'\converter rtf      html        "%%"     ""' ])
701     #
702     checkProg('a PS to PDF converter', ['ps2pdf13 $$i $$o'],
703         rc_entry = [ r'\converter ps         pdf        "%%"    ""' ])
704     #
705     checkProg('a PS to TXT converter', ['pstotext $$i > $$o'],
706         rc_entry = [ r'\converter ps         text2      "%%"    ""' ])
707     #
708     checkProg('a PS to TXT converter', ['ps2ascii $$i $$o'],
709         rc_entry = [ r'\converter ps         text3      "%%"    ""' ])
710     #
711     checkProg('a PS to EPS converter', ['ps2eps $$i'],
712         rc_entry = [ r'\converter ps         eps      "%%"      ""' ])
713     #
714     checkProg('a PDF to PS converter', ['pdf2ps $$i $$o', 'pdftops $$i $$o'],
715         rc_entry = [ r'\converter pdf         ps        "%%"    ""' ])
716     #
717     checkProg('a PDF to EPS converter', ['pdftops -eps -f 1 -l 1 $$i $$o'],
718         rc_entry = [ r'\converter pdf         eps        "%%"   ""' ])
719     #
720     checkProg('a DVI to TXT converter', ['catdvi $$i > $$o'],
721         rc_entry = [ r'\converter dvi        text4      "%%"    ""' ])
722     #
723     checkProg('a DVI to PS converter', ['dvips -o $$o $$i'],
724         rc_entry = [ r'\converter dvi        ps         "%%"    ""' ])
725     #
726     checkProg('a DVI to PDF converter', ['dvipdfmx -o $$o $$i', 'dvipdfm -o $$o $$i'],
727         rc_entry = [ r'\converter dvi        pdf3       "%%"    ""' ])
728     #
729     path, dvipng = checkProg('dvipng', ['dvipng'])
730     if dvipng == "dvipng":
731         addToRC(r'\converter lyxpreview png        "python -tt $$s/scripts/lyxpreview2bitmap.py"        ""')
732     else:
733         addToRC(r'\converter lyxpreview png        ""   ""')
734     #  
735     checkProg('a fax program', ['kdeprintfax $$i', 'ksendfax $$i', 'hylapex $$i'],
736         rc_entry = [ r'\converter ps         fax        "%%"    ""'])
737     #
738     checkProg('a FIG -> EPS/PPM converter', ['fig2dev'],
739         rc_entry = [
740             r'''\converter fig        eps        "fig2dev -L eps $$i $$o"       ""
741 \converter fig        ppm        "fig2dev -L ppm $$i $$o"       ""
742 \converter fig        png        "fig2dev -L png $$i $$o"       ""''',
743             ''])
744     #
745     checkProg('a TIFF -> PS converter', ['tiff2ps $$i > $$o'],
746         rc_entry = [ r'\converter tiff       eps        "%%"    ""', ''])
747     #
748     checkProg('a TGIF -> EPS/PPM converter', ['tgif'],
749         rc_entry = [
750             r'''\converter tgif       eps        "tgif -print -color -eps -stdout $$i > $$o"    ""
751 \converter tgif       png        "tgif -print -color -png -o $$d $$i"   ""
752 \converter tgif       pdf        "tgif -print -color -pdf -stdout $$i > $$o"    ""''',
753             ''])
754     #
755     checkProg('a WMF -> EPS converter', ['metafile2eps $$i $$o', 'wmf2eps -o $$o $$i'],
756         rc_entry = [ r'\converter wmf        eps        "%%"    ""'])
757     #
758     checkProg('an EMF -> EPS converter', ['metafile2eps $$i $$o', 'wmf2eps -o $$o $$i'],
759         rc_entry = [ r'\converter emf        eps        "%%"    ""'])
760     #
761     checkProg('an EPS -> PDF converter', ['epstopdf'],
762         rc_entry = [ r'\converter eps        pdf        "epstopdf --outfile=$$o $$i"    ""', ''])
763     #
764     # no agr -> pdf converter, since the pdf library used by gracebat is not
765     # free software and therefore not compiled in in many installations.
766     # Fortunately, this is not a big problem, because we will use epstopdf to
767     # convert from agr to pdf via eps without loss of quality.
768     checkProg('a Grace -> Image converter', ['gracebat'],
769         rc_entry = [
770             r'''\converter agr        eps        "gracebat -hardcopy -printfile $$o -hdevice EPS $$i 2>/dev/null"       ""
771 \converter agr        png        "gracebat -hardcopy -printfile $$o -hdevice PNG $$i 2>/dev/null"       ""
772 \converter agr        jpg        "gracebat -hardcopy -printfile $$o -hdevice JPEG $$i 2>/dev/null"      ""
773 \converter agr        ppm        "gracebat -hardcopy -printfile $$o -hdevice PNM $$i 2>/dev/null"       ""''',
774             ''])
775     #
776     checkProg('a Dot -> Image converter', ['dot'],
777         rc_entry = [
778             r'''\converter dot        eps        "dot -Teps $$i -o $$o" ""
779 \converter dot        pdf        "dot -Tpdf $$i -o $$o" ""
780 \converter dot        png        "dot -Tpng $$i -o $$o" ""''',
781             ''])
782     #
783     checkProg('a Dia -> PNG converter', ['dia -e $$o -t png $$i'],
784         rc_entry = [ r'\converter dia        png        "%%"    ""'])
785     #
786     checkProg('a Dia -> EPS converter', ['dia -e $$o -t eps $$i'],
787         rc_entry = [ r'\converter dia        eps        "%%"    ""'])
788     #
789     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'],
790         rc_entry = [ r'\converter svg        pdf        "%%"    ""'])
791     #
792     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'],
793         rc_entry = [ r'\converter svg        eps        "%%"    ""'])
794     # the PNG export via Inkscape must not have the full path ($$p) for the file
795     checkProg('a SVG -> PNG converter', ['rsvg-convert -f png -o $$o $$i', 'inkscape --without-gui --file=$$i --export-png=$$o'],
796         rc_entry = [ r'\converter svg        png        "%%"    ""'])
797     
798     #
799     path, lilypond = checkProg('a LilyPond -> EPS/PDF/PNG converter', ['lilypond'])
800     if (lilypond != ''):
801         version_string = cmdOutput("lilypond --version")
802         match = re.match('GNU LilyPond (\S+)', version_string)
803         if match:
804             version_number = match.groups()[0]
805             version = version_number.split('.')
806             if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 11):
807                 addToRC(r'''\converter lilypond   eps        "lilypond -dbackend=eps --ps $$i"  ""
808 \converter lilypond   png        "lilypond -dbackend=eps --png $$i"     ""''')
809                 addToRC(r'\converter lilypond   pdf        "lilypond -dbackend=eps --pdf $$i"   ""')
810                 print '+  found LilyPond version %s.' % version_number
811             elif int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 6):
812                 addToRC(r'''\converter lilypond   eps        "lilypond -b eps --ps $$i" ""
813 \converter lilypond   png        "lilypond -b eps --png $$i"    ""''')
814                 if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 9):
815                     addToRC(r'\converter lilypond   pdf        "lilypond -b eps --pdf $$i"      ""')
816                 logger.info('+  found LilyPond version %s.' % version_number)
817             else:
818                 logger.info('+  found LilyPond, but version %s is too old.' % version_number)
819         else:
820             logger.info('+  found LilyPond, but could not extract version number.')
821     #
822     checkProg('a Noteedit -> LilyPond converter', ['noteedit --export-lilypond $$i'],
823         rc_entry = [ r'\converter noteedit   lilypond   "%%"    ""', ''])
824     #
825     # FIXME: no rc_entry? comment it out
826     # checkProg('Image converter', ['convert $$i $$o'])
827     #
828     # Entries that do not need checkProg
829     addToRC(r'''\converter lyxpreview ppm        "python -tt $$s/scripts/lyxpreview2bitmap.py"  ""
830 \converter lyxpreview-platex ppm        "python -tt $$s/scripts/lyxpreview-platex2bitmap.py"    ""
831 \converter csv        lyx        "python -tt $$s/scripts/csv2lyx.py $$i $$o"    ""
832 \converter date       dateout    "python -tt $$s/scripts/date.py %d-%m-%Y > $$o"        ""
833 \converter docbook    docbook-xml "cp $$i $$o"  "xml"
834 \converter fen        asciichess "python -tt $$s/scripts/fen2ascii.py $$i $$o"  ""
835 \converter fig        pdftex     "python -tt $$s/scripts/fig2pdftex.py $$i $$o" ""
836 \converter fig        pstex      "python -tt $$s/scripts/fig2pstex.py $$i $$o"  ""
837 \converter lyx        lyx13x     "python -tt $$s/lyx2lyx/lyx2lyx -t 221 $$i > $$o"      ""
838 \converter lyx        lyx14x     "python -tt $$s/lyx2lyx/lyx2lyx -t 245 $$i > $$o"      ""
839 \converter lyx        lyx15x     "python -tt $$s/lyx2lyx/lyx2lyx -t 276 $$i > $$o"      ""
840 \converter lyx        lyx16x     "python -tt $$s/lyx2lyx/lyx2lyx -t 345 $$i > $$o"      ""
841 \converter lyx        clyx       "python -tt $$s/lyx2lyx/lyx2lyx -c big5 -t 245 $$i > $$o"      ""
842 \converter lyx        jlyx       "python -tt $$s/lyx2lyx/lyx2lyx -c euc_jp -t 245 $$i > $$o"    ""
843 \converter lyx        klyx       "python -tt $$s/lyx2lyx/lyx2lyx -c euc_kr -t 245 $$i > $$o"    ""
844 \converter clyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c big5 $$i > $$o"     ""
845 \converter jlyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c euc_jp $$i > $$o"   ""
846 \converter klyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c euc_kr $$i > $$o"   ""
847 ''')
848
849
850 def checkDocBook():
851     ''' Check docbook '''
852     path, DOCBOOK = checkProg('SGML-tools 2.x (DocBook), db2x scripts or xsltproc', ['sgmltools', 'db2dvi', 'xsltproc'],
853         rc_entry = [
854             r'''\converter docbook    dvi        "sgmltools -b dvi $$i" ""
855 \converter docbook    html       "sgmltools -b html $$i"        ""''',
856             r'''\converter docbook    dvi        "db2dvi $$i"   ""
857 \converter docbook    html       "db2html $$i"  ""''',
858             r'''\converter docbook    dvi        ""     ""
859 \converter docbook    html       "" ""''',
860             r'''\converter docbook    dvi        ""     ""
861 \converter docbook    html       ""     ""'''])
862     #
863     if DOCBOOK != '':
864         return ('yes', 'true', '\\def\\hasdocbook{yes}')
865     else:
866         return ('no', 'false', '')
867
868
869 def checkOtherEntries():
870     ''' entries other than Format and Converter '''
871     checkProg('ChkTeX', ['chktex -n1 -n3 -n6 -n9 -n22 -n25 -n30 -n38'],
872         rc_entry = [ r'\chktex_command "%%"' ])
873     checkProgAlternatives('BibTeX or alternative programs', ['bibtex', 'bibtex8', 'biber'],
874         rc_entry = [ r'\bibtex_command "%%"' ],
875         alt_rc_entry = [ r'\bibtex_alternatives "%%"' ])
876     checkProg('a specific Japanese BibTeX variant', ['pbibtex', 'jbibtex', 'bibtex'],
877         rc_entry = [ r'\jbibtex_command "%%"' ])
878     checkProgAlternatives('available index processors', ['texindy', 'makeindex -c -q'],
879         rc_entry = [ r'\index_command "%%"' ],
880         alt_rc_entry = [ r'\index_alternatives "%%"' ])
881     checkProg('an index processor appropriate to Japanese', ['mendex -c -q', 'jmakeindex -c -q', 'makeindex -c -q'],
882         rc_entry = [ r'\jindex_command "%%"' ])
883     path, splitindex = checkProg('the splitindex processor', ['splitindex.pl', 'splitindex'],
884         rc_entry = [ r'\splitindex_command "%%"' ])
885     if splitindex == '':
886         checkProg('the splitindex processor (java version)', ['splitindex.class'],
887             rc_entry = [ r'\splitindex_command "java splitindex"' ])
888     checkProg('a nomenclature processor', ['makeindex'],
889         rc_entry = [ r'\nomencl_command "makeindex -s nomencl.ist"' ])
890     ## FIXME: OCTAVE is not used anywhere
891     # path, OCTAVE = checkProg('Octave', ['octave'])
892     ## FIXME: MAPLE is not used anywhere
893     # path, MAPLE = checkProg('Maple', ['maple'])
894     checkProg('a spool command', ['lp', 'lpr'],
895         rc_entry = [
896             r'''\print_spool_printerprefix "-d "
897 \print_spool_command "lp"''',
898             r'''\print_spool_printerprefix "-P",
899 \print_spool_command "lpr"''',
900             ''])
901     # Add the rest of the entries (no checkProg is required)
902     addToRC(r'''\copier    fig        "python -tt $$s/scripts/fig_copy.py $$i $$o"
903 \copier    pstex      "python -tt $$s/scripts/tex_copy.py $$i $$o $$l"
904 \copier    pdftex     "python -tt $$s/scripts/tex_copy.py $$i $$o $$l"
905 \copier    program    "python -tt $$s/scripts/ext_copy.py $$i $$o"
906 ''')
907
908
909 def processLayoutFile(file, bool_docbook):
910     ''' process layout file and get a line of result
911         
912         Declare lines look like this: (article.layout, scrbook.layout, svjog.layout)
913         
914         \DeclareLaTeXClass{article}
915         \DeclareLaTeXClass[scrbook]{book (koma-script)}
916         \DeclareLaTeXClass[svjour,svjog.clo]{article (Springer - svjour/jog)}
917
918         we expect output:
919         
920         "article" "article" "article" "false" "article.cls"
921         "scrbook" "scrbook" "book (koma-script)" "false" "scrbook.cls"
922         "svjog" "svjour" "article (Springer - svjour/jog)" "false" "svjour.cls,svjog.clo"
923     '''
924     def checkForClassExtension(x):
925         '''if the extension for a latex class is not
926            provided, add .cls to the classname'''
927         if not '.' in x:
928             return x.strip() + '.cls'
929         else:
930             return x.strip()
931     classname = file.split(os.sep)[-1].split('.')[0]
932     # return ('LaTeX', '[a,b]', 'a', ',b,c', 'article') for \DeclareLaTeXClass[a,b,c]{article}
933     p = re.compile(r'\Declare(LaTeX|DocBook)Class\s*(\[([^,]*)(,.*)*\])*\s*{(.*)}')
934     for line in open(file).readlines():
935         res = p.search(line)
936         if res != None:
937             (classtype, optAll, opt, opt1, desc) = res.groups()
938             avai = {'LaTeX':'false', 'DocBook':bool_docbook}[classtype]
939             if opt == None:
940                 opt = classname
941                 prereq_latex = checkForClassExtension(classname)
942             else:
943                 prereq_list = optAll[1:-1].split(',')
944                 prereq_list = map(checkForClassExtension, prereq_list)
945                 prereq_latex = ','.join(prereq_list)
946             prereq_docbook = {'true':'', 'false':'docbook'}[bool_docbook]
947             prereq = {'LaTeX':prereq_latex, 'DocBook':prereq_docbook}[classtype]
948             return '"%s" "%s" "%s" "%s" "%s"\n' % (classname, opt, desc, avai, prereq)
949     logger.warning("Layout file " + file + " has no \DeclareXXClass line. ")
950     return ""
951
952
953 def checkLatexConfig(check_config, bool_docbook):
954     ''' Explore the LaTeX configuration 
955         Return None (will be passed to sys.exit()) for success.
956     '''
957     msg = 'checking LaTeX configuration... '
958     # if --without-latex-config is forced, or if there is no previous 
959     # version of textclass.lst, re-generate a default file.
960     if not os.path.isfile('textclass.lst') or not check_config:
961         # remove the files only if we want to regenerate
962         removeFiles(['textclass.lst', 'packages.lst'])
963         #
964         # Then, generate a default textclass.lst. In case configure.py
965         # fails, we still have something to start lyx.
966         logger.info(msg + ' default values')
967         logger.info('+checking list of textclasses... ')
968         tx = open('textclass.lst', 'w')
969         tx.write('''
970 # This file declares layouts and their associated definition files
971 # (include dir. relative to the place where this file is).
972 # It contains only default values, since chkconfig.ltx could not be run
973 # for some reason. Run ./configure.py if you need to update it after a
974 # configuration change.
975 ''')
976         # build the list of available layout files and convert it to commands
977         # for chkconfig.ltx
978         foundClasses = []
979         for file in glob.glob( os.path.join('layouts', '*.layout') ) + \
980             glob.glob( os.path.join(srcdir, 'layouts', '*.layout' ) ) :
981             # valid file?
982             if not os.path.isfile(file): 
983                 continue
984             # get stuff between /xxxx.layout .
985             classname = file.split(os.sep)[-1].split('.')[0]
986             #  tr ' -' '__'`
987             cleanclass = classname.replace(' ', '_')
988             cleanclass = cleanclass.replace('-', '_')
989             # make sure the same class is not considered twice
990             if foundClasses.count(cleanclass) == 0: # not found before
991                 foundClasses.append(cleanclass)
992                 retval = processLayoutFile(file, bool_docbook)
993                 if retval != "":
994                     tx.write(retval)
995         tx.close()
996         logger.info('\tdone')
997     if not check_config:
998         return None
999     # the following will generate textclass.lst.tmp, and packages.lst.tmp
1000     logger.info(msg + '\tauto')
1001     removeFiles(['wrap_chkconfig.ltx', 'chkconfig.vars', \
1002         'chkconfig.classes', 'chklayouts.tex'])
1003     rmcopy = False
1004     if not os.path.isfile( 'chkconfig.ltx' ):
1005         shutil.copyfile( os.path.join(srcdir, 'chkconfig.ltx'), 'chkconfig.ltx' )
1006         rmcopy = True
1007     writeToFile('wrap_chkconfig.ltx', '%s\n\\input{chkconfig.ltx}\n' % docbook_cmd)
1008     # Construct the list of classes to test for.
1009     # build the list of available layout files and convert it to commands
1010     # for chkconfig.ltx
1011     declare = re.compile(r'\Declare(LaTeX|DocBook)Class\s*(\[([^,]*)(,.*)*\])*\s*{(.*)}')
1012     empty = re.compile(r'^\s*$')
1013     testclasses = list()
1014     for file in glob.glob( os.path.join('layouts', '*.layout') ) + \
1015         glob.glob( os.path.join(srcdir, 'layouts', '*.layout' ) ) :
1016         if not os.path.isfile(file):
1017             continue
1018         classname = file.split(os.sep)[-1].split('.')[0]
1019         for line in open(file).readlines():
1020             if not empty.match(line) and line[0] != '#':
1021                 logger.error("Failed to find \Declare line for layout file `" + file + "'")
1022                 sys.exit(3)
1023             if declare.search(line) == None:
1024                 continue
1025             testclasses.append("\\TestDocClass{%s}{%s}" % (classname, line[1:].strip()))
1026             break
1027     testclasses.sort()
1028     cl = open('chklayouts.tex', 'w')
1029     for line in testclasses:
1030         cl.write(line + '\n')
1031     cl.close()
1032     #
1033     # we have chklayouts.tex, then process it
1034     fout = os.popen(LATEX + ' wrap_chkconfig.ltx')
1035     while True:
1036         line = fout.readline()
1037         if not line:
1038             break;
1039         if re.match('^\+', line):
1040             logger.info(line.strip())
1041     # if the command succeeds, None will be returned
1042     ret = fout.close()
1043     #
1044     # currently, values in chhkconfig are only used to set
1045     # \font_encoding
1046     values = {}
1047     for line in open('chkconfig.vars').readlines():
1048         key, val = re.sub('-', '_', line).split('=')
1049         val = val.strip()
1050         values[key] = val.strip("'")
1051     # chk_fontenc may not exist 
1052     try:
1053         addToRC(r'\font_encoding "%s"' % values["chk_fontenc"])
1054     except:
1055         pass
1056     if rmcopy:   # remove the copied file
1057         removeFiles( [ 'chkconfig.ltx' ] )
1058     # if configure successed, move textclass.lst.tmp to textclass.lst
1059     # and packages.lst.tmp to packages.lst
1060     if os.path.isfile('textclass.lst.tmp') and len(open('textclass.lst.tmp').read()) > 0 \
1061         and os.path.isfile('packages.lst.tmp') and len(open('packages.lst.tmp').read()) > 0:
1062         shutil.move('textclass.lst.tmp', 'textclass.lst')
1063         shutil.move('packages.lst.tmp', 'packages.lst')
1064     return ret
1065
1066
1067 def checkModulesConfig():
1068   removeFiles(['lyxmodules.lst', 'chkmodules.tex'])
1069
1070   logger.info('+checking list of modules... ')
1071   tx = open('lyxmodules.lst', 'w')
1072   tx.write('''## This file declares modules and their associated definition files.
1073 ## It has been automatically generated by configure
1074 ## Use "Options/Reconfigure" if you need to update it after a
1075 ## configuration change. 
1076 ## "ModuleName" "filename" "Description" "Packages" "Requires" "Excludes" "Category"
1077 ''')
1078   # build the list of available modules
1079   foundClasses = []
1080   for file in glob.glob( os.path.join('layouts', '*.module') ) + \
1081       glob.glob( os.path.join(srcdir, 'layouts', '*.module' ) ) :
1082       # valid file?
1083       logger.info(file)
1084       if not os.path.isfile(file): 
1085           continue
1086       retval = processModuleFile(file, bool_docbook)
1087       if retval != "":
1088           tx.write(retval)
1089   tx.close()
1090   logger.info('\tdone')
1091
1092
1093 def processModuleFile(file, bool_docbook):
1094     ''' process module file and get a line of result
1095
1096         The top of a module file should look like this:
1097           #\DeclareLyXModule[LaTeX Packages]{ModuleName}
1098           #DescriptionBegin
1099           #...body of description...
1100           #DescriptionEnd
1101           #Requires: [list of required modules]
1102           #Excludes: [list of excluded modules]
1103           #Category: [category name]
1104         The last three lines are optional (though do give a category).
1105         We expect output:
1106           "ModuleName" "filename" "Description" "Packages" "Requires" "Excludes" "Category"
1107     '''
1108     remods = re.compile(r'\DeclareLyXModule\s*(?:\[([^]]*?)\])?{(.*)}')
1109     rereqs = re.compile(r'#+\s*Requires: (.*)')
1110     reexcs = re.compile(r'#+\s*Excludes: (.*)')
1111     recaty = re.compile(r'#+\s*Category: (.*)')
1112     redbeg = re.compile(r'#+\s*DescriptionBegin\s*$')
1113     redend = re.compile(r'#+\s*DescriptionEnd\s*$')
1114
1115     modname = desc = pkgs = req = excl = catgy = ""
1116     readingDescription = False
1117     descLines = []
1118     filename = file.split(os.sep)[-1]
1119     filename = filename[:-7]
1120
1121     for line in open(file).readlines():
1122       if readingDescription:
1123         res = redend.search(line)
1124         if res != None:
1125           readingDescription = False
1126           desc = " ".join(descLines)
1127           # Escape quotes.
1128           desc = desc.replace('"', '\\"')
1129           continue
1130         descLines.append(line[1:].strip())
1131         continue
1132       res = redbeg.search(line)
1133       if res != None:
1134         readingDescription = True
1135         continue
1136       res = remods.search(line)
1137       if res != None:
1138           (pkgs, modname) = res.groups()
1139           if pkgs == None:
1140             pkgs = ""
1141           else:
1142             tmp = [s.strip() for s in pkgs.split(",")]
1143             pkgs = ",".join(tmp)
1144           continue
1145       res = rereqs.search(line)
1146       if res != None:
1147         req = res.group(1)
1148         tmp = [s.strip() for s in req.split("|")]
1149         req = "|".join(tmp)
1150         continue
1151       res = reexcs.search(line)
1152       if res != None:
1153         excl = res.group(1)
1154         tmp = [s.strip() for s in excl.split("|")]
1155         excl = "|".join(tmp)
1156         continue
1157       res = recaty.search(line)
1158       if res != None:
1159         catgy = res.group(1)
1160         continue
1161
1162     if modname == "":
1163       logger.warning("Module file without \DeclareLyXModule line. ")
1164       return ""
1165
1166     if pkgs != "":
1167         # this module has some latex dependencies:
1168         # append the dependencies to chkmodules.tex,
1169         # which is \input'ed by chkconfig.ltx
1170         testpackages = list()
1171         for pkg in pkgs.split(","):
1172             if "->" in pkg:
1173                 # this is a converter dependency: skip
1174                 continue
1175             if pkg.endswith(".sty"):
1176                 pkg = pkg[:-4]
1177             testpackages.append("\\TestPackage{%s}" % (pkg,))
1178         cm = open('chkmodules.tex', 'a')
1179         for line in testpackages:
1180             cm.write(line + '\n')
1181         cm.close()
1182
1183     return '"%s" "%s" "%s" "%s" "%s" "%s" "%s"\n' % (modname, filename, desc, pkgs, req, excl, catgy)
1184     
1185
1186
1187 def checkTeXAllowSpaces():
1188     ''' Let's check whether spaces are allowed in TeX file names '''
1189     tex_allows_spaces = 'false'
1190     if lyx_check_config:
1191         msg = "Checking whether TeX allows spaces in file names... "
1192         writeToFile('a b.tex', r'\message{working^^J}' )
1193         if LATEX != '':
1194             if os.name == 'nt':
1195                 latex_out = cmdOutput(LATEX + r""" "\nonstopmode\input{\"a b\"}" """)
1196             else:
1197                 latex_out = cmdOutput(LATEX + r""" '\nonstopmode\input{"a b"}' """)
1198         else:
1199             latex_out = ''
1200         if 'working' in latex_out:
1201             logger.info(msg + 'yes')
1202             tex_allows_spaces = 'true'
1203         else:
1204             logger.info(msg + 'no')
1205             tex_allows_spaces = 'false'
1206         addToRC(r'\tex_allows_spaces ' + tex_allows_spaces)
1207         removeFiles( [ 'a b.tex', 'a b.log', 'texput.log' ])
1208
1209
1210 def removeTempFiles():
1211     # Final clean-up
1212     if not lyx_keep_temps:
1213         removeFiles(['chkconfig.vars',  \
1214             'wrap_chkconfig.ltx', 'wrap_chkconfig.log', \
1215             'chklayouts.tex', 'chkmodules.tex', 'missfont.log', 
1216             'chklatex.ltx', 'chklatex.log'])
1217
1218
1219 if __name__ == '__main__':
1220     lyx_check_config = True
1221     outfile = 'lyxrc.defaults'
1222     rc_entries = ''
1223     lyx_keep_temps = False
1224     version_suffix = ''
1225     ## Parse the command line
1226     for op in sys.argv[1:]:   # default shell/for list is $*, the options
1227         if op in [ '-help', '--help', '-h' ]:
1228             print '''Usage: configure [options]
1229 Options:
1230     --help                   show this help lines
1231     --keep-temps             keep temporary files (for debug. purposes)
1232     --without-latex-config   do not run LaTeX to determine configuration
1233     --with-version-suffix=suffix suffix of binary installed files
1234 '''
1235             sys.exit(0)
1236         elif op == '--without-latex-config':
1237             lyx_check_config = False
1238         elif op == '--keep-temps':
1239             lyx_keep_temps = True
1240         elif op[0:22] == '--with-version-suffix=':  # never mind if op is not long enough
1241             version_suffix = op[22:]
1242         else:
1243             print "Unknown option", op
1244             sys.exit(1)
1245     #
1246     # check if we run from the right directory
1247     srcdir = os.path.dirname(sys.argv[0])
1248     if srcdir == '':
1249         srcdir = '.'
1250     if not os.path.isfile( os.path.join(srcdir, 'chkconfig.ltx') ):
1251         logger.error("configure: error: cannot find chkconfig.ltx script")
1252         sys.exit(1)
1253     setEnviron()
1254     createDirectories()
1255     windows_style_tex_paths = checkTeXPaths()
1256     dtl_tools = checkDTLtools()
1257     ## Write the first part of outfile
1258     writeToFile(outfile, '''# This file has been automatically generated by LyX' lib/configure.py
1259 # script. It contains default settings that have been determined by
1260 # examining your system. PLEASE DO NOT MODIFY ANYTHING HERE! If you
1261 # want to customize LyX, use LyX' Preferences dialog or modify directly 
1262 # the "preferences" file instead. Any setting in that file will
1263 # override the values given here.
1264 ''')
1265     # check latex
1266     LATEX = checkLatex(dtl_tools)
1267     checkFormatEntries(dtl_tools)
1268     checkConverterEntries()
1269     (chk_docbook, bool_docbook, docbook_cmd) = checkDocBook()
1270     checkTeXAllowSpaces()
1271     if windows_style_tex_paths != '':
1272         addToRC(r'\tex_expects_windows_paths %s' % windows_style_tex_paths)
1273     checkOtherEntries()
1274     checkModulesConfig()
1275     # --without-latex-config can disable lyx_check_config
1276     ret = checkLatexConfig(lyx_check_config and LATEX != '', bool_docbook)
1277     removeTempFiles()
1278     # The return error code can be 256. Because most systems expect an error code
1279     # in the range 0-127, 256 can be interpretted as 'success'. Because we expect
1280     # a None for success, 'ret is not None' is used to exit.
1281     sys.exit(ret is not None)