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