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