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