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