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