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