]> git.lyx.org Git - lyx.git/blob - lib/configure.py
Fix encoding issues with configuration under Python 3.
[lyx.git] / lib / configure.py
1 #! /usr/bin/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 from __future__ import print_function
12 import glob, logging, os, re, shutil, subprocess, sys, stat
13
14 if sys.version_info[0] < 3:
15     import codecs
16     open = codecs.open
17
18
19 # set up logging
20 logging.basicConfig(level = logging.DEBUG,
21     format = '%(levelname)s: %(message)s', # ignore application name
22     filename = 'configure.log',
23     filemode = 'w')
24 #
25 # Add a handler to log to console
26 console = logging.StreamHandler()
27 console.setLevel(logging.INFO) # the console only print out general information
28 formatter = logging.Formatter('%(message)s') # only print out the message itself
29 console.setFormatter(formatter)
30 logger = logging.getLogger('LyX')
31 logger.addHandler(console)
32
33 def quoteIfSpace(name):
34     " utility function: quote name if it contains spaces "
35     if ' ' in name:
36         return '"' + name + '"'
37     else:
38         return name
39
40 def writeToFile(filename, lines, append = False):
41     " utility function: write or append lines to filename "
42     if append:
43         file = open(filename, 'a')
44     else:
45         file = open(filename, 'w')
46     file.write(lines)
47     file.close()
48
49
50 def addToRC(lines):
51     ''' utility function: shortcut for appending lines to outfile
52         add newline at the end of lines.
53     '''
54     if lines.strip():
55         writeToFile(outfile, lines + '\n', append = True)
56         logger.debug('Add to RC:\n' + lines + '\n\n')
57
58
59 def removeFiles(filenames):
60     '''utility function: 'rm -f'
61         ignore errors when file does not exist, or is a directory.
62     '''
63     for file in filenames:
64         try:
65             os.remove(file)
66             logger.debug('Removing file %s' % file)
67         except:
68             logger.debug('Failed to remove file %s' % file)
69             pass
70
71
72 def cmdOutput(cmd, asynchronous = False):
73     '''utility function: run a command and get its output as a string
74         cmd: command to run
75         asynchronous: if False, return whole output as a string, otherwise
76                return the stdout handle from which the output can be
77                read (the caller is then responsible for closing it)
78     '''
79     if os.name == 'nt':
80         b = False
81         if sys.version_info[0] < 3:
82             cmd = 'cmd /d /c pushd ' + shortPath(os.getcwdu()) + '&' + cmd
83         else:
84             cmd = 'cmd /d /c pushd ' + shortPath(os.getcwd()) + '&' + cmd
85     else:
86         b = True
87     pipe = subprocess.Popen(cmd, shell=b, close_fds=b, stdin=subprocess.PIPE,
88                             stdout=subprocess.PIPE, universal_newlines=True)
89     pipe.stdin.close()
90     if asynchronous:
91         return pipe.stdout
92     output = pipe.stdout.read()
93     pipe.stdout.close()
94     return output.strip()
95
96
97 def shortPath(path):
98     ''' On Windows, return the short version of "path" if possible '''
99     if os.name == 'nt':
100         from ctypes import windll, create_unicode_buffer
101         GetShortPathName = windll.kernel32.GetShortPathNameW
102         shortlen = GetShortPathName(path, 0, 0)
103         shortpath = create_unicode_buffer(shortlen)
104         if GetShortPathName(path, shortpath, shortlen):
105             return shortpath.value
106     return path
107
108
109 def setEnviron():
110     ''' I do not really know why this is useful, but we might as well keep it.
111         NLS nuisances.
112         Only set these to C if already set.  These must not be set unconditionally
113         because not all systems understand e.g. LANG=C (notably SCO).
114         Fixing LC_MESSAGES prevents Solaris sh from translating var values in set!
115         Non-C LC_CTYPE values break the ctype check.
116     '''
117     os.environ['LANG'] = os.getenv('LANG', 'C')
118     os.environ['LC'] = os.getenv('LC_ALL', 'C')
119     os.environ['LC_MESSAGE'] = os.getenv('LC_MESSAGE', 'C')
120     os.environ['LC_CTYPE'] = os.getenv('LC_CTYPE', 'C')
121
122
123 def copy_tree(src, dst, preserve_symlinks=False, level=0):
124     ''' Copy an entire directory tree 'src' to a new location 'dst'.
125
126     Code inspired from distutils.copy_tree.
127          Copying ignores non-regular files and the cache directory.
128     Pipes may be present as leftovers from LyX for lyx-server.
129
130     If 'preserve_symlinks' is true, symlinks will be
131     copied as symlinks (on platforms that support them!); otherwise
132     (the default), the destination of the symlink will be copied.
133     '''
134
135     if not os.path.isdir(src):
136         raise FileError("cannot copy tree '%s': not a directory" % src)
137     try:
138         names = os.listdir(src)
139     except os.error as oserror:
140         (errno, errstr) = oserror.args
141         raise FileError("error listing files in '%s': %s" % (src, errstr))
142
143     if not os.path.isdir(dst):
144         os.makedirs(dst)
145
146     outputs = []
147
148     for name in names:
149         src_name = os.path.join(src, name)
150         dst_name = os.path.join(dst, name)
151         if preserve_symlinks and os.path.islink(src_name):
152             link_dest = os.readlink(src_name)
153             os.symlink(link_dest, dst_name)
154             outputs.append(dst_name)
155         elif level == 0 and name == 'cache':
156             logger.info("Skip cache %s", src_name)
157         elif os.path.isdir(src_name):
158             outputs.extend(
159                 copy_tree(src_name, dst_name, preserve_symlinks, level=(level + 1)))
160         elif stat.S_ISREG(os.stat(src_name).st_mode) or os.path.islink(src_name):
161             shutil.copy2(src_name, dst_name)
162             outputs.append(dst_name)
163         else:
164             logger.info("Ignore non-regular file %s", src_name)
165
166     return outputs
167
168
169 def checkUpgrade():
170     ''' Check for upgrade from previous version '''
171     cwd = os.getcwd()
172     basename = os.path.basename( cwd )
173     lyxrc = os.path.join(cwd, outfile)
174     if not os.path.isfile( lyxrc ) and basename.endswith( version_suffix ) :
175         logger.info('Checking for upgrade from previous version.')
176         parent = os.path.dirname(cwd)
177         appname = basename[:(-len(version_suffix))]
178         for version in ['-2.2', '-2.1', '-2.0', '-1.6' ]:
179             logger.debug('Checking for upgrade from previous version ' + version)
180             previous = os.path.join(parent, appname + version)
181             logger.debug('previous = ' + previous)
182             if os.path.isdir( previous ):
183                 logger.info('Found directory "%s".', previous)
184                 copy_tree( previous, cwd, True )
185                 logger.info('Content copied from directory "%s".', previous)
186                 return
187
188
189 def checkUpgradeWin():
190     ''' Check for upgrade from previous version '''
191     cwd = os.getcwd()
192     basename = os.path.basename(cwd)
193     if basename != "LyX":
194         return
195     lyxrc = os.path.join(cwd, outfile)
196     if os.path.isfile(lyxrc):
197         return
198     olddir = os.path.join(os.path.dirname(cwd), "LyX2.3")
199     if not os.path.isdir(oldir):
200         return
201     logger.info('Copying ' + olddir + ' into ' + cwd)
202     copy_tree(olddir, cwd, True)
203
204
205 def createDirectories():
206     ''' Create the build directories if necessary '''
207     for dir in ['bind', 'clipart', 'doc', 'examples', 'images', 'kbd',
208         'layouts', 'scripts', 'templates', 'ui' ]:
209         if not os.path.isdir( dir ):
210             try:
211                 os.mkdir( dir)
212                 logger.debug('Create directory %s.' % dir)
213             except:
214                 logger.error('Failed to create directory %s.' % dir)
215                 sys.exit(1)
216
217
218 def checkTeXPaths():
219     ''' Determine the path-style needed by the TeX engine on Win32 (Cygwin) '''
220     windows_style_tex_paths = ''
221     if LATEX == '':
222         return windows_style_tex_paths
223     if os.name == 'nt' or sys.platform == 'cygwin':
224         from tempfile import mkstemp
225         fd, tmpfname = mkstemp(suffix='.ltx')
226         if os.name == 'nt':
227             encoding = sys.getfilesystemencoding()
228             if sys.version_info[0] < 3:
229                 inpname = shortPath(unicode(tmpfname, encoding)).replace('\\', '/')
230             else:
231                 inpname = shortPath(tmpfname).replace('\\', '/') 
232         else:
233             inpname = cmdOutput('cygpath -m ' + tmpfname)
234         logname = os.path.basename(re.sub("(?i).ltx", ".log", inpname))
235         inpname = inpname.replace('~', '\\string~')
236         os.write(fd, b'\\relax')
237         os.close(fd)
238         latex_out = cmdOutput(r'latex "\nonstopmode\input{%s}\makeatletter\@@end"'
239                               % inpname)
240         if 'Error' in latex_out:
241             latex_out = cmdOutput(r'latex "\nonstopmode\input{\"%s\"}\makeatletter\@@end"'
242                                   % inpname)
243         if 'Error' in latex_out:
244             logger.warning("configure: TeX engine needs posix-style paths in latex files")
245             windows_style_tex_paths = 'false'
246         else:
247             logger.info("configure: TeX engine needs windows-style paths in latex files")
248             windows_style_tex_paths = 'true'
249         removeFiles([tmpfname, logname, 'texput.log'])
250     return windows_style_tex_paths
251
252
253 ## Searching some useful programs
254 def checkProg(description, progs, rc_entry = [], path = [], not_found = ''):
255     '''
256         This function will search a program in $PATH plus given path
257         If found, return directory and program name (not the options).
258
259         description: description of the program
260
261         progs: check programs, for each prog, the first word is used
262             for searching but the whole string is used to replace
263             %% for a rc_entry. So, feel free to add '$$i' etc for programs.
264
265         path: additional paths (will be prepended to the program name)
266
267         rc_entry: entry to outfile, can be
268             1. emtpy: no rc entry will be added
269             2. one pattern: %% will be replaced by the first found program,
270                 or '' if no program is found.
271             3. several patterns for each prog and not_found. This is used
272                 when different programs have different usages. If you do not
273                 want not_found entry to be added to the RC file, you can specify
274                 an entry for each prog and use '' for the not_found entry.
275
276         not_found: the value that should be used instead of '' if no program
277             was found
278
279     '''
280     # one rc entry for each progs plus not_found entry
281     if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1:
282         logger.error("rc entry should have one item or item "
283                      "for each prog and not_found.")
284         sys.exit(2)
285     logger.info('checking for ' + description + '...')
286     ## print '(' + ','.join(progs) + ')',
287     additional_path = path
288     path = os.environ["PATH"].split(os.pathsep) + additional_path
289     extlist = ['']
290     if "PATHEXT" in os.environ:
291         extlist = extlist + os.environ["PATHEXT"].split(os.pathsep)
292     global java, perl
293     unquoted_space = re.compile(r'''((?:[^ "']|"[^"]*"|'[^']*')+)''')
294     for idx in range(len(progs)):
295         # ac_prog may have options, ac_word is the command name
296         ac_prog = progs[idx].replace('"', '\\"')
297         ac_word = unquoted_space.split(progs[idx])[1::2][0].strip('"')
298         if (ac_word.endswith('.class') or ac_word.endswith('.jar')) and java == '':
299             continue
300         if ac_word.endswith('.pl') and perl == '':
301             continue
302         msg = '+checking for "' + ac_word + '"... '
303         for ac_dir in path:
304             if hasattr(os, "access") and not os.access(ac_dir, os.F_OK):
305                 continue
306             for ext in extlist:
307                 if os.path.isfile( os.path.join(ac_dir, ac_word + ext) ):
308                     logger.info(msg + ' yes')
309                     # deal with java and perl
310                     if ac_word.endswith('.class'):
311                         ac_prog = ac_prog.replace(ac_word, r'%s \"%s\"'
312                                     % (java, os.path.join(ac_dir, ac_word[:-6])))
313                     elif ac_word.endswith('.jar'):
314                         ac_prog = ac_prog.replace(ac_word, r'%s -jar \"%s\"'
315                                     % (java, os.path.join(ac_dir, ac_word)))
316                     elif ac_word.endswith('.pl'):
317                         ac_prog = ac_prog.replace(ac_word, r'%s -w \"%s\"'
318                                     % (perl, os.path.join(ac_dir, ac_word)))
319                     elif ac_dir in additional_path:
320                         ac_prog = ac_prog.replace(ac_word, r'\"%s\"'
321                                     % (os.path.join(ac_dir, ac_word)))
322                     # write rc entries for this command
323                     if len(rc_entry) == 1:
324                         addToRC(rc_entry[0].replace('%%', ac_prog))
325                     elif len(rc_entry) > 1:
326                         addToRC(rc_entry[idx].replace('%%', ac_prog))
327                     return [ac_dir, ac_word]
328         # if not successful
329         logger.info(msg + ' no')
330     # write rc entries for 'not found'
331     if len(rc_entry) > 0:  # the last one.
332         addToRC(rc_entry[-1].replace('%%', not_found))
333     return ['', not_found]
334
335
336 def checkProgAlternatives(description, progs, rc_entry = [],
337                           alt_rc_entry = [], path = [], not_found = ''):
338     '''
339         The same as checkProg, but additionally, all found programs will be added
340         as alt_rc_entries
341     '''
342     # one rc entry for each progs plus not_found entry
343     if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1:
344         logger.error("rc entry should have one item or item for each prog and not_found.")
345         sys.exit(2)
346     logger.info('checking for ' + description + '...')
347     ## print '(' + ','.join(progs) + ')',
348     additional_path = path
349     path = os.environ["PATH"].split(os.pathsep) + additional_path
350     extlist = ['']
351     if "PATHEXT" in os.environ:
352         extlist = extlist + os.environ["PATHEXT"].split(os.pathsep)
353     found_prime = False
354     real_ac_dir = ''
355     real_ac_word = not_found
356     global java, perl
357     for idx in range(len(progs)):
358         # ac_prog may have options, ac_word is the command name
359         ac_prog = progs[idx]
360         ac_word = ac_prog.split(' ')[0]
361         if (ac_word.endswith('.class') or ac_word.endswith('.jar')) and java == '':
362             continue
363         if ac_word.endswith('.pl') and perl == '':
364             continue
365         msg = '+checking for "' + ac_word + '"... '
366         found_alt = False
367         for ac_dir in path:
368             if hasattr(os, "access") and not os.access(ac_dir, os.F_OK):
369                 continue
370             for ext in extlist:
371                 if os.path.isfile( os.path.join(ac_dir, ac_word + ext) ):
372                     logger.info(msg + ' yes')
373                     pr = re.compile(r'(\\\S+)(.*)$')
374                     m = None
375                     # deal with java and perl
376                     if ac_word.endswith('.class'):
377                         ac_prog = ac_prog.replace(ac_word, r'%s \"%s\"'
378                                     % (java, os.path.join(ac_dir, ac_word[:-6])))
379                     elif ac_word.endswith('.jar'):
380                         ac_prog = ac_prog.replace(ac_word, r'%s -jar \"%s\"'
381                                     % (java, os.path.join(ac_dir, ac_word)))
382                     elif ac_word.endswith('.pl'):
383                         ac_prog = ac_prog.replace(ac_word, r'%s -w \"%s\"'
384                                     % (perl, os.path.join(ac_dir, ac_word)))
385                     elif ac_dir in additional_path:
386                         ac_prog = ac_prog.replace(ac_word, r'\"%s\"'
387                                     % (os.path.join(ac_dir, ac_word)))
388                     # write rc entries for this command
389                     if found_prime == False:
390                         if len(rc_entry) == 1:
391                             addToRC(rc_entry[0].replace('%%', ac_prog))
392                         elif len(rc_entry) > 1:
393                             addToRC(rc_entry[idx].replace('%%', ac_prog))
394                         real_ac_dir = ac_dir
395                         real_ac_word = ac_word
396                         found_prime = True
397                     if len(alt_rc_entry) == 1:
398                         alt_rc = alt_rc_entry[0]
399                         if alt_rc == "":
400                             # if no explicit alt_rc is given, construct one
401                             m = pr.match(rc_entry[0])
402                             if m:
403                                 alt_rc = m.group(1) + "_alternatives" + m.group(2)
404                         addToRC(alt_rc.replace('%%', ac_prog))
405                     elif len(alt_rc_entry) > 1:
406                         alt_rc = alt_rc_entry[idx]
407                         if alt_rc == "":
408                             # if no explicit alt_rc is given, construct one
409                             m = pr.match(rc_entry[idx])
410                             if m:
411                                 alt_rc = m.group(1) + "_alternatives" + m.group(2)
412                         addToRC(alt_rc.replace('%%', ac_prog))
413                     found_alt = True
414                     break
415             if found_alt:
416                 break
417         if found_alt == False:
418             # if not successful
419             logger.info(msg + ' no')
420     if found_prime:
421         return [real_ac_dir, real_ac_word]
422     # write rc entries for 'not found'
423     if len(rc_entry) > 0:  # the last one.
424         addToRC(rc_entry[-1].replace('%%', not_found))
425     return ['', not_found]
426
427
428 def addAlternatives(rcs, alt_type):
429     '''
430         Returns a \\prog_alternatives string to be used as an alternative
431         rc entry.  alt_type can be a string or a list of strings.
432     '''
433     r = re.compile(r'\\Format (\S+).*$')
434     m = None
435     alt = ''
436     alt_token = '\\%s_alternatives '
437     if isinstance(alt_type, str):
438         alt_tokens = [alt_token % alt_type]
439     else:
440         alt_tokens = [alt_token % s for s in alt_type]
441     for idxx in range(len(rcs)):
442         if len(rcs) == 1:
443             m = r.match(rcs[0])
444             if m:
445                 alt = '\n'.join([s + m.group(1) + ' "%%"' for s in alt_tokens])
446         elif len(rcs) > 1:
447             m = r.match(rcs[idxx])
448             if m:
449                 if idxx > 0:
450                     alt += '\n'
451                 alt += '\n'.join([s + m.group(1) + ' "%%"' for s in alt_tokens])
452     return alt
453
454
455 def listAlternatives(progs, alt_type, rc_entry = []):
456     '''
457         Returns a list of \\prog_alternatives strings to be used as alternative
458         rc entries.  alt_type can be a string or a list of strings.
459     '''
460     if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1:
461         logger.error("rc entry should have one item or item for each prog and not_found.")
462         sys.exit(2)
463     alt_rc_entry = []
464     for idx in range(len(progs)):
465         if len(rc_entry) == 1:
466             rcs = rc_entry[0].split('\n')
467             alt = addAlternatives(rcs, alt_type)
468             alt_rc_entry.insert(0, alt)
469         elif len(rc_entry) > 1:
470             rcs = rc_entry[idx].split('\n')
471             alt = addAlternatives(rcs, alt_type)
472             alt_rc_entry.insert(idx, alt)
473     return alt_rc_entry
474
475
476 def checkViewer(description, progs, rc_entry = [], path = []):
477     ''' The same as checkProgAlternatives, but for viewers '''
478     alt_rc_entry = listAlternatives(progs, 'viewer', rc_entry)
479     return checkProgAlternatives(description, progs, rc_entry,
480                                  alt_rc_entry, path, not_found = 'auto')
481
482
483 def checkEditor(description, progs, rc_entry = [], path = []):
484     ''' The same as checkProgAlternatives, but for editors '''
485     alt_rc_entry = listAlternatives(progs, 'editor', rc_entry)
486     return checkProgAlternatives(description, progs, rc_entry,
487                                  alt_rc_entry, path, not_found = 'auto')
488
489
490 def checkViewerNoRC(description, progs, rc_entry = [], path = []):
491     ''' The same as checkViewer, but do not add rc entry '''
492     alt_rc_entry = listAlternatives(progs, 'viewer', rc_entry)
493     rc_entry = []
494     return checkProgAlternatives(description, progs, rc_entry,
495                                  alt_rc_entry, path, not_found = 'auto')
496
497
498 def checkEditorNoRC(description, progs, rc_entry = [], path = []):
499     ''' The same as checkViewer, but do not add rc entry '''
500     alt_rc_entry = listAlternatives(progs, 'editor', rc_entry)
501     rc_entry = []
502     return checkProgAlternatives(description, progs, rc_entry,
503                                  alt_rc_entry, path, not_found = 'auto')
504
505
506 def checkViewerEditor(description, progs, rc_entry = [], path = []):
507     ''' The same as checkProgAlternatives, but for viewers and editors '''
508     alt_rc_entry = listAlternatives(progs, ['editor', 'viewer'], rc_entry)
509     return checkProgAlternatives(description, progs, rc_entry,
510                                  alt_rc_entry, path, not_found = 'auto')
511
512
513 def checkDTLtools():
514     ''' Check whether DTL tools are available (Windows only) '''
515     # Find programs! Returned path is not used now
516     if ((os.name == 'nt' or sys.platform == 'cygwin') and
517             checkProg('DVI to DTL converter', ['dv2dt']) != ['', ''] and
518             checkProg('DTL to DVI converter', ['dt2dv']) != ['', '']):
519         dtl_tools = True
520     else:
521         dtl_tools = False
522     return dtl_tools
523
524 def checkInkscape():
525     ''' Check whether Inkscape is available and return the full path (Windows only) '''
526     ''' On Mac OS (darwin) a wrapper is used - therefore the version is checked '''
527     ''' The answer of the real inkscape is validated and a fake binary used if this fails '''
528     if sys.platform == 'darwin':
529         version_string = cmdOutput("inkscape --version")
530         match = re.match('^Inkscape', version_string)
531         if match:
532             return 'inkscape'
533         else:
534             return 'inkscape-binary'
535     elif os.name != 'nt':
536         return 'inkscape'
537     if sys.version_info[0] < 3:
538         import _winreg as winreg
539     else:
540         import winreg
541     aReg = winreg.ConnectRegistry(None, winreg.HKEY_CLASSES_ROOT)
542     try:
543         aKey = winreg.OpenKey(aReg, r"inkscape.svg\DefaultIcon")
544         val = winreg.QueryValueEx(aKey, "")
545         return str(val[0]).split('"')[1]
546     except EnvironmentError:
547         try:
548             aKey = winreg.OpenKey(aReg, r"Applications\inkscape.exe\shell\open\command")
549             val = winreg.QueryValueEx(aKey, "")
550             return str(val[0]).split('"')[1]
551         except EnvironmentError:
552             return 'inkscape'
553
554 def checkLatex(dtl_tools):
555     ''' Check latex, return lyx_check_config '''
556     path, LATEX = checkProg('a Latex2e program', ['latex $$i', 'latex2e $$i'])
557     path, PPLATEX = checkProg('a DVI postprocessing program', ['pplatex $$i'])
558     #-----------------------------------------------------------------
559     path, PLATEX = checkProg('pLaTeX, the Japanese LaTeX', ['platex $$i'])
560     if PLATEX:
561         # check if PLATEX is pLaTeX2e
562         writeToFile('chklatex.ltx', r'\nonstopmode\makeatletter\@@end')
563         # run platex on chklatex.ltx and check result
564         if cmdOutput(PLATEX + ' chklatex.ltx').find('pLaTeX2e') != -1:
565             # We have the Japanese pLaTeX2e
566             addToRC(r'\converter platex   dvi       "%s"   "latex=platex"' % PLATEX)
567         else:
568             PLATEX = ''
569             removeFiles(['chklatex.ltx', 'chklatex.log'])
570     #-----------------------------------------------------------------
571     # use LATEX to convert from latex to dvi if PPLATEX is not available
572     if PPLATEX == '':
573         PPLATEX = LATEX
574     if dtl_tools:
575         # Windows only: DraftDVI
576         addToRC(r'''\converter latex      dvi2       "%s"       "latex,hyperref-driver=dvips"
577 \converter dvi2       dvi        "python -tt $$s/scripts/clean_dvi.py $$i $$o"  ""''' % PPLATEX)
578     else:
579         addToRC(r'\converter latex      dvi        "%s" "latex,hyperref-driver=dvips"' % PPLATEX)
580     # no latex
581     if LATEX:
582         # Check if latex is usable
583         writeToFile('chklatex.ltx', r'''
584 \nonstopmode
585 \ifx\undefined\documentclass\else
586   \message{ThisIsLaTeX2e}
587 \fi
588 \makeatletter
589 \@@end
590 ''')
591         # run latex on chklatex.ltx and check result
592         if cmdOutput(LATEX + ' chklatex.ltx').find('ThisIsLaTeX2e') != -1:
593             # valid latex2e
594             return LATEX
595         else:
596             logger.warning("Latex not usable (not LaTeX2e) ")
597         # remove temporary files
598         removeFiles(['chklatex.ltx', 'chklatex.log'])
599     return ''
600
601
602 def checkLuatex():
603     ''' Check if luatex is there '''
604     path, LUATEX = checkProg('LuaTeX', ['lualatex $$i'])
605     path, DVILUATEX = checkProg('LuaTeX (DVI)', ['dvilualatex $$i'])
606     if LUATEX:
607         addToRC(r'\converter luatex      pdf5       "%s"        "latex=lualatex"' % LUATEX)
608     if DVILUATEX:
609         addToRC(r'\converter dviluatex   dvi3        "%s"       "latex=dvilualatex"' % DVILUATEX)
610
611
612 def checkModule(module):
613     ''' Check for a Python module, return the status '''
614     msg = 'checking for "' + module + ' module"... '
615     try:
616       __import__(module)
617       logger.info(msg + ' yes')
618       return True
619     except ImportError:
620       logger.info(msg + ' no')
621       return False
622
623
624 def checkFormatEntries(dtl_tools):
625     ''' Check all formats (\Format entries) '''
626     checkViewerEditor('a Tgif viewer and editor', ['tgif'],
627         rc_entry = [r'\Format tgif      "obj, tgo" Tgif                 "" "%%" "%%"    "vector"        "application/x-tgif"'])
628     #
629     checkViewerEditor('a FIG viewer and editor', ['xfig', 'jfig3-itext.jar', 'jfig3.jar'],
630         rc_entry = [r'\Format fig        fig     FIG                    "" "%%" "%%"    "vector"        "application/x-xfig"'])
631     #
632     checkViewerEditor('a Dia viewer and editor', ['dia'],
633         rc_entry = [r'\Format dia        dia     DIA                    "" "%%" "%%"    "vector,zipped=native", "application/x-dia-diagram"'])
634     #
635     checkViewerEditor('an OpenDocument drawing viewer and editor', ['libreoffice', 'lodraw', 'ooffice', 'oodraw', 'soffice'],
636         rc_entry = [r'\Format odg        "odg, sxd" "OpenDocument drawing"   "" "%%"    "%%"    "vector,zipped=native"  "application/vnd.oasis.opendocument.graphics"'])
637     #
638     checkViewerEditor('a Grace viewer and editor', ['xmgrace'],
639         rc_entry = [r'\Format agr        agr     Grace                  "" "%%" "%%"    "vector"        ""'])
640     #
641     checkViewerEditor('a FEN viewer and editor', ['xboard -lpf $$i -mode EditPosition'],
642         rc_entry = [r'\Format fen        fen     FEN                    "" "%%" "%%"    ""      ""'])
643     #
644     checkViewerEditor('a SVG viewer and editor', [inkscape_gui],
645         rc_entry = [r'''\Format svg        "svg" SVG                "" "%%" "%%"        "vector"        "image/svg+xml"
646 \Format svgz       "svgz" "SVG (compressed)" "" "%%" "%%"       "vector,zipped=native"  ""'''],
647         path = [inkscape_path])
648     #
649     imageformats = r'''\Format bmp        bmp     BMP                    "" "%s"        "%s"    ""      "image/x-bmp"
650 \Format gif        gif     GIF                    "" "%s"       "%s"    ""      "image/gif"
651 \Format jpg       "jpg, jpeg" JPEG                "" "%s"       "%s"    ""      "image/jpeg"
652 \Format pbm        pbm     PBM                    "" "%s"       "%s"    ""      "image/x-portable-bitmap"
653 \Format pgm        pgm     PGM                    "" "%s"       "%s"    ""      "image/x-portable-graymap"
654 \Format png        png     PNG                    "" "%s"       "%s"    ""      "image/x-png"
655 \Format ppm        ppm     PPM                    "" "%s"       "%s"    ""      "image/x-portable-pixmap"
656 \Format tiff       tif     TIFF                   "" "%s"       "%s"    ""      "image/tiff"
657 \Format xbm        xbm     XBM                    "" "%s"       "%s"    ""      "image/x-xbitmap"
658 \Format xpm        xpm     XPM                    "" "%s"       "%s"    ""      "image/x-xpixmap"'''
659     path, iv = checkViewerNoRC('a raster image viewer',
660         ['xv', 'gwenview', 'kview',
661          'eog', 'xviewer', 'ristretto', 'gpicview', 'lximage-qt',
662          'xdg-open', 'gimp-remote', 'gimp'],
663         rc_entry = [imageformats])
664     path, ie = checkEditorNoRC('a raster image editor',
665         ['gimp-remote', 'gimp'], rc_entry = [imageformats])
666     addToRC(imageformats % ((iv, ie)*10))
667     #
668     checkViewerEditor('a text editor',
669         ['xemacs', 'gvim', 'kedit', 'kwrite', 'kate',
670          'nedit', 'gedit', 'geany', 'leafpad', 'mousepad', 'xed', 'notepad'],
671         rc_entry = [r'''\Format asciichess asc    "Plain text (chess output)"  "" ""    "%%"    ""      ""
672 \Format docbook    sgml    DocBook                B  "" "%%"    "document,menu=export"  ""
673 \Format docbook-xml xml   "DocBook (XML)"         "" "" "%%"    "document,menu=export"  "application/docbook+xml"
674 \Format dot        dot    "Graphviz Dot"          "" "" "%%"    "vector"        "text/vnd.graphviz"
675 \Format dviluatex  tex    "LaTeX (dviluatex)"     "" "" "%%"    "document,menu=export"  ""
676 \Format platex     tex    "LaTeX (pLaTeX)"        "" "" "%%"    "document,menu=export"  ""
677 \Format literate   nw      NoWeb                  N  "" "%%"    "document,menu=export"  ""
678 \Format sweave     Rnw    "Sweave"                S  "" "%%"    "document,menu=export"  ""
679 \Format sweave-ja  Rnw    "Sweave (Japanese)"     S  "" "%%"    "document,menu=export"  ""
680 \Format r          R      "R/S code"              "" "" "%%"    "document,menu=export"  ""
681 \Format knitr      Rnw    "Rnw (knitr)"           "" "" "%%"    "document,menu=export"  ""
682 \Format knitr-ja   Rnw    "Rnw (knitr, Japanese)" "" "" "%%"    "document,menu=export"  ""
683 \Format lilypond-book    lytex "LilyPond book (LaTeX)"   "" ""  "%%"    "document,menu=export"  ""
684 \Format lilypond-book-ja lytex "LilyPond book (pLaTeX)"   "" "" "%%"    "document,menu=export"  ""
685 \Format latex      tex    "LaTeX (plain)"         L  "" "%%"    "document,menu=export"  "text/x-tex"
686 \Format luatex     tex    "LaTeX (LuaTeX)"        "" "" "%%"    "document,menu=export"  ""
687 \Format pdflatex   tex    "LaTeX (pdflatex)"      "" "" "%%"    "document,menu=export"  ""
688 \Format xetex      tex    "LaTeX (XeTeX)"         "" "" "%%"    "document,menu=export"  ""
689 \Format latexclipboard tex "LaTeX (clipboard)"    "" "" "%%"    ""      ""
690 \Format text       txt    "Plain text"            a  "" "%%"    "document,menu=export"  "text/plain"
691 \Format text2      txt    "Plain text (pstotext)" "" "" "%%"    "document"      ""
692 \Format text3      txt    "Plain text (ps2ascii)" "" "" "%%"    "document"      ""
693 \Format text4      txt    "Plain text (catdvi)"   "" "" "%%"    "document"      ""
694 \Format textparagraph txt "Plain Text, Join Lines" "" ""        "%%"    "document"      ""
695 \Format beamer.info pdf.info   "Info (Beamer)"         "" ""   "%%"    "document,menu=export"   ""''' ])
696    #Lilypond files have special editors, but fall back to plain text editors
697     checkViewerEditor('a lilypond editor',
698         ['frescobaldi', 'xemacs', 'gvim', 'kedit', 'kwrite', 'kate',
699          'nedit', 'gedit', 'geany', 'leafpad', 'mousepad', 'xed', 'notepad'],
700         rc_entry = [r'''\Format lilypond   ly     "LilyPond music"        "" "" "%%"    "vector"        "text/x-lilypond"''' ])
701    #Spreadsheets using ssconvert from gnumeric
702     checkViewer('gnumeric spreadsheet software', ['gnumeric'],
703       rc_entry = [r'''\Format gnumeric gnumeric "Gnumeric spreadsheet" "" ""    "%%"   "document"       "application/x-gnumeric"
704 \Format excel      xls    "Excel spreadsheet"      "" "" "%%"    "document"     "application/vnd.ms-excel"
705 \Format excel2     xlsx   "MS Excel Office Open XML" "" "" "%%" "document"      "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
706 \Format html_table html   "HTML Table (for spreadsheets)"      "" "" "%%"    "document" "text/html"
707 \Format oocalc     ods    "OpenDocument spreadsheet" "" "" "%%"    "document"   "application/vnd.oasis.opendocument.spreadsheet"'''])
708  #
709     checkViewer('an HTML previewer', ['firefox', 'mozilla file://$$p$$i', 'netscape'],
710         rc_entry = [r'\Format xhtml      xhtml   "LyXHTML"              y "%%" ""    "document,menu=export"     "application/xhtml+xml"'])
711  #
712     checkEditor('a BibTeX editor', ['jabref', 'JabRef',
713         'pybliographic', 'bibdesk', 'gbib', 'kbib',
714         'kbibtex', 'sixpack', 'bibedit', 'tkbibtex'
715         'xemacs', 'gvim', 'kedit', 'kwrite', 'kate',
716         'jedit', 'TeXnicCenter', 'WinEdt', 'WinShell', 'PSPad',
717         'nedit', 'gedit', 'notepad', 'geany', 'leafpad', 'mousepad'],
718         rc_entry = [r'''\Format bibtex bib    "BibTeX"         "" ""    "%%"    ""      "text/x-bibtex"''' ])
719     #
720     #checkProg('a Postscript interpreter', ['gs'],
721     #  rc_entry = [ r'\ps_command "%%"' ])
722     checkViewer('a Postscript previewer',
723                 ['kghostview', 'okular', 'qpdfview --unique',
724                  'evince', 'xreader',
725                  'gv', 'ghostview -swap', 'gsview64', 'gsview32'],
726         rc_entry = [r'''\Format eps        eps     EPS                    "" "%%"       ""      "vector"        "image/x-eps"
727 \Format eps2       eps    "EPS (uncropped)"       "" "%%"       ""      "vector"        ""
728 \Format eps3       eps    "EPS (cropped)"         "" "%%"       ""      "document"      ""
729 \Format ps         ps      Postscript             t  "%%"       ""      "document,vector,menu=export"   "application/postscript"'''])
730     # for xdg-open issues look here: http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg151818.html
731     # maybe use "bestApplication()" from https://github.com/jleclanche/python-mime
732     # the MIME type is set for pdf6, because that one needs to be autodetectable by libmime
733     checkViewer('a PDF previewer',
734                 ['pdfview', 'kpdf', 'okular', 'qpdfview --unique',
735                  'evince', 'xreader', 'kghostview', 'xpdf', 'SumatraPDF',
736                  'acrobat', 'acroread', 'mupdf',
737                  'gv', 'ghostview', 'AcroRd32', 'gsview64', 'gsview32'],
738         rc_entry = [r'''\Format pdf        pdf    "PDF (ps2pdf)"          P  "%%"       ""      "document,vector,menu=export"   ""
739 \Format pdf2       pdf    "PDF (pdflatex)"        F  "%%"       ""      "document,vector,menu=export"   ""
740 \Format pdf3       pdf    "PDF (dvipdfm)"         m  "%%"       ""      "document,vector,menu=export"   ""
741 \Format pdf4       pdf    "PDF (XeTeX)"           X  "%%"       ""      "document,vector,menu=export"   ""
742 \Format pdf5       pdf    "PDF (LuaTeX)"          u  "%%"       ""      "document,vector,menu=export"   ""
743 \Format pdf6       pdf    "PDF (graphics)"        "" "%%"       ""      "vector"        "application/pdf"
744 \Format pdf7       pdf    "PDF (cropped)"         "" "%%"       ""      "document,vector"       ""
745 \Format pdf8       pdf    "PDF (lower resolution)"         "" "%%"      ""      "document,vector"       ""'''])
746     #
747     checkViewer('a DVI previewer', ['xdvi', 'kdvi', 'okular',
748                                     'evince', 'xreader',
749                                     'yap', 'dviout -Set=!m'],
750         rc_entry = [r'''\Format dvi        dvi     DVI                    D  "%%"       ""      "document,vector,menu=export"   "application/x-dvi"
751 \Format dvi3       dvi     "DVI (LuaTeX)"          V  "%%"      ""      "document,vector,menu=export"   ""'''])
752     if dtl_tools:
753         # Windows only: DraftDVI
754         addToRC(r'\Format dvi2       dvi     DraftDVI               ""  ""      ""      "vector"        ""')
755     #
756     checkViewer('an HTML previewer', ['firefox', 'mozilla file://$$p$$i', 'netscape'],
757         rc_entry = [r'\Format html      "html, htm" HTML                H  "%%" ""      "document,menu=export"  "text/html"'])
758     #
759     checkViewerEditor('Noteedit', ['noteedit'],
760         rc_entry = [r'\Format noteedit   not     Noteedit               "" "%%" "%%"    "vector"        ""'])
761     #
762     checkViewerEditor('an OpenDocument viewer', ['libreoffice', 'lwriter', 'lowriter', 'oowriter', 'swriter', 'abiword'],
763         rc_entry = [r'''\Format odt        odt     "OpenDocument (tex4ht)"  "" "%%"     "%%"    "document,vector,menu=export"   "application/vnd.oasis.opendocument.text"
764 \Format odt2       odt    "OpenDocument (eLyXer)"  "" "%%"      "%%"    "document,vector,menu=export"   "application/vnd.oasis.opendocument.text"
765 \Format odt3       odt    "OpenDocument (Pandoc)"  "" "%%"      "%%"    "document,vector,menu=export"   "application/vnd.oasis.opendocument.text"
766 \Format sxw        sxw    "OpenOffice.Org (sxw)"  "" "" ""      "document,vector"       "application/vnd.sun.xml.writer"'''])
767     #
768     checkViewerEditor('a Rich Text and Word viewer', ['libreoffice', 'lwriter', 'lowriter', 'oowriter', 'swriter', 'abiword'],
769         rc_entry = [r'''\Format rtf        rtf    "Rich Text Format"      "" "%%"       "%%"    "document,vector,menu=export"   "application/rtf"
770 \Format word       doc    "MS Word"               W  "%%"       "%%"    "document,vector,menu=export"   "application/msword"
771 \Format word2      docx    "MS Word Office Open XML"               O  "%%"      "%%"    "document,vector,menu=export"   "application/vnd.openxmlformats-officedocument.wordprocessingml.document"'''])
772     #
773     # entries that do not need checkProg
774     addToRC(r'''\Format csv        csv    "Table (CSV)"           "" "" ""      "document"      "text/csv"
775 \Format fax        ""      Fax                    "" "" ""      "document"      ""
776 \Format lyx        lyx     LyX                    "" "" ""      ""      "application/x-lyx"
777 \Format lyx13x     13.lyx "LyX 1.3.x"             "" "" ""      "document"      ""
778 \Format lyx14x     14.lyx "LyX 1.4.x"             "" "" ""      "document"      ""
779 \Format lyx15x     15.lyx "LyX 1.5.x"             "" "" ""      "document"      ""
780 \Format lyx16x     16.lyx "LyX 1.6.x"             "" "" ""      "document"      ""
781 \Format lyx20x     20.lyx "LyX 2.0.x"             "" "" ""      "document"      ""
782 \Format lyx21x     21.lyx "LyX 2.1.x"             "" "" ""      "document"      ""
783 \Format lyx22x     22.lyx "LyX 2.2.x"             "" "" ""      "document,menu=export"  ""
784 \Format clyx       cjklyx "CJK LyX 1.4.x (big5)"  "" "" ""      "document"      ""
785 \Format jlyx       cjklyx "CJK LyX 1.4.x (euc-jp)" "" ""        ""      "document"      ""
786 \Format klyx       cjklyx "CJK LyX 1.4.x (euc-kr)" "" ""        ""      "document"      ""
787 \Format lyxpreview lyxpreview "LyX Preview"       "" "" ""      ""      ""
788 \Format pdftex     "pdftex_t, pdf_tex" PDFTEX                "" ""      ""      ""      ""
789 \Format program    ""      Program                "" "" ""      ""      ""
790 \Format pstex      "pstex_t, ps_tex" PSTEX                  "" ""       ""      ""      ""
791 \Format wmf        wmf    "Windows Metafile"      "" "" ""      "vector"        "image/x-wmf"
792 \Format emf        emf    "Enhanced Metafile"     "" "" ""      "vector"        "image/x-emf"
793 \Format wordhtml  "html, htm" "HTML (MS Word)"    "" "" ""      "document"      ""
794 ''')
795
796
797 def checkConverterEntries():
798     ''' Check all converters (\converter entries) '''
799     checkProg('the pdflatex program', ['pdflatex $$i'],
800         rc_entry = [ r'\converter pdflatex   pdf2       "%%"    "latex=pdflatex,hyperref-driver=pdftex"' ])
801
802     checkProg('XeTeX', ['xelatex $$i'],
803         rc_entry = [ r'\converter xetex      pdf4       "%%"    "latex=xelatex,hyperref-driver=xetex"' ])
804
805     checkLuatex()
806
807     # Look for tex2lyx in this order (see bugs #3308 and #6986):
808     #   1)  If we're building LyX with autotools then tex2lyx is found
809     #       in the subdirectory tex2lyx with respect to the binary dir.
810     #   2)  If we're building LyX with cmake then tex2lyx is found
811     #       in the binary dir.
812     #   3)  If LyX was configured with a version suffix then tex2lyx
813     #       will also have this version suffix.
814     #   4)  Otherwise always use tex2lyx.
815     in_binary_subdir = os.path.join(lyx_binary_dir, 'tex2lyx', 'tex2lyx')
816     in_binary_subdir = os.path.abspath(in_binary_subdir).replace('\\', '/')
817
818     in_binary_dir = os.path.join(lyx_binary_dir, 'tex2lyx')
819     in_binary_dir = os.path.abspath(in_binary_dir).replace('\\', '/')
820
821     path, t2l = checkProg('a LaTeX/Noweb -> LyX converter', [quoteIfSpace(in_binary_subdir), quoteIfSpace(in_binary_subdir + version_suffix), quoteIfSpace(in_binary_dir), quoteIfSpace(in_binary_dir + version_suffix), 'tex2lyx' + version_suffix, 'tex2lyx'],
822         rc_entry = [r'''\converter latex      lyx        "%% -f $$i $$o"        ""
823 \converter latexclipboard lyx        "%% -fixedenc utf8 -f $$i $$o"     ""
824 \converter literate   lyx        "%% -n -m noweb -f $$i $$o"    ""
825 \converter sweave   lyx        "%% -n -m sweave -f $$i $$o"     ""
826 \converter knitr   lyx        "%% -n -m knitr -f $$i $$o"       ""'''], not_found = 'tex2lyx')
827     if path == '':
828         logger.warning("Failed to find tex2lyx on your system.")
829
830     #
831     checkProg('a Noweb -> LaTeX converter', ['noweave -delay -index $$i > $$o'],
832         rc_entry = [r'''\converter literate   latex      "%%"   ""
833 \converter literate   pdflatex      "%%"        ""
834 \converter literate   xetex         "%%"        ""
835 \converter literate   luatex        "%%"        ""
836 \converter literate   dviluatex     "%%"        ""'''])
837     #
838     checkProg('a Sweave -> LaTeX converter', ['Rscript --verbose --no-save --no-restore $$s/scripts/lyxsweave.R $$p$$i $$p$$o $$e $$r'],
839         rc_entry = [r'''\converter sweave   latex      "%%"     "needauth"
840 \converter sweave   pdflatex   "%%"     "needauth"
841 \converter sweave-ja   platex     "%%"  "needauth"
842 \converter sweave   xetex      "%%"     "needauth"
843 \converter sweave   luatex     "%%"     "needauth"
844 \converter sweave   dviluatex  "%%"     "needauth"'''])
845     #
846     checkProg('a knitr -> LaTeX converter', ['Rscript --verbose --no-save --no-restore $$s/scripts/lyxknitr.R $$p$$i $$p$$o $$e $$r'],
847         rc_entry = [r'''\converter knitr   latex      "%%"      "needauth"
848 \converter knitr   pdflatex   "%%"      "needauth"
849 \converter knitr-ja   platex     "%%"   "needauth"
850 \converter knitr   xetex      "%%"      "needauth"
851 \converter knitr   luatex     "%%"      "needauth"
852 \converter knitr   dviluatex  "%%"      "needauth"'''])
853     #
854     checkProg('a Sweave -> R/S code converter', ['Rscript --verbose --no-save --no-restore $$s/scripts/lyxstangle.R $$i $$e $$r'],
855         rc_entry = [ r'\converter sweave      r      "%%"    ""',
856                      r'\converter sweave-ja   r      "%%"    ""' ])
857     #
858     checkProg('a knitr -> R/S code converter', ['Rscript --verbose --no-save --no-restore $$s/scripts/lyxknitr.R $$p$$i $$p$$o $$e $$r tangle'],
859         rc_entry = [ r'\converter knitr      r      "%%"    ""',
860                      r'\converter knitr-ja   r      "%%"    ""' ])
861     #
862     checkProg('an HTML -> LaTeX converter', ['html2latex $$i', 'gnuhtml2latex',
863         'htmltolatex -input $$i -output $$o', 'htmltolatex.jar -input $$i -output $$o'],
864         rc_entry = [ r'\converter html       latex      "%%"    ""',
865                      r'\converter html       latex      "python -tt $$s/scripts/html2latexwrapper.py %% $$i $$o"        ""',
866                      r'\converter html       latex      "%%"    ""',
867                      r'\converter html       latex      "%%"    ""', '' ])
868     #
869     checkProg('an MS Word -> LaTeX converter', ['wvCleanLatex $$i $$o'],
870         rc_entry = [ r'\converter word       latex      "%%"    ""' ])
871
872     # eLyXer: search as an executable (elyxer.py, elyxer)
873     path, elyxer = checkProg('a LyX -> HTML converter',
874         ['elyxer.py --nofooter --directory $$r $$i $$o', 'elyxer --nofooter --directory $$r $$i $$o'],
875         rc_entry = [ r'\converter lyx      html       "%%"      ""' ])
876     path, elyxer = checkProg('a LyX -> HTML (MS Word) converter',
877         ['elyxer.py --nofooter --html --directory $$r $$i $$o', 'elyxer --nofooter --html --directory $$r $$i $$o'],
878         rc_entry = [ r'\converter lyx      wordhtml       "%%"  ""' ])
879     path, elyxer = checkProg('a LyX -> OpenDocument (eLyXer) converter',
880         ['elyxer.py --html --nofooter --unicode --directory $$r $$i $$o', 'elyxer --html --nofooter --unicode --directory $$r $$i $$o'],
881         rc_entry = [ r'\converter lyx      odt2       "%%"      ""' ])
882     path, elyxer = checkProg('a LyX -> Word converter',
883         ['elyxer.py --html --nofooter --unicode --directory $$r $$i $$o', 'elyxer --html --nofooter --unicode --directory $$r $$i $$o'],
884         rc_entry = [ r'\converter lyx      word      "%%"       ""' ])
885     if elyxer.find('elyxer') >= 0:
886       addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py -e html,png,jpg,jpeg,css $$i $$o"''')
887       addToRC(r'''\copier    wordhtml       "python -tt $$s/scripts/ext_copy.py -e html,png,jpg,jpeg,css $$i $$o"''')
888     else:
889       # search for HTML converters other than eLyXer
890       # On SuSE the scripts have a .sh suffix, and on debian they are in /usr/share/tex4ht/
891       path, htmlconv = checkProg('a LaTeX -> HTML converter', ['htlatex $$i', 'htlatex.sh $$i',
892           '/usr/share/tex4ht/htlatex $$i', 'tth  -t -e2 -L$$b < $$i > $$o',
893           'latex2html -no_subdir -split 0 -show_section_numbers $$i', 'hevea -s $$i'],
894           rc_entry = [ r'\converter latex      html       "%%"  "needaux"' ])
895       if htmlconv.find('htlatex') >= 0 or htmlconv == 'latex2html':
896         addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py -e html,png,css $$i $$o"''')
897       else:
898         addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py $$i $$o"''')
899       path, htmlconv = checkProg('a LaTeX -> HTML (MS Word) converter', ["htlatex $$i 'html,word' 'symbol/!' '-cvalidate'",
900           "htlatex.sh $$i 'html,word' 'symbol/!' '-cvalidate'",
901           "/usr/share/tex4ht/htlatex $$i 'html,word' 'symbol/!' '-cvalidate'"],
902           rc_entry = [ r'\converter latex      wordhtml   "%%"  "needaux"' ])
903       if htmlconv.find('htlatex') >= 0:
904         addToRC(r'''\copier    wordhtml       "python -tt $$s/scripts/ext_copy.py -e html,png,css $$i $$o"''')
905       else:
906         addToRC(r'''\copier    wordhtml       "python -tt $$s/scripts/ext_copy.py $$i $$o"''')
907
908
909     # Check if LyXBlogger is installed
910     lyxblogger_found = checkModule('lyxblogger')
911     if lyxblogger_found:
912       addToRC(r'\Format    blog       blog       "LyXBlogger"           "" "" ""  "document"  ""')
913       addToRC(r'\converter xhtml      blog       "python -m lyxblogger $$i"       ""')
914
915     #
916     checkProg('an OpenOffice.org -> LaTeX converter', ['w2l -clean $$i'],
917         rc_entry = [ r'\converter sxw        latex      "%%"    ""' ])
918     #
919     checkProg('an OpenDocument -> LaTeX converter', ['w2l -clean $$i'],
920         rc_entry = [ r'\converter odt        latex      "%%"    ""' ])
921     #
922     checkProg('an Open Document (Pandoc) -> LaTeX converter', ['pandoc -s -f odt -o $$o -t latex $$i'],
923         rc_entry = [ r'\converter odt3        latex      "%%"   ""' ])
924     #
925     checkProg('a MS Word Office Open XML converter -> LaTeX', ['pandoc -s -f docx -o $$o -t latex $$i'],
926         rc_entry = [ r'\converter word2      latex      "%%"    ""' ])
927     # Only define a converter to pdf6, otherwise the odt format could be
928     # used as an intermediate step for export to pdf, which is not wanted.
929     checkProg('an OpenDocument -> PDF converter', ['unoconv -f pdf --stdout $$i > $$o'],
930         rc_entry = [ r'\converter odt        pdf6       "%%"    ""' ])
931     # According to http://www.tug.org/applications/tex4ht/mn-commands.html
932     # the command mk4ht oolatex $$i has to be used as default,
933     # but as this would require to have Perl installed, in MiKTeX oolatex is
934     # directly available as application.
935     # On SuSE the scripts have a .sh suffix, and on debian they are in /usr/share/tex4ht/
936     # Both SuSE and debian have oolatex
937     checkProg('a LaTeX -> Open Document (tex4ht) converter', [
938         'oolatex $$i', 'mk4ht oolatex $$i', 'oolatex.sh $$i', '/usr/share/tex4ht/oolatex $$i',
939         'htlatex $$i \'xhtml,ooffice\' \'ooffice/! -cmozhtf\' \'-coo\' \'-cvalidate\''],
940         rc_entry = [ r'\converter latex      odt        "%%"    "needaux"' ])
941     # On windows it is called latex2rt.exe
942     checkProg('a LaTeX -> RTF converter', ['latex2rtf -p -S -o $$o $$i', 'latex2rt -p -S -o $$o $$i'],
943         rc_entry = [ r'\converter latex      rtf        "%%"    "needaux"' ])
944     #
945     checkProg('a LaTeX -> Open Document (Pandoc) converter', ['pandoc -s -f latex -o $$o -t odt $$i'],
946         rc_entry = [ r'\converter latex      odt3        "%%"   ""' ])
947     #
948     checkProg('a LaTeX -> MS Word Office Open XML converter', ['pandoc -s -f latex -o $$o -t docx $$i'],
949         rc_entry = [ r'\converter latex      word2       "%%"   ""' ])
950     #
951     checkProg('a RTF -> HTML converter', ['unrtf --html  $$i > $$o'],
952         rc_entry = [ r'\converter rtf      html        "%%"     ""' ])
953     # Do not define a converter to pdf6, ps is a pure export format
954     checkProg('a PS to PDF converter', ['ps2pdf $$i $$o'],
955         rc_entry = [ r'\converter ps         pdf        "%%"    "hyperref-driver=dvips"' ])
956     #
957     checkProg('a PS to TXT converter', ['pstotext $$i > $$o'],
958         rc_entry = [ r'\converter ps         text2      "%%"    ""' ])
959     #
960     checkProg('a PS to TXT converter', ['ps2ascii $$i $$o'],
961         rc_entry = [ r'\converter ps         text3      "%%"    ""' ])
962     # Need to call ps2eps in a pipe, otherwise it would name the output file
963     # depending on the extension of the input file. We do not know the input
964     # file extension in general, so the resultfile= flag would not help.
965     # Since ps2eps crops the image, we do not use it to convert from ps->eps.
966     # This would create additional paths in the converter graph with unwanted
967     # side effects (e.g. ps->pdf via ps2pdf would create a different result
968     # than ps->eps->pdf via ps2eps and epstopdf).
969     checkProg('a PS to EPS converter', ['ps2eps -- < $$i > $$o'],
970         rc_entry = [ r'\converter eps2       eps      "%%"      ""' ])
971     #
972     checkProg('a PDF to PS converter', ['pdftops $$i $$o', 'pdf2ps $$i $$o'],
973         rc_entry = [ r'\converter pdf         ps        "%%"    ""' ])
974     # Only define a converter from pdf6 for graphics
975     checkProg('a PDF to EPS converter', ['pdftops -eps -f 1 -l 1 $$i $$o'],
976         rc_entry = [ r'\converter pdf6        eps        "%%"   ""' ])
977     # Define a converter from pdf6 to png for Macs where pdftops is missing.
978     # The converter utility sips allows to force the dimensions of the resulting
979     # png image. The value of 800 pixel for the width is arbitrary and not
980     # related to the current screen resolution or width.
981     # There is no converter parameter for this information.
982     checkProg('a PDF to PNG converter',
983         ['sips --resampleWidth 800 --setProperty format png $$i --out $$o'],
984         rc_entry = [ r'\converter pdf6        png        "%%" ""' ])
985     # Create one converter for a PDF produced using TeX fonts and one for a
986     # PDF produced using non-TeX fonts. This does not produce non-unique
987     # conversion paths, since a given document either uses TeX fonts or not.
988     checkProg('a PDF cropping tool', ['pdfcrop $$i $$o'],
989         rc_entry = [ r'''\converter pdf2   pdf7       "%%"      ""
990 \converter pdf4   pdf7       "%%"       ""''' ])
991     # Create one converter for a PDF produced using TeX fonts and one for a
992     # PDF produced using non-TeX fonts. This does not produce non-unique
993     # conversion paths, since a given document either uses TeX fonts or not.
994     checkProg('Ghostscript', ["gswin32c", "gswin64c", "gs"],
995         rc_entry = [ r'''\converter pdf2   pdf8       "python -tt $$s/scripts/convert_pdf.py $$i $$o ebook"     ""
996 \converter pdf4   pdf8       "python -tt $$s/scripts/convert_pdf.py $$i $$o ebook"      ""''' ])
997     #
998     checkProg('a Beamer info extractor', ['makebeamerinfo -p $$i'],
999         rc_entry = [ r'\converter pdf2         beamer.info        "%%"  ""' ])
1000     #
1001     checkProg('a DVI to TXT converter', ['catdvi $$i > $$o'],
1002         rc_entry = [ r'\converter dvi        text4      "%%"    ""' ])
1003     #
1004     checkProg('a DVI to PS converter', ['dvips -o $$o $$i'],
1005         rc_entry = [ r'\converter dvi        ps         "%%"    "hyperref-driver=dvips"' ])
1006     #
1007     checkProg('a DVI to cropped EPS converter', ['dvips -E -o $$o $$i'],
1008         rc_entry = [ r'\converter dvi        eps3         "%%"  ""' ])
1009     #
1010     checkProg('a DVI to PDF converter', ['dvipdfmx', 'dvipdfm'],
1011         rc_entry = [ r'\converter dvi        pdf3       "%%  -o $$o $$i"        "hyperref-driver=%%"' ])
1012     #
1013     checkProg('a fax program', ['kdeprintfax $$i', 'ksendfax $$i', 'hylapex $$i'],
1014         rc_entry = [ r'\converter ps         fax        "%%"    ""'])
1015     #
1016     path, fig2dev = checkProg('a FIG -> Image converter', ['fig2dev'])
1017     if fig2dev == "fig2dev":
1018         addToRC(r'''\converter fig        eps        "fig2dev -L eps $$i $$o"   ""
1019 \converter fig        ppm        "fig2dev -L ppm $$i $$o"       ""
1020 \converter fig        svg        "fig2dev -L svg $$i $$o"       ""
1021 \converter fig        png        "fig2dev -L png $$i $$o"       ""
1022 \converter fig        pdftex     "python -tt $$s/scripts/fig2pdftex.py $$i $$o" ""
1023 \converter fig        pstex      "python -tt $$s/scripts/fig2pstex.py $$i $$o"  ""''')
1024     #
1025     checkProg('a SVG -> PDFTeX converter', [inkscape_cl],
1026         rc_entry = [ r'\converter svg        pdftex     "python -tt $$s/scripts/svg2pdftex.py %% $$p$$i $$p$$o" ""'],
1027         path = [inkscape_path])
1028     #
1029     checkProg('a SVG -> PSTeX converter', [inkscape_cl],
1030         rc_entry = [ r'\converter svg        pstex     "python -tt $$s/scripts/svg2pstex.py %% $$p$$i $$p$$o" ""'],
1031         path = [inkscape_path])
1032     #
1033     checkProg('a TIFF -> PS converter', ['tiff2ps $$i > $$o'],
1034         rc_entry = [ r'\converter tiff       eps        "%%"    ""'])
1035     #
1036     checkProg('a TGIF -> EPS/PPM converter', ['tgif'],
1037         rc_entry = [
1038             r'''\converter tgif       eps        "tgif -print -color -eps -stdout $$i > $$o"    ""
1039 \converter tgif       png        "tgif -print -color -png -o $$d $$i"   ""
1040 \converter tgif       pdf6       "tgif -print -color -pdf -stdout $$i > $$o"    ""'''])
1041     #
1042     checkProg('a WMF -> EPS converter', ['metafile2eps $$i $$o', 'wmf2eps -o $$o $$i', inkscape_cl + ' --file=$$i --export-area-drawing --without-gui --export-eps=$$o'],
1043         rc_entry = [ r'\converter wmf        eps        "%%"    ""'])
1044     #
1045     checkProg('an EMF -> EPS converter', ['metafile2eps $$i $$o', inkscape_cl + ' --file=$$i --export-area-drawing --without-gui --export-eps=$$o'],
1046         rc_entry = [ r'\converter emf        eps        "%%"    ""'])
1047     #
1048     checkProg('a WMF -> PDF converter', [inkscape_cl + ' --file=$$i --export-area-drawing --without-gui --export-pdf=$$o'],
1049         rc_entry = [ r'\converter wmf        pdf6        "%%"   ""'])
1050     #
1051     checkProg('an EMF -> PDF converter', [inkscape_cl + ' --file=$$i --export-area-drawing --without-gui --export-pdf=$$o'],
1052         rc_entry = [ r'\converter emf        pdf6        "%%"   ""'])
1053     # Only define a converter to pdf6 for graphics
1054     checkProg('an EPS -> PDF converter', ['epstopdf'],
1055         rc_entry = [ r'\converter eps        pdf6       "epstopdf --outfile=$$o $$i"    ""'])
1056     #
1057     checkProg('an EPS -> PNG converter', ['magick $$i $$o', 'convert $$i $$o'],
1058         rc_entry = [ r'\converter eps        png        "%%"    ""'])
1059     #
1060     # no agr -> pdf6 converter, since the pdf library used by gracebat is not
1061     # free software and therefore not compiled in in many installations.
1062     # Fortunately, this is not a big problem, because we will use epstopdf to
1063     # convert from agr to pdf6 via eps without loss of quality.
1064     checkProg('a Grace -> Image converter', ['gracebat'],
1065         rc_entry = [
1066             r'''\converter agr        eps        "gracebat -hardcopy -printfile $$o -hdevice EPS $$i 2>/dev/null"       ""
1067 \converter agr        png        "gracebat -hardcopy -printfile $$o -hdevice PNG $$i 2>/dev/null"       ""
1068 \converter agr        jpg        "gracebat -hardcopy -printfile $$o -hdevice JPEG $$i 2>/dev/null"      ""
1069 \converter agr        ppm        "gracebat -hardcopy -printfile $$o -hdevice PNM $$i 2>/dev/null"       ""'''])
1070     #
1071     checkProg('a Dot -> Image converter', ['dot'],
1072         rc_entry = [
1073             r'''\converter dot        eps        "dot -Teps $$i -o $$o" ""
1074 \converter dot        png        "dot -Tpng $$i -o $$o" ""'''])
1075     #
1076     path, dia = checkProg('a Dia -> Image converter', ['dia'])
1077     if dia == 'dia':
1078         addToRC(r'''\converter dia        png        "dia -e $$o -t png $$i"    ""
1079 \converter dia        eps        "dia -e $$o -t eps $$i"        ""
1080 \converter dia        svg        "dia -e $$o -t svg $$i"        ""''')
1081
1082     #
1083     # Actually, this produces EPS, but with a wrong bounding box (usually A4 or letter).
1084     # The eps2->eps converter then fixes the bounding box by cropping.
1085     # Although unoconv can convert to png and pdf as well, do not define
1086     # odg->png and odg->pdf converters, since the bb would be too large as well.
1087     checkProg('an OpenDocument -> EPS converter', ['libreoffice --headless --nologo --convert-to eps $$i', 'unoconv -f eps --stdout $$i > $$o'],
1088         rc_entry = [ r'\converter odg        eps2       "%%"    ""'])
1089     #
1090     checkProg('a SVG (compressed) -> SVG converter', ['gunzip -c $$i > $$o'],
1091         rc_entry = [ r'\converter svgz       svg        "%%"    ""'])
1092     #
1093     checkProg('a SVG -> SVG (compressed) converter', ['gzip -c $$i > $$o'],
1094         rc_entry = [ r'\converter svg        svgz       "%%"    ""'])
1095     # Only define a converter to pdf6 for graphics
1096     # Prefer rsvg-convert over inkscape since it is faster (see http://www.lyx.org/trac/ticket/9891)
1097     checkProg('a SVG -> PDF converter', ['rsvg-convert -f pdf -o $$o $$i', inkscape_cl + ' --file=$$i --export-area-drawing --without-gui --export-pdf=$$o'],
1098         rc_entry = [ r'''\converter svg        pdf6       "%%"    ""
1099 \converter svgz       pdf6       "%%"    ""'''],
1100         path = ['', inkscape_path])
1101     #
1102     checkProg('a SVG -> EPS converter', ['rsvg-convert -f ps -o $$o $$i', inkscape_cl + ' --file=$$i --export-area-drawing --without-gui --export-eps=$$o'],
1103         rc_entry = [ r'''\converter svg        eps        "%%"    ""
1104 \converter svgz       eps        "%%"    ""'''],
1105         path = ['', inkscape_path])
1106     #
1107     checkProg('a SVG -> PNG converter', ['rsvg-convert -f png -o $$o $$i', inkscape_cl + ' --without-gui --file=$$i --export-png=$$o'],
1108         rc_entry = [ r'''\converter svg        png        "%%"    "",
1109 \converter svgz       png        "%%"    ""'''],
1110         path = ['', inkscape_path])
1111     #
1112     checkProg('Gnuplot', ['gnuplot'], 
1113         rc_entry = [ r'''\Format gnuplot     "gp, gnuplot"    "Gnuplot"     "" "" ""  "vector"  "text/plain"
1114 \converter gnuplot      pdf6      "python -tt $$s/scripts/gnuplot2pdf.py $$i $$o"    "needauth"''' ])
1115     #
1116     # gnumeric/xls/ods to tex
1117     checkProg('a spreadsheet -> latex converter', ['ssconvert'],
1118        rc_entry = [ r'''\converter gnumeric latex "ssconvert --export-type=Gnumeric_html:latex $$i $$o" ""
1119 \converter oocalc latex "ssconvert --export-type=Gnumeric_html:latex $$i $$o" ""
1120 \converter excel  latex "ssconvert --export-type=Gnumeric_html:latex $$i $$o" ""
1121 \converter excel2 latex "ssconvert --export-type=Gnumeric_html:latex $$i $$o" ""
1122 \converter gnumeric html_table "ssconvert --export-type=Gnumeric_html:html40frag $$i $$o" ""
1123 \converter oocalc html_table "ssconvert --export-type=Gnumeric_html:html40frag $$i $$o" ""
1124 \converter excel  html_table "ssconvert --export-type=Gnumeric_html:html40frag $$i $$o" ""
1125 \converter excel2 html_table "ssconvert --export-type=Gnumeric_html:html40frag $$i $$o" ""
1126 '''])
1127
1128     path, lilypond = checkProg('a LilyPond -> EPS/PDF/PNG converter', ['lilypond'])
1129     if (lilypond):
1130         version_string = cmdOutput("lilypond --version")
1131         match = re.match('GNU LilyPond (\S+)', version_string)
1132         if match:
1133             version_number = match.groups()[0]
1134             version = version_number.split('.')
1135             if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 11):
1136                 addToRC(r'''\converter lilypond   eps        "lilypond -dbackend=eps -dsafe --ps $$i"   ""
1137 \converter lilypond   png        "lilypond -dbackend=eps -dsafe --png $$i"      ""''')
1138                 addToRC(r'\converter lilypond   pdf6       "lilypond -dbackend=eps -dsafe --pdf $$i"    ""')
1139                 logger.info('+  found LilyPond version %s.' % version_number)
1140             elif int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 6):
1141                 addToRC(r'''\converter lilypond   eps        "lilypond -b eps --ps --safe $$i"  ""
1142 \converter lilypond   png        "lilypond -b eps --png $$i"    ""''')
1143                 if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 9):
1144                     addToRC(r'\converter lilypond   pdf6       "lilypond -b eps --pdf --safe $$i"       ""')
1145                 logger.info('+  found LilyPond version %s.' % version_number)
1146             else:
1147                 logger.info('+  found LilyPond, but version %s is too old.' % version_number)
1148         else:
1149             logger.info('+  found LilyPond, but could not extract version number.')
1150     #
1151     path, lilypond_book = checkProg('a LilyPond book (LaTeX) -> LaTeX converter', ['lilypond-book'])
1152     if (lilypond_book):
1153         version_string = cmdOutput("lilypond-book --version")
1154         match = re.match('^(\S+)$', version_string)
1155         if match:
1156             version_number = match.groups()[0]
1157             version = version_number.split('.')
1158             if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 13):
1159                 # Note: The --lily-output-dir flag is required because lilypond-book
1160                 #       does not process input again unless the input has changed,
1161                 #       even if the output format being requested is different. So
1162                 #       once a .eps file exists, lilypond-book won't create a .pdf
1163                 #       even when requested with --pdf. This is a problem if a user
1164                 #       clicks View PDF after having done a View DVI. To circumvent
1165                 #       this, use different output folders for eps and pdf outputs.
1166                 addToRC(r'\converter lilypond-book latex    "lilypond-book --safe --lily-output-dir=ly-eps $$i"                                ""')
1167                 addToRC(r'\converter lilypond-book pdflatex "lilypond-book --safe --pdf --latex-program=pdflatex --lily-output-dir=ly-pdf $$i" ""')
1168                 addToRC(r'\converter lilypond-book-ja platex "lilypond-book --safe --pdf --latex-program=platex --lily-output-dir=ly-pdf $$i" ""')
1169                 addToRC(r'\converter lilypond-book xetex    "lilypond-book --safe --pdf --latex-program=xelatex --lily-output-dir=ly-pdf $$i"  ""')
1170                 addToRC(r'\converter lilypond-book luatex   "lilypond-book --safe --pdf --latex-program=lualatex --lily-output-dir=ly-pdf $$i" ""')
1171                 addToRC(r'\converter lilypond-book dviluatex "lilypond-book --safe --latex-program=dvilualatex --lily-output-dir=ly-eps $$i" ""')
1172                 logger.info('+  found LilyPond-book version %s.' % version_number)
1173             else:
1174                 logger.info('+  found LilyPond-book, but version %s is too old.' % version_number)
1175         else:
1176             logger.info('+  found LilyPond-book, but could not extract version number.')
1177     #
1178     checkProg('a Noteedit -> LilyPond converter', ['noteedit --export-lilypond $$i'],
1179         rc_entry = [ r'\converter noteedit   lilypond   "%%"    ""' ])
1180     #
1181     # Currently, lyxpak outputs a gzip compressed tar archive on *nix
1182     # and a zip archive on Windows.
1183     # So, we configure the appropriate version according to the platform.
1184     cmd = r'\converter lyx %s "python -tt $$s/scripts/lyxpak.py $$r/$$f" ""'
1185     if os.name == 'nt':
1186         addToRC(r'\Format lyxzip     zip    "LyX Archive (zip)"     "" "" ""  "document,menu=export"    ""')
1187         addToRC(cmd % "lyxzip")
1188     else:
1189         addToRC(r'\Format lyxgz      gz     "LyX Archive (tar.gz)"  "" "" ""  "document,menu=export"    ""')
1190         addToRC(cmd % "lyxgz")
1191
1192     #
1193     # FIXME: no rc_entry? comment it out
1194     # checkProg('Image converter', ['convert $$i $$o'])
1195     #
1196     # Entries that do not need checkProg
1197     addToRC(r'''
1198 \converter csv        lyx        "python -tt $$s/scripts/csv2lyx.py $$i $$o"    ""
1199 \converter docbook    docbook-xml "cp $$i $$o"  "xml"
1200 \converter fen        asciichess "python -tt $$s/scripts/fen2ascii.py $$i $$o"  ""
1201 \converter lyx        lyx13x     "python -tt $$s/lyx2lyx/lyx2lyx -V 1.3 -o $$o $$i"     ""
1202 \converter lyx        lyx14x     "python -tt $$s/lyx2lyx/lyx2lyx -V 1.4 -o $$o $$i"     ""
1203 \converter lyx        lyx15x     "python -tt $$s/lyx2lyx/lyx2lyx -V 1.5 -o $$o $$i"     ""
1204 \converter lyx        lyx16x     "python -tt $$s/lyx2lyx/lyx2lyx -V 1.6 -o $$o $$i"     ""
1205 \converter lyx        lyx20x     "python -tt $$s/lyx2lyx/lyx2lyx -V 2.0 -o $$o $$i"     ""
1206 \converter lyx        lyx21x     "python -tt $$s/lyx2lyx/lyx2lyx -V 2.1 -o $$o $$i"     ""
1207 \converter lyx        lyx22x     "python -tt $$s/lyx2lyx/lyx2lyx -V 2.2 -o $$o $$i"     ""
1208 \converter lyx        clyx       "python -tt $$s/lyx2lyx/lyx2lyx -V 1.4 -o $$o -c big5   $$i"   ""
1209 \converter lyx        jlyx       "python -tt $$s/lyx2lyx/lyx2lyx -V 1.4 -o $$o -c euc_jp $$i"   ""
1210 \converter lyx        klyx       "python -tt $$s/lyx2lyx/lyx2lyx -V 1.4 -o $$o -c euc_kr $$i"   ""
1211 \converter clyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c big5   -o $$o $$i"  ""
1212 \converter jlyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c euc_jp -o $$o $$i"  ""
1213 \converter klyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c euc_kr -o $$o $$i"  ""
1214 \converter lyxpreview png        "python -tt $$s/scripts/lyxpreview2bitmap.py --png"    ""
1215 \converter lyxpreview ppm        "python -tt $$s/scripts/lyxpreview2bitmap.py --ppm"    ""
1216 ''')
1217
1218
1219 def checkDocBook():
1220     ''' Check docbook '''
1221     path, DOCBOOK = checkProg('SGML-tools 2.x (DocBook), db2x scripts or xsltproc', ['sgmltools', 'db2dvi', 'xsltproc'],
1222         rc_entry = [
1223             r'''\converter docbook    dvi        "sgmltools -b dvi $$i" ""
1224 \converter docbook    html       "sgmltools -b html $$i"        ""
1225 \converter docbook    ps         "sgmltools -b ps $$i"  ""''',
1226             r'''\converter docbook    dvi        "db2dvi $$i"   ""
1227 \converter docbook    html       "db2html $$i"  ""''',
1228             r'''\converter docbook    dvi        ""     ""
1229 \converter docbook    html       "" ""''',
1230             r'''\converter docbook    dvi        ""     ""
1231 \converter docbook    html       ""     ""'''])
1232     #
1233     if DOCBOOK:
1234         return ('yes', 'true', '\\def\\hasdocbook{yes}')
1235     else:
1236         return ('no', 'false', '')
1237
1238
1239 def checkOtherEntries():
1240     ''' entries other than Format and Converter '''
1241     checkProg('ChkTeX', ['chktex -n1 -n3 -n6 -n9 -n22 -n25 -n30 -n38'],
1242         rc_entry = [ r'\chktex_command "%%"' ])
1243     checkProgAlternatives('BibTeX or alternative programs',
1244         ['bibtex', 'bibtex8', 'biber'],
1245         rc_entry = [ r'\bibtex_command "automatic"' ],
1246         alt_rc_entry = [ r'\bibtex_alternatives "%%"' ])
1247     checkProgAlternatives('a specific Japanese BibTeX variant',
1248         ['pbibtex', 'upbibtex', 'jbibtex', 'bibtex', 'biber'],
1249         rc_entry = [ r'\jbibtex_command "automatic"' ],
1250         alt_rc_entry = [ r'\jbibtex_alternatives "%%"' ])
1251     checkProgAlternatives('available index processors',
1252         ['texindy', 'makeindex -c -q', 'xindy'],
1253         rc_entry = [ r'\index_command "%%"' ],
1254         alt_rc_entry = [ r'\index_alternatives "%%"' ])
1255     checkProg('an index processor appropriate to Japanese',
1256         ['mendex -c -q', 'jmakeindex -c -q', 'makeindex -c -q'],
1257         rc_entry = [ r'\jindex_command "%%"' ])
1258     checkProg('the splitindex processor', ['splitindex.pl', 'splitindex',
1259         'splitindex.class'], rc_entry = [ r'\splitindex_command "%%"' ])
1260     checkProg('a nomenclature processor', ['makeindex'],
1261         rc_entry = [ r'\nomencl_command "makeindex -s nomencl.ist"' ])
1262     checkProg('a python-pygments driver command', ['pygmentize'],
1263         rc_entry = [ r'\pygmentize_command "%%"' ])
1264     ## FIXME: OCTAVE is not used anywhere
1265     # path, OCTAVE = checkProg('Octave', ['octave'])
1266     ## FIXME: MAPLE is not used anywhere
1267     # path, MAPLE = checkProg('Maple', ['maple'])
1268     # Add the rest of the entries (no checkProg is required)
1269     addToRC(r'''\copier    fig        "python -tt $$s/scripts/fig_copy.py $$i $$o"
1270 \copier    pstex      "python -tt $$s/scripts/tex_copy.py $$i $$o $$l"
1271 \copier    pdftex     "python -tt $$s/scripts/tex_copy.py $$i $$o $$l"
1272 \copier    program    "python -tt $$s/scripts/ext_copy.py $$i $$o"
1273 ''')
1274
1275 def _checkForClassExtension(x):
1276     '''if the extension for a latex class is not
1277         provided, add .cls to the classname'''
1278     if not '.' in x:
1279         return x.strip() + '.cls'
1280     else:
1281         return x.strip()
1282
1283 def processLayoutFile(file, bool_docbook):
1284     ''' process layout file and get a line of result
1285
1286         Declare lines look like this:
1287
1288         \DeclareLaTeXClass[<requirements>]{<description>}
1289
1290         Optionally, a \DeclareCategory line follows:
1291
1292         \DeclareCategory{<category>}
1293
1294         So for example (article.layout, scrbook.layout, svjog.layout)
1295
1296         \DeclareLaTeXClass{article}
1297         \DeclareCategory{Articles}
1298
1299         \DeclareLaTeXClass[scrbook]{book (koma-script)}
1300         \DeclareCategory{Books}
1301
1302         \DeclareLaTeXClass[svjour,svjog.clo]{article (Springer - svjour/jog)}
1303
1304         we'd expect this output:
1305
1306         "article" "article" "article" "false" "article.cls" "Articles"
1307         "scrbook" "scrbook" "book (koma-script)" "false" "scrbook.cls" "Books"
1308         "svjog" "svjour" "article (Springer - svjour/jog)" "false" "svjour.cls,svjog.clo" ""
1309     '''
1310     classname = file.split(os.sep)[-1].split('.')[0]
1311     # return ('LaTeX', '[a,b]', 'a', ',b,c', 'article') for \DeclareLaTeXClass[a,b,c]{article}
1312     p = re.compile('\s*#\s*\\\\Declare(LaTeX|DocBook)Class\s*(\[([^,]*)(,.*)*\])*\s*{(.*)}\s*$')
1313     q = re.compile('\s*#\s*\\\\DeclareCategory{(.*)}\s*$')
1314     classdeclaration = ""
1315     categorydeclaration = '""'
1316     for line in open(file, 'r', encoding='utf8').readlines():
1317         res = p.match(line)
1318         qres = q.match(line)
1319         if res != None:
1320             (classtype, optAll, opt, opt1, desc) = res.groups()
1321             avai = {'LaTeX': 'false', 'DocBook': bool_docbook}[classtype]
1322             if opt == None:
1323                 opt = classname
1324                 prereq_latex = _checkForClassExtension(classname)
1325             else:
1326                 prereq_list = optAll[1:-1].split(',')
1327                 prereq_list = list(map(_checkForClassExtension, prereq_list))
1328                 prereq_latex = ','.join(prereq_list)
1329             prereq_docbook = {'true':'', 'false':'docbook'}[bool_docbook]
1330             prereq = {'LaTeX':prereq_latex, 'DocBook':prereq_docbook}[classtype]
1331             classdeclaration = ('"%s" "%s" "%s" "%s" "%s"'
1332                                % (classname, opt, desc, avai, prereq))
1333             if categorydeclaration != '""':
1334                 return classdeclaration + " " + categorydeclaration
1335         if qres != None:
1336              categorydeclaration = '"%s"' % (qres.groups()[0])
1337              if classdeclaration:
1338                  return classdeclaration + " " + categorydeclaration
1339     if classdeclaration:
1340         return classdeclaration + " " + categorydeclaration
1341     logger.warning("Layout file " + file + " has no \DeclareXXClass line. ")
1342     return ""
1343
1344
1345 def checkLatexConfig(check_config, bool_docbook):
1346     ''' Explore the LaTeX configuration
1347         Return None (will be passed to sys.exit()) for success.
1348     '''
1349     msg = 'checking LaTeX configuration... '
1350     # if --without-latex-config is forced, or if there is no previous
1351     # version of textclass.lst, re-generate a default file.
1352     if not os.path.isfile('textclass.lst') or not check_config:
1353         # remove the files only if we want to regenerate
1354         removeFiles(['textclass.lst', 'packages.lst'])
1355         #
1356         # Then, generate a default textclass.lst. In case configure.py
1357         # fails, we still have something to start lyx.
1358         logger.info(msg + ' default values')
1359         logger.info('+checking list of textclasses... ')
1360         tx = open('textclass.lst', 'w', encoding='utf8')
1361         tx.write('''
1362 # This file declares layouts and their associated definition files
1363 # (include dir. relative to the place where this file is).
1364 # It contains only default values, since chkconfig.ltx could not be run
1365 # for some reason. Run ./configure.py if you need to update it after a
1366 # configuration change.
1367 ''')
1368         # build the list of available layout files and convert it to commands
1369         # for chkconfig.ltx
1370         foundClasses = []
1371         for file in (glob.glob(os.path.join('layouts', '*.layout'))
1372                      + glob.glob(os.path.join(srcdir, 'layouts', '*.layout'))):
1373             # valid file?
1374             if not os.path.isfile(file):
1375                 continue
1376             # get stuff between /xxxx.layout .
1377             classname = file.split(os.sep)[-1].split('.')[0]
1378             #  tr ' -' '__'`
1379             cleanclass = classname.replace(' ', '_').replace('-', '_')
1380             # make sure the same class is not considered twice
1381             if foundClasses.count(cleanclass) == 0: # not found before
1382                 foundClasses.append(cleanclass)
1383                 retval = processLayoutFile(file, bool_docbook)
1384                 if retval:
1385                     tx.write(retval + os.linesep)
1386         tx.close()
1387         logger.info('\tdone')
1388     if not os.path.isfile('packages.lst') or not check_config:
1389         logger.info('+generating default list of packages... ')
1390         removeFiles(['packages.lst'])
1391         tx = open('packages.lst', 'w', encoding='utf8')
1392         tx.close()
1393         logger.info('\tdone')
1394     if not check_config:
1395         return None
1396     # the following will generate textclass.lst.tmp, and packages.lst.tmp
1397     logger.info(msg + '\tauto')
1398     removeFiles(['chkconfig.classes', 'chkconfig.vars', 'chklayouts.tex',
1399         'wrap_chkconfig.ltx'])
1400     rmcopy = False
1401     if not os.path.isfile( 'chkconfig.ltx' ):
1402         shutil.copyfile( os.path.join(srcdir, 'chkconfig.ltx'), 'chkconfig.ltx' )
1403         rmcopy = True
1404     writeToFile('wrap_chkconfig.ltx', '%s\n\\input{chkconfig.ltx}\n' % docbook_cmd)
1405     # Construct the list of classes to test for.
1406     # build the list of available layout files and convert it to commands
1407     # for chkconfig.ltx
1408     declare = re.compile('\\s*#\\s*\\\\Declare(LaTeX|DocBook)Class\\s*(\[([^,]*)(,.*)*\])*\\s*{(.*)}\\s*$')
1409     category = re.compile('\\s*#\\s*\\\\DeclareCategory{(.*)}\\s*$')
1410     empty = re.compile('\\s*$')
1411     testclasses = list()
1412     for file in (glob.glob( os.path.join('layouts', '*.layout') )
1413                  + glob.glob( os.path.join(srcdir, 'layouts', '*.layout' ) ) ):
1414         nodeclaration = False
1415         if not os.path.isfile(file):
1416             continue
1417         classname = file.split(os.sep)[-1].split('.')[0]
1418         decline = ""
1419         catline = ""
1420         for line in open(file, 'r', encoding='utf8').readlines():
1421             if not empty.match(line) and line[0] != '#'[0]:
1422                 if decline == "":
1423                     logger.warning("Failed to find valid \Declare line "
1424                         "for layout file `%s'.\n\t=> Skipping this file!" % file)
1425                     nodeclaration = True
1426                 # A class, but no category declaration. Just break.
1427                 break
1428             if declare.match(line) != None:
1429                 decline = "\\TestDocClass{%s}{%s}" % (classname, line[1:].strip())
1430                 testclasses.append(decline)
1431             elif category.match(line) != None:
1432                 catline = ("\\DeclareCategory{%s}{%s}"
1433                            % (classname, category.match(line).groups()[0]))
1434                 testclasses.append(catline)
1435             if catline == "" or decline == "":
1436                 continue
1437             break
1438         if nodeclaration:
1439             continue
1440     testclasses.sort()
1441     cl = open('chklayouts.tex', 'w', encoding='utf8')
1442     for line in testclasses:
1443         cl.write(line + '\n')
1444     cl.close()
1445     #
1446     # we have chklayouts.tex, then process it
1447     latex_out = cmdOutput(LATEX + ' wrap_chkconfig.ltx', True)
1448     while True:
1449         line = latex_out.readline()
1450         if not line:
1451             break;
1452         if re.match('^\+', line):
1453             logger.info(line.strip())
1454     # if the command succeeds, None will be returned
1455     ret = latex_out.close()
1456     #
1457     # remove the copied file
1458     if rmcopy:
1459         removeFiles( [ 'chkconfig.ltx' ] )
1460     #
1461     # currently, values in chkconfig are only used to set
1462     # \font_encoding
1463     values = {}
1464     for line in open('chkconfig.vars').readlines():
1465         key, val = re.sub('-', '_', line).split('=')
1466         val = val.strip()
1467         values[key] = val.strip("'")
1468     # chk_fontenc may not exist
1469     try:
1470         addToRC(r'\font_encoding "%s"' % values["chk_fontenc"])
1471     except:
1472         pass
1473     # if configure successed, move textclass.lst.tmp to textclass.lst
1474     # and packages.lst.tmp to packages.lst
1475     if (os.path.isfile('textclass.lst.tmp')
1476           and len(open('textclass.lst.tmp', encoding='utf8').read()) > 0
1477         and os.path.isfile('packages.lst.tmp')
1478           and len(open('packages.lst.tmp', encoding='utf8').read()) > 0):
1479         shutil.move('textclass.lst.tmp', 'textclass.lst')
1480         shutil.move('packages.lst.tmp', 'packages.lst')
1481     return ret
1482
1483
1484 def checkModulesConfig():
1485   removeFiles(['lyxmodules.lst', 'chkmodules.tex'])
1486
1487   logger.info('+checking list of modules... ')
1488   tx = open('lyxmodules.lst', 'w', encoding='utf8')
1489   tx.write('''## This file declares modules and their associated definition files.
1490 ## It has been automatically generated by configure
1491 ## Use "Options/Reconfigure" if you need to update it after a
1492 ## configuration change.
1493 ## "ModuleName" "filename" "Description" "Packages" "Requires" "Excludes" "Category"
1494 ''')
1495
1496   # build the list of available modules
1497   seen = []
1498   # note that this searches the local directory first, then the
1499   # system directory. that way, we pick up the user's version first.
1500   for file in (glob.glob( os.path.join('layouts', '*.module') )
1501                + glob.glob( os.path.join(srcdir, 'layouts', '*.module' ) ) ):
1502       # valid file?
1503       logger.info(file)
1504       if not os.path.isfile(file):
1505           continue
1506
1507       filename = file.split(os.sep)[-1]
1508       filename = filename[:-7]
1509       if seen.count(filename):
1510           continue
1511
1512       seen.append(filename)
1513       retval = processModuleFile(file, filename, bool_docbook)
1514       if retval:
1515           tx.write(retval)
1516   tx.close()
1517   logger.info('\tdone')
1518
1519
1520 def processModuleFile(file, filename, bool_docbook):
1521     ''' process module file and get a line of result
1522
1523         The top of a module file should look like this:
1524           #\DeclareLyXModule[LaTeX Packages]{ModuleName}
1525           #DescriptionBegin
1526           #...body of description...
1527           #DescriptionEnd
1528           #Requires: [list of required modules]
1529           #Excludes: [list of excluded modules]
1530           #Category: [category name]
1531         The last three lines are optional (though do give a category).
1532         We expect output:
1533           "ModuleName" "filename" "Description" "Packages" "Requires" "Excludes" "Category"
1534     '''
1535     remods = re.compile('\s*#\s*\\\\DeclareLyXModule\s*(?:\[([^]]*?)\])?{(.*)}')
1536     rereqs = re.compile('\s*#+\s*Requires: (.*)')
1537     reexcs = re.compile('\s*#+\s*Excludes: (.*)')
1538     recaty = re.compile('\\s*#\\s*\\\\DeclareCategory{(.*)}\\s*$')
1539     redbeg = re.compile('\s*#+\s*DescriptionBegin\s*$')
1540     redend = re.compile('\s*#+\s*DescriptionEnd\s*$')
1541
1542     modname = desc = pkgs = req = excl = catgy = ""
1543     readingDescription = False
1544     descLines = []
1545
1546     for line in open(file, 'r', encoding='utf8').readlines():
1547       if readingDescription:
1548         res = redend.search(line)
1549         if res != None:
1550           readingDescription = False
1551           desc = " ".join(descLines)
1552           # Escape quotes.
1553           desc = desc.replace('"', '\\"')
1554           continue
1555         descLines.append(line[1:].strip())
1556         continue
1557       res = redbeg.search(line)
1558       if res != None:
1559         readingDescription = True
1560         continue
1561       res = remods.search(line)
1562       if res != None:
1563           (pkgs, modname) = res.groups()
1564           if pkgs == None:
1565             pkgs = ""
1566           else:
1567             tmp = [s.strip() for s in pkgs.split(",")]
1568             pkgs = ",".join(tmp)
1569           continue
1570       res = rereqs.search(line)
1571       if res != None:
1572         req = res.group(1)
1573         tmp = [s.strip() for s in req.split("|")]
1574         req = "|".join(tmp)
1575         continue
1576       res = reexcs.search(line)
1577       if res != None:
1578         excl = res.group(1)
1579         tmp = [s.strip() for s in excl.split("|")]
1580         excl = "|".join(tmp)
1581         continue
1582       res = recaty.search(line)
1583       if res != None:
1584         catgy = res.group(1)
1585         continue
1586
1587     if modname == "":
1588       logger.warning("Module file without \DeclareLyXModule line. ")
1589       return ""
1590
1591     if pkgs:
1592         # this module has some latex dependencies:
1593         # append the dependencies to chkmodules.tex,
1594         # which is \input'ed by chkconfig.ltx
1595         testpackages = list()
1596         for pkg in pkgs.split(","):
1597             if "->" in pkg:
1598                 # this is a converter dependency: skip
1599                 continue
1600             if pkg.endswith(".sty"):
1601                 pkg = pkg[:-4]
1602             testpackages.append("\\TestPackage{%s}" % pkg)
1603         cm = open('chkmodules.tex', 'a', encoding='utf8')
1604         for line in testpackages:
1605             cm.write(line + '\n')
1606         cm.close()
1607
1608     local = "true"
1609     if (file.startswith(srcdir)):
1610         local = "false"
1611     return ('"%s" "%s" "%s" "%s" "%s" "%s" "%s" "%s"\n'
1612             % (modname, filename, desc, pkgs, req, excl, catgy, local))
1613
1614
1615 def checkCiteEnginesConfig():
1616   removeFiles(['lyxciteengines.lst', 'chkciteengines.tex'])
1617
1618   logger.info('+checking list of cite engines... ')
1619   tx = open('lyxciteengines.lst', 'w', encoding='utf8')
1620   tx.write('''## This file declares cite engines and their associated definition files.
1621 ## It has been automatically generated by configure
1622 ## Use "Options/Reconfigure" if you need to update it after a
1623 ## configuration change.
1624 ## "CiteEngineName" "filename" "CiteEngineType" "CiteFramework" "DefaultBiblio" "Description" "Packages"
1625 ''')
1626
1627   # build the list of available modules
1628   seen = []
1629   # note that this searches the local directory first, then the
1630   # system directory. that way, we pick up the user's version first.
1631   for file in glob.glob( os.path.join('citeengines', '*.citeengine') ) + \
1632       glob.glob( os.path.join(srcdir, 'citeengines', '*.citeengine' ) ) :
1633       # valid file?
1634       logger.info(file)
1635       if not os.path.isfile(file):
1636           continue
1637
1638       filename = file.split(os.sep)[-1]
1639       filename = filename[:-11]
1640       if seen.count(filename):
1641           continue
1642
1643       seen.append(filename)
1644       retval = processCiteEngineFile(file, filename, bool_docbook)
1645       if retval:
1646           tx.write(retval)
1647   tx.close()
1648   logger.info('\tdone')
1649
1650
1651 def processCiteEngineFile(file, filename, bool_docbook):
1652     ''' process cite engines file and get a line of result
1653
1654         The top of a cite engine file should look like this:
1655           #\DeclareLyXCiteEngine[LaTeX Packages]{CiteEngineName}
1656           #DescriptionBegin
1657           #...body of description...
1658           #DescriptionEnd
1659         We expect output:
1660           "CiteEngineName" "filename" "CiteEngineType" "CiteFramework" "DefaultBiblio" "Description" "Packages"
1661     '''
1662     remods = re.compile('\s*#\s*\\\\DeclareLyXCiteEngine\s*(?:\[([^]]*?)\])?{(.*)}')
1663     redbeg = re.compile('\s*#+\s*DescriptionBegin\s*$')
1664     redend = re.compile('\s*#+\s*DescriptionEnd\s*$')
1665     recet = re.compile('\s*CiteEngineType\s*(.*)')
1666     redb = re.compile('\s*DefaultBiblio\s*(.*)')
1667     resfm = re.compile('\s*CiteFramework\s*(.*)')
1668
1669     modname = desc = pkgs = cet = db = cfm = ""
1670     readingDescription = False
1671     descLines = []
1672
1673     for line in open(file, 'r', encoding='utf8').readlines():
1674       if readingDescription:
1675         res = redend.search(line)
1676         if res != None:
1677           readingDescription = False
1678           desc = " ".join(descLines)
1679           # Escape quotes.
1680           desc = desc.replace('"', '\\"')
1681           continue
1682         descLines.append(line[1:].strip())
1683         continue
1684       res = redbeg.search(line)
1685       if res != None:
1686         readingDescription = True
1687         continue
1688       res = remods.search(line)
1689       if res != None:
1690           (pkgs, modname) = res.groups()
1691           if pkgs == None:
1692             pkgs = ""
1693           else:
1694             tmp = [s.strip() for s in pkgs.split(",")]
1695             pkgs = ",".join(tmp)
1696           continue
1697       res = recet.search(line)
1698       if res != None:
1699         cet = res.group(1)
1700         continue
1701       res = redb.search(line)
1702       if res != None:
1703         db = res.group(1)
1704         continue
1705       res = resfm.search(line)
1706       if res != None:
1707         cfm = res.group(1)
1708         continue
1709
1710     if modname == "":
1711       logger.warning("Cite Engine File file without \DeclareLyXCiteEngine line. ")
1712       return ""
1713
1714     if pkgs:
1715         # this cite engine has some latex dependencies:
1716         # append the dependencies to chkciteengines.tex,
1717         # which is \input'ed by chkconfig.ltx
1718         testpackages = list()
1719         for pkg in pkgs.split(","):
1720             if "->" in pkg:
1721                 # this is a converter dependency: skip
1722                 continue
1723             if pkg.endswith(".sty"):
1724                 pkg = pkg[:-4]
1725             testpackages.append("\\TestPackage{%s}" % pkg)
1726         cm = open('chkciteengines.tex', 'a', encoding='utf8')
1727         for line in testpackages:
1728             cm.write(line + '\n')
1729         cm.close()
1730
1731     return ('"%s" "%s" "%s" "%s" "%s" "%s" "%s"\n'
1732             % (modname, filename, cet, cfm, db, desc, pkgs))
1733
1734
1735 def checkXTemplates():
1736   removeFiles(['xtemplates.lst'])
1737
1738   logger.info('+checking list of external templates... ')
1739   tx = open('xtemplates.lst', 'w', encoding='utf8')
1740   tx.write('''## This file lists external templates.
1741 ## It has been automatically generated by configure
1742 ## Use "Options/Reconfigure" if you need to update it after a
1743 ## configuration change.
1744 ''')
1745
1746   # build the list of available templates
1747   seen = []
1748   # note that this searches the local directory first, then the
1749   # system directory. that way, we pick up the user's version first.
1750   for file in glob.glob( os.path.join('xtemplates', '*.xtemplate') ) + \
1751       glob.glob( os.path.join(srcdir, 'xtemplates', '*.xtemplate' ) ) :
1752       # valid file?
1753       logger.info(file)
1754       if not os.path.isfile(file):
1755           continue
1756
1757       filename = file.split(os.sep)[-1]
1758       if seen.count(filename):
1759           continue
1760
1761       seen.append(filename)
1762       if filename:
1763           tx.write(filename + "\n")
1764   tx.close()
1765   logger.info('\tdone')
1766
1767
1768 def checkTeXAllowSpaces():
1769     ''' Let's check whether spaces are allowed in TeX file names '''
1770     tex_allows_spaces = 'false'
1771     if lyx_check_config:
1772         msg = "Checking whether TeX allows spaces in file names... "
1773         writeToFile('a b.tex', r'\message{working^^J}' )
1774         if LATEX:
1775             if os.name == 'nt' or sys.platform == 'cygwin':
1776                 latex_out = cmdOutput(LATEX + r""" "\nonstopmode\input{\"a b\"}\makeatletter\@@end" """)
1777             else:
1778                 latex_out = cmdOutput(LATEX + r""" '\nonstopmode\input{"a b"}\makeatletter\@@end' """)
1779         else:
1780             latex_out = ''
1781         if 'working' in latex_out:
1782             logger.info(msg + 'yes')
1783             tex_allows_spaces = 'true'
1784         else:
1785             logger.info(msg + 'no')
1786             tex_allows_spaces = 'false'
1787         addToRC(r'\tex_allows_spaces ' + tex_allows_spaces)
1788         removeFiles( [ 'a b.tex', 'a b.log', 'texput.log' ])
1789
1790
1791 def rescanTeXFiles():
1792     ''' Run kpsewhich to update information about TeX files '''
1793     logger.info("+Indexing TeX files... ")
1794     tfscript = os.path.join(srcdir, 'scripts', 'TeXFiles.py')
1795     if not os.path.isfile(tfscript):
1796         logger.error("configure: error: cannot find TeXFiles.py script")
1797         sys.exit(1)
1798     interpreter = sys.executable
1799     if interpreter == '':
1800         interpreter = "python"
1801     tfp = cmdOutput('"%s" -tt "%s"' % (interpreter, tfscript))
1802     logger.info(tfp)
1803     logger.info("\tdone")
1804
1805
1806 def removeTempFiles():
1807     # Final clean-up
1808     if not lyx_keep_temps:
1809         removeFiles(['chkconfig.vars', 'chklatex.ltx', 'chklatex.log',
1810             'chklayouts.tex', 'chkmodules.tex', 'chkciteengines.tex',
1811             'missfont.log', 'wrap_chkconfig.ltx', 'wrap_chkconfig.log'])
1812
1813
1814 if __name__ == '__main__':
1815     lyx_check_config = True
1816     lyx_kpsewhich = True
1817     outfile = 'lyxrc.defaults'
1818     lyxrc_fileformat = 24
1819     rc_entries = ''
1820     lyx_keep_temps = False
1821     version_suffix = ''
1822     lyx_binary_dir = ''
1823     ## Parse the command line
1824     for op in sys.argv[1:]:   # default shell/for list is $*, the options
1825         if op in [ '-help', '--help', '-h' ]:
1826             print('''Usage: configure [options]
1827 Options:
1828     --help                   show this help lines
1829     --keep-temps             keep temporary files (for debug. purposes)
1830     --without-kpsewhich      do not update TeX files information via kpsewhich
1831     --without-latex-config   do not run LaTeX to determine configuration
1832     --with-version-suffix=suffix suffix of binary installed files
1833     --binary-dir=directory   directory of binary installed files
1834 ''')
1835             sys.exit(0)
1836         elif op == '--without-kpsewhich':
1837             lyx_kpsewhich = False
1838         elif op == '--without-latex-config':
1839             lyx_check_config = False
1840         elif op == '--keep-temps':
1841             lyx_keep_temps = True
1842         elif op[0:22] == '--with-version-suffix=':  # never mind if op is not long enough
1843             version_suffix = op[22:]
1844         elif op[0:13] == '--binary-dir=':
1845             lyx_binary_dir = op[13:]
1846         else:
1847             print("Unknown option %s" % op)
1848             sys.exit(1)
1849     #
1850     # check if we run from the right directory
1851     srcdir = os.path.dirname(sys.argv[0])
1852     if srcdir == '':
1853         srcdir = '.'
1854     if not os.path.isfile( os.path.join(srcdir, 'chkconfig.ltx') ):
1855         logger.error("configure: error: cannot find chkconfig.ltx script")
1856         sys.exit(1)
1857     setEnviron()
1858     if sys.platform == 'darwin' and len(version_suffix) > 0:
1859         checkUpgrade()
1860     if os.name == 'nt':
1861         checkUpgradeWin()
1862     createDirectories()
1863     dtl_tools = checkDTLtools()
1864     ## Write the first part of outfile
1865     writeToFile(outfile, '''# This file has been automatically generated by LyX' lib/configure.py
1866 # script. It contains default settings that have been determined by
1867 # examining your system. PLEASE DO NOT MODIFY ANYTHING HERE! If you
1868 # want to customize LyX, use LyX' Preferences dialog or modify directly
1869 # the "preferences" file instead. Any setting in that file will
1870 # override the values given here.
1871
1872 Format %i
1873
1874 ''' % lyxrc_fileformat)
1875     # check latex
1876     LATEX = checkLatex(dtl_tools)
1877     # check java and perl before any checkProg that may require them
1878     java = checkProg('a java interpreter', ['java'])[1]
1879     perl = checkProg('a perl interpreter', ['perl'])[1]
1880     (inkscape_path, inkscape_gui) = os.path.split(checkInkscape())
1881     # On Windows, we need to call the "inkscape.com" wrapper
1882     # for command line purposes. Other OSes do not differentiate.
1883     inkscape_cl = inkscape_gui
1884     if os.name == 'nt':
1885         inkscape_cl = inkscape_gui.replace('.exe', '.com')
1886     # On MacOSX, Inkscape requires full path file arguments. This
1887     # is not needed on Linux and Win and even breaks the latter.
1888     checkFormatEntries(dtl_tools)
1889     checkConverterEntries()
1890     (chk_docbook, bool_docbook, docbook_cmd) = checkDocBook()
1891     checkTeXAllowSpaces()
1892     windows_style_tex_paths = checkTeXPaths()
1893     if windows_style_tex_paths:
1894         addToRC(r'\tex_expects_windows_paths %s' % windows_style_tex_paths)
1895     checkOtherEntries()
1896     if lyx_kpsewhich:
1897         rescanTeXFiles()
1898     checkModulesConfig()
1899     checkCiteEnginesConfig()
1900     checkXTemplates()
1901     # --without-latex-config can disable lyx_check_config
1902     ret = checkLatexConfig(lyx_check_config and LATEX, bool_docbook)
1903     removeTempFiles()
1904     # The return error code can be 256. Because most systems expect an error code
1905     # in the range 0-127, 256 can be interpretted as 'success'. Because we expect
1906     # a None for success, 'ret is not None' is used to exit.
1907     sys.exit(ret is not None)