2 # -*- coding: utf-8 -*-
5 # This file is part of LyX, the document processor.
6 # Licence details can be found in the file COPYING.
8 # author Enrico Forestieri
10 # Full author contact details are available in file CREDITS
12 # This script creates a tar or zip archive with a lyx file and all included
13 # files (graphics and so on). A zip archive is created only if tar is not
14 # found in the path. The tar archive is then compressed with gzip or bzip2.
16 import os
, re
, string
, sys
19 # Replace with the actual path to the 1.5.x or 1.6.x lyx2lyx.
20 # If left undefined and the LyX executable is in the path, the script will
21 # try to locate lyx2lyx by querying LyX about the system dir.
23 # lyx2lyx = /usr/share/lyx/lyx2lyx/lyx2lyx
26 # Pre-compiled regular expressions.
27 re_lyxfile
= re
.compile("\.lyx$")
28 re_input
= re
.compile(r
'^(.*)\\(input|include){(\s*)(\S+)(\s*)}.*$')
29 re_package
= re
.compile(r
'^(.*)\\(usepackage){(\s*)(\S+)(\s*)}.*$')
30 re_class
= re
.compile(r
'^(\\)(textclass)(\s+)(\S+)$')
31 re_norecur
= re
.compile(r
'^(.*)\\(verbatiminput|lstinputlisting|includegraphics\[*.*\]*){(\s*)(\S+)(\s*)}.*$')
32 re_filename
= re
.compile(r
'^(\s*)(filename)(\s+)(\S+)$')
33 re_options
= re
.compile(r
'^(\s*)options(\s+)(\S+)$')
34 re_bibfiles
= re
.compile(r
'^(\s*)bibfiles(\s+)(\S+)$')
38 return "Usage: %s file.lyx [output_dir]\n" % prog_name
42 sys
.stderr
.write(message
+ '\n')
47 handle
= os
.popen(cmd
, 'r')
48 cmd_stdout
= handle
.read()
49 cmd_status
= handle
.close()
50 return cmd_status
, cmd_stdout
53 def find_exe(candidates
, extlist
, path
):
54 for prog
in candidates
:
55 for directory
in path
:
57 full_path
= os
.path
.join(directory
, prog
+ ext
)
58 if os
.access(full_path
, os
.X_OK
):
59 return prog
, full_path
64 " Resolve symlinks and returns the absolute normalized name."
65 newname
= os
.path
.normpath(os
.path
.abspath(name
))
67 newname
= os
.path
.realpath(newname
)
71 def gather_files(curfile
, incfiles
):
72 " Recursively gather files."
73 curdir
= os
.path
.dirname(abspath(curfile
))
74 is_lyxfile
= re_lyxfile
.search(curfile
)
76 lyx2lyx_cmd
= 'python "%s" "%s"' % (lyx2lyx
, curfile
)
77 l2l_status
, l2l_stdout
= run_cmd(lyx2lyx_cmd
)
78 if l2l_status
!= None:
79 error('%s failed to convert "%s"' % (lyx2lyx
, curfile
))
80 lines
= l2l_stdout
.splitlines()
82 input = open(curfile
, 'rU')
83 lines
= input.readlines()
91 match
= re_filename
.match(lines
[i
])
93 match
= re_input
.match(lines
[i
])
95 match
= re_package
.match(lines
[i
])
98 match
= re_class
.match(lines
[i
])
101 match
= re_norecur
.match(lines
[i
])
102 extlist
= ['', '.eps', '.pdf', '.png', '.jpg']
105 file = match
.group(4).strip('"')
106 if not os
.path
.isabs(file):
107 file = os
.path
.join(curdir
, file)
110 if os
.path
.exists(file + ext
):
115 incfiles
.append(abspath(file))
117 gather_files(file, incfiles
)
125 # Gather bibtex *.bst files.
126 match
= re_options
.match(lines
[i
])
128 file = match
.group(3).strip('"')
129 if not os
.path
.isabs(file):
130 file = os
.path
.join(curdir
, file + '.bst')
131 if os
.path
.exists(file):
132 incfiles
.append(abspath(file))
136 # Gather bibtex *.bib files.
137 match
= re_bibfiles
.match(lines
[i
])
139 bibfiles
= match
.group(3).strip('"').split(',')
141 while j
< len(bibfiles
):
142 if os
.path
.isabs(bibfiles
[j
]):
145 file = os
.path
.join(curdir
, bibfiles
[j
] + '.bib')
146 if os
.path
.exists(file):
147 incfiles
.append(abspath(file))
159 if len(argv
) >= 2 and len(argv
) <= 3:
161 if not os
.path
.exists(lyxfile
):
162 error('File "%s" not found.' % lyxfile
)
164 # Check that it actually is a LyX document
165 input = open(lyxfile
, 'rU')
166 line
= input.readline()
168 if not (line
and line
.startswith('#LyX')):
169 error('File "%s" is not a LyX document.' % lyxfile
)
171 # Either tar or zip must be available
173 if os
.environ
.has_key("PATHEXT"):
174 extlist
= extlist
+ os
.environ
["PATHEXT"].split(os
.pathsep
)
175 path
= string
.split(os
.environ
["PATH"], os
.pathsep
)
176 archiver
, full_path
= find_exe(["tar", "zip"], extlist
, path
)
178 if archiver
== "tar":
180 ar_name
= re_lyxfile
.sub(".tar", abspath(lyxfile
))
181 # Archive will be compressed if either gzip or bzip2 are available
182 compress
, full_path
= find_exe(["gzip", "bzip2"], extlist
, path
)
183 if compress
== "gzip":
185 elif compress
== "bzip2":
187 elif archiver
== "zip":
189 ar_name
= re_lyxfile
.sub(".zip", abspath(lyxfile
))
192 error("Unable to find neither tar nor zip.")
196 if not os
.path
.isdir(outdir
):
197 error('Error: "%s" is not a directory.' % outdir
)
198 ar_name
= os
.path
.join(abspath(outdir
), os
.path
.basename(ar_name
))
200 error(usage(argv
[0]))
202 # Try to find the location of the lyx2lyx script
205 lyx_exe
, full_path
= find_exe(["lyxc", "lyx"], extlist
, path
)
207 error('Cannot find the LyX executable in the path.')
209 cmd_status
, cmd_stdout
= run_cmd("%s -version 2>&1" % lyx_exe
)
210 if cmd_status
!= None:
211 error('Cannot query LyX about the lyx2lyx script.')
212 re_msvc
= re
.compile(r
'^(\s*)(Host type:)(\s+)(win32)$')
213 re_sysdir
= re
.compile(r
'^(\s*)(LyX files dir:)(\s+)(\S+)$')
214 lines
= cmd_stdout
.splitlines()
216 match
= re_msvc
.match(line
)
218 # The LyX executable was built with MSVC, so the
219 # "LyX files dir:" line is unusable
220 basedir
= os
.path
.dirname(os
.path
.dirname(full_path
))
221 lyx2lyx
= os
.path
.join(basedir
, 'Resources', 'lyx2lyx', 'lyx2lyx')
223 match
= re_sysdir
.match(line
)
225 lyx2lyx
= os
.path
.join(match
.group(4), 'lyx2lyx', 'lyx2lyx')
227 if not os
.access(lyx2lyx
, os
.X_OK
):
228 error('Unable to find the lyx2lyx script.')
230 # Initialize the list with the specified LyX file and recursively
231 # gather all required files (also from child documents).
232 incfiles
= [abspath(lyxfile
)]
233 gather_files(lyxfile
, incfiles
)
235 # Find the topmost dir common to all files
236 if len(incfiles
) > 1:
237 topdir
= os
.path
.commonprefix(incfiles
)
239 topdir
= os
.path
.dirname(incfiles
[0]) + os
.path
.sep
241 # Remove the prefix common to all paths in the list
243 while i
< len(incfiles
):
244 incfiles
[i
] = string
.replace(incfiles
[i
], topdir
, '')
247 # Remove duplicates and sort the list
248 incfiles
= list(Set(incfiles
))
251 # Build the archive command
252 ar_cmd
= '%s "%s"' % (ar_cmd
, ar_name
)
253 for file in incfiles
:
255 ar_cmd
= ar_cmd
+ ' "' + file + '"'
260 cmd_status
, cmd_stdout
= run_cmd(ar_cmd
)
261 if cmd_status
!= None:
262 error('Failed to create LyX archive "%s"' % ar_name
)
264 # If possible, compress the archive
266 compress_cmd
= '%s "%s"' % (compress
, ar_name
)
267 cmd_status
, cmd_stdout
= run_cmd(compress_cmd
)
268 if cmd_status
!= None:
269 error('Failed to compress LyX archive "%s"' % ar_name
)
270 ar_name
= ar_name
+ ext
272 print 'LyX archive "%s" created successfully.' % ar_name
276 if __name__
== "__main__":