This file is indexed.

/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))