/usr/share/acl2-8.0dfsg/books/oslib/copy-logic.lisp is in acl2-books-source 8.0dfsg-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 | ; OSLIB -- Operating System Utilities
; Copyright (C) 2013-2014 Centaur Technology
;
; Contact:
; Centaur Technology Formal Verification Group
; 7600-C N. Capital of Texas Highway, Suite 300, Austin, TX 78731, USA.
; http://www.centtech.com/
;
; License: (An MIT/X11-style license)
;
; Permission is hereby granted, free of charge, to any person obtaining a
; copy of this software and associated documentation files (the "Software"),
; to deal in the Software without restriction, including without limitation
; the rights to use, copy, modify, merge, publish, distribute, sublicense,
; and/or sell copies of the Software, and to permit persons to whom the
; Software is furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in
; all copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
; FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
; DEALINGS IN THE SOFTWARE.
;
; Original author: Jared Davis <jared@centtech.com>
(in-package "OSLIB")
(include-book "dirname-logic")
(include-book "ls-logic")
(include-book "file-types-logic")
(include-book "mkdir-logic")
(include-book "std/util/defines" :dir :system)
(include-book "std/basic/two-nats-measure" :dir :system)
(set-state-ok t)
(define copy-file
:parents (copy)
:short "Copy a single file. (low level)"
((from stringp "Path to copy from, should be an ordinary file.")
(to stringp "Path to copy to, should <b>not</b> be a directory.")
&key
((overwrite booleanp "Should we overwrite @('to'), if it exists?") 'nil)
(state 'state))
:returns
(mv (error "NIL on success, or an error @(see msg) on failure.")
(state state-p1 :hyp (force (state-p1 state))))
(declare (ignorable from to overwrite))
:long "<p>This is a low level function for copying files. See @(see copy)
for a higher-level alternative that can, e.g., copy directories.</p>
<p>In the logic this function reads from the ACL2 oracle. In the execution we
use raw Lisp functions to attempt to copy the indicated file. This can fail
for many reasons, e.g., @('from') might not exist or we might not have
permission to copy it.</p>
<p>Some typical examples of how to use this command correctly would be:</p>
@({
(copy-file \"original.txt\" \"backup.txt\")
(copy-file \"/home/jared/original.txt\"
\"/home/jared/my-backups/today/original.txt\"
:overwrite t)
})
<p>The following example is <b>not correct and will fail</b> because the
destination is a directory:</p>
@({
(copy-file \"original.txt\" \"/home/jared/\") ;; wrong, fails
})
<p>Probably what was intended is instead:</p>
@({
(copy-file \"original.txt\" ;; correct: specify full
\"/home/jared/original.txt\") ;; destination path
})"
(b* ((- (raise "Raw Lisp definition not installed?"))
((mv ?err val state) (read-acl2-oracle state)))
(mv val state)))
(define copy-file-list
;; Implementation function, do not call directly.
((fromdir stringp "Directory where these files currently live.")
(fromnames string-listp "Names of the files in fromdir to copy.")
(todir stringp "Directory to copy the files to (should already exist).")
&key
((overwrite booleanp) 'nil)
(state 'state))
:returns (mv (error "NIL on success, or an error @(see msg) on failure.")
(state state-p1 :hyp (force (state-p1 state))))
(b* (((when (atom fromnames))
(mv nil state))
(full-from1 (catpath fromdir (car fromnames)))
(full-to1 (catpath todir (car fromnames)))
((mv err state) (copy-file full-from1 full-to1 :overwrite overwrite))
((when err)
(mv err state)))
(copy-file-list fromdir (cdr fromnames) todir :overwrite overwrite)))
(defines copy-recursively
;; Implementation function, do not call directly.
(define copy-directory-recursively
((fromdir stringp "Directory whose contents we want to copy.")
(todir stringp "Full name of target directory to copy to.")
&key
(limit natp "Maximum number of directories to descend into.")
(state 'state))
:returns (mv (error "NIL on success, or an error @(see msg) on failure.")
(state state-p1 :hyp (force (state-p1 state))))
:measure (acl2::two-nats-measure limit 0)
(b* (((when (zp limit))
(mv (msg "~s0: too many levels of recursion while copying ~s1. Looping symlinks?"
__function__ fromdir)
state))
((mv err from-files state) (ls-files fromdir))
((when err)
(mv (msg "~s0: Error listing files in ~s1: ~@2" __function__ fromdir err)
state))
((mv err from-subdirs state) (ls-subdirs fromdir))
((when err)
(mv (msg "~s0: Error listing subdirectories in ~s1: ~@2" __function__ fromdir err)
state))
;; Make sure todir exists or create it
((mv err to-kind state) (file-kind todir))
((when err)
(mv (msg "~s0: Error checking type of ~s1: ~@2" __function__ todir err)
state))
((unless (or (not to-kind)
(eq to-kind :directory)))
(mv (msg "~s0: Expected ~s1 to be a directory but it is a ~s2?" __function__ todir to-kind)
state))
;; Create the target directory if necessary
((mv okp state)
(if (not to-kind)
(mkdir todir)
(mv t state)))
((unless okp)
(mv (msg "~s0: Failed to create directory ~s1." __function__ todir)
state))
;; Copy all the files into the target dir.
((mv files-err state) (copy-file-list fromdir from-files todir))
((when files-err) (mv files-err state))
;; And recursively copy all of the subdirectories.
((mv dirs-err state) (copy-directory-list-recursively fromdir from-subdirs todir
:limit (- limit 1)))
((when dirs-err) (mv dirs-err state)))
;; Copied everything and it all worked. Woohoo.
(mv nil state)))
(define copy-directory-list-recursively
((fromdir stringp "Root directory being copied from.")
(subdirs string-listp "List of immediate subdirectories of fromdir.")
(todir stringp "Target directory to copy to (should have just been created).")
&key
(limit natp)
(state 'state))
:returns (mv (error "NIL on success, or an error @(see msg) on failure.")
(state state-p1 :hyp (force (state-p1 state))))
:measure (acl2::two-nats-measure limit (len subdirs))
(b* (((when (atom subdirs))
;; Done copying everything.
(mv nil state))
((mv err1 state)
(copy-directory-recursively (catpath fromdir (car subdirs))
(catpath todir (car subdirs))
:limit limit))
((when err1)
(mv err1 state)))
(copy-directory-list-recursively fromdir (cdr subdirs) todir :limit limit))))
(define nice-copy-single-file
;; Implementation function, do not call directly.
;; Copy a single file.
((from stringp "Path to copy from (assumed to be a regular file that exists).")
(to stringp "Path to copy to (hasn't yet been examined).")
&key
(overwrite booleanp)
(state 'state))
:returns (mv (error "NIL on success, or an error @(see msg) on failure.")
(state state-p1 :hyp (force (state-p1 state))))
(b* (((mv to-err to-kind state) (file-kind to))
((when to-err) (mv to-err state))
((unless to-kind)
;; Trying to write a file to somewhere that doesn't yet exist. That
;; seems fine.
(copy-file from to :overwrite overwrite))
((when (eq to-kind :regular-file))
(if overwrite
(copy-file from to :overwrite overwrite)
(mv (msg "~s0: destination already exists: ~s1" 'copy to)
state)))
((unless (eq to-kind :directory))
;; Destination exists but isn't a regular file or a directory? It must
;; be some kind of weird thing like a pipe, socket, device. Let's not
;; try to overwrite it.
(mv (msg "~s0: can't overwrite ~s1: ~s2" 'copy to-kind to)
state))
;; Trying to copy a file (hello.txt) into an existing directory (/foo).
;; So the real destination is /foo/hello.txt.
((mv err basename state) (basename from))
((when err)
(mv (msg "~s0: can't figure out base name of file ~s1: ~@2."
'copy from err)
state))
(to (catpath to basename))
((mv to-err to-kind state) (file-kind to))
((when to-err) (mv to-err state))
((unless to-kind)
;; Fine, the real dest doesn't exist yet, not overwriting anything.
(copy-file from to :overwrite overwrite))
((when (eq to-kind :regular-file))
(if overwrite
(copy-file from to :overwrite overwrite)
(mv (msg "~s0: destination already exists: ~s1" 'copy to)
state))))
;; Otherwise we're trying to overwrite something else (a directory, or
;; maybe something tricky like a pipe/socket/etc), which is too scary, so
;; just fail.
(mv (msg "~s0: can't overwrite ~s1: ~s2" 'copy to-kind to) state)))
(define nice-copy-single-directory
;; Implementation function, do not call directly.
;; Copy a whole directory.
;; Assumes recursion is okay.
((from stringp "Path to copy from (assumed to be a directory that exists).")
(to stringp "Path to copy to (hasn't yet been examined).")
&key
(limit natp)
(state 'state))
:returns (mv (error "NIL on success, or an error @(see msg) on failure.")
(state state-p1 :hyp (force (state-p1 state))))
(b* (((mv to-err to-kind state) (file-kind to))
((when to-err) (mv to-err state))
((unless to-kind)
;; Trying to write a place that doesn't exist yet. That's all fine.
(copy-directory-recursively from to :limit limit))
((unless (eq to-kind :directory))
;; Trying to overwrite a regular file or some other kind of fancy
;; file (socket, pipe, device) with a directory? Can't do that.
(mv (msg "~s0: can't overwrite ~s1 with directory ~s2" 'copy to from)
state))
;; Otherwise, trying to copy a directory (foo) into a directory (bar).
;; That's ok, we just want to create bar/foo. To do that we need to
;; know the basename of foo, as in nice-single-copy-file.
((mv err basename state) (basename from))
((when err)
(mv (msg "~s0: can't figure out base name of directory ~s1: ~@2."
'copy from err)
state))
(to (catpath to basename))
((mv to-err to-kind state) (file-kind to))
((when to-err) (mv to-err state))
((unless to-kind)
;; Fine, the real dest doesn't exist
(copy-directory-recursively from to :limit limit)))
;; Otherwise, we're trying to copy foo into bar/foo, but bar/foo already
;; exists. We'll just fail.
(mv (msg "~s0: can't overwrite ~s1 with directory ~s2" 'copy to from)
state)))
(define copy
:parents (oslib)
:short "Copy files or directories, with recoverable errors."
((from stringp "Path to copy from (ordinary file or directory).")
(to stringp "Path to copy to (ordinary file or directory).")
&key
((recursive booleanp "Allow copying directories like @('cp -r')?") 'nil)
((overwrite booleanp "Should we overwrite files/directories that already exist?
Only matters when copying individual files, not directories.") 'nil)
((limit natp "Directly depth recursion limit (in case of symlink loops).
Only matters when copying directories, not files.") '1000)
(state 'state))
:returns
(mv (error "NIL on success, or an error @(see msg) on failure.")
(state state-p1 :hyp (force (state-p1 state))))
:long "<p>This is a nice, higher-level wrapper around the low-level @(see
copy-file) routine, which acts more like the unix @('cp') shell command. It
can (optionally) copy whole directories recursively, and more correctly handles
copying individual files into an existing directory.</p>
<p>Copying files can fail for a variety of reasons. This function attempts to
gracefully catch errors and return them in a form that you can recover from.
See also @(see copy!), an alternative that just causes a hard error if there is
any problem.</p>
<p>Examples:</p>
<dl>
<dt>@('(oslib::copy \"./hello.txt\" \"./hello-copy.txt\")')</dt>
<dd>copies hello.txt to hello-copy.txt</dd>
<dd>\"safe\", won't overwrite hello-copy.txt if it exists</dd>
<dt>@('(oslib::copy \"./hello.txt\" \"./hello-copy.txt\" :overwrite t)')</dt>
<dd>copies hello.txt to hello-copy.txt</dd>
<dd>overwrites hello-copy.txt if it exists</dd>
<dd>won't overwrite directories, pipes, sockets, etc.</dd>
<dt>@('(oslib::copy \"./hello.txt\" \"./foo/\")')</dt>
<dd>copies hello.txt to foo/hello.txt</dd>
<dd>won't overwrite foo/hello.txt if it exists</dd>
<dt>@('(oslib::copy \"./foo/\" \"./bar/\")')</dt>
<dd>Recursively copy the directory @('foo') to...
<ul>
<li>If directory @('bar') exists: @('bar/foo')</li>
<li>Otherwise, @('bar')</li>
</ul></dd>
<dd>Won't overwrite @('./bar/foo') if it already exists.</dd>
<dd>Won't overwrite @('./bar') if it is some non-directory file.</dd>
</dl>"
(b* (((mv from-err from-kind state) (file-kind from)) ;; follows symlinks
((when from-err)
(mv (msg "~s0: can't copy ~s1: ~@2" 'copy from from-err) state))
((unless from-kind)
(mv (msg "~s0: no such file: ~s1" 'copy from) state))
((when (eq from-kind :regular-file))
(nice-copy-single-file from to :overwrite overwrite))
((when (eq from-kind :directory))
(if (not recursive)
(mv (msg "~s0: can't copy directory (in non-recursive mode): ~s1" 'copy from)
state)
(nice-copy-single-directory from to :limit limit))))
;; Let's not try to copy weird files (pipes, sockets, etc.)
(mv (msg "~s0: unsupported file type ~s1: ~s2" 'copy from-kind from)
state)))
(define copy!
:parents (oslib)
:short "Copy files or directories, or cause a hard error."
((from stringp "Path to copy from (ordinary file or directory).")
(to stringp "Path to copy to (ordinary file or directory).")
&key
((recursive booleanp "Allow copying directories like @('cp -r')?") 'nil)
((overwrite booleanp "Should we overwrite files/directories that already exist?
Only matters when copying individual files, not directories.") 'nil)
((limit natp "Directly depth recursion limit (in case of symlink loops).
Only matters when copying directories, not files.") '1000)
(state 'state))
:returns
(state state-p1 :hyp (force (state-p1 state)))
:long "<p>This is identical to @(see copy) except that we raise a hard error
if anything goes wrong.</p>"
(b* (((mv err state) (copy from to
:recursive recursive
:overwrite overwrite
:limit limit))
((when err)
(raise "~@0" err)
state))
state))
|