disarchive.scm 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. ;;; Disarchive
  2. ;;; Copyright © 2020-2022 Timothy Sample <samplet@ngyro.com>
  3. ;;;
  4. ;;; This file is part of Disarchive.
  5. ;;;
  6. ;;; Disarchive is free software: you can redistribute it and/or modify
  7. ;;; it under the terms of the GNU General Public License as published by
  8. ;;; the Free Software Foundation, either version 3 of the License, or
  9. ;;; (at your option) any later version.
  10. ;;;
  11. ;;; Disarchive is distributed in the hope that it will be useful,
  12. ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. ;;; GNU General Public License for more details.
  15. ;;;
  16. ;;; You should have received a copy of the GNU General Public License
  17. ;;; along with Disarchive. If not, see <http://www.gnu.org/licenses/>.
  18. (define-module (disarchive)
  19. #:use-module (disarchive assemblers)
  20. #:use-module (disarchive config)
  21. #:use-module (disarchive digests)
  22. #:use-module (disarchive disassemblers)
  23. #:use-module (disarchive logging)
  24. #:use-module (disarchive resolvers)
  25. #:use-module (disarchive utils)
  26. #:use-module (gcrypt base16)
  27. #:use-module (gcrypt hash)
  28. #:use-module (ice-9 exceptions)
  29. #:use-module (ice-9 match)
  30. #:use-module (rnrs bytevectors)
  31. #:use-module (srfi srfi-34)
  32. #:re-export (%disarchive-log-port)
  33. #:export (specification->blueprint
  34. disarchive-assemble
  35. disarchive-disassemble))
  36. ;;; Commentary:
  37. ;;;
  38. ;;; This module provides a high-level interface into Disarchive.
  39. ;;;
  40. ;;; Code:
  41. (define (wrap-blueprint serial)
  42. `(disarchive
  43. (version 0)
  44. ,serial))
  45. (define (unwrap-blueprint obj)
  46. (match obj
  47. (('disarchive ('version 0) serial) serial)
  48. (_ (error "Invalid Disarchive wrapper"))))
  49. (define (specification->blueprint specification)
  50. (match specification
  51. ((? string?) (specification->blueprint
  52. (call-with-input-file specification read)))
  53. ((? port?) (specification->blueprint (read specification)))
  54. (_ (deserialize-blueprint (unwrap-blueprint specification)))))
  55. (define* (disarchive-assemble specification out
  56. #:key (resolver (%resolve-addresses)))
  57. "Assemble the archive described by SPECIFICATION and write it to OUT.
  58. SPECIFICATION can be either a value returned by 'disarchive-disassemble'
  59. or a filename or port that contains such a value. If OUT is a filename,
  60. the result will be written there. Otherwise, OUT must be an output
  61. port.
  62. If RESOLVER is set, it will be used to resolve directory references.
  63. It must be a two-argument procedure that takes a list of addresses
  64. that refer to the same content and the name of an output directory.
  65. The RESOLVE procedure needs to obtain that content and write it to the
  66. given output directory."
  67. (let ((blueprint (specification->blueprint specification)))
  68. (call-with-temporary-directory
  69. (lambda (workspace)
  70. (guard (exn ((assembly-error? exn)
  71. (when (exception-with-message? exn)
  72. (message (exception-message exn)))
  73. #f))
  74. (parameterize ((%resolve-addresses resolver))
  75. (assemble blueprint workspace))
  76. (let* ((digest (blueprint-digest blueprint))
  77. (result (digest->filename digest workspace)))
  78. (match out
  79. ((? output-port?)
  80. (message "Writing result to output port")
  81. (call-with-input-file result
  82. (lambda (port)
  83. (dump-port-all port out))))
  84. ((? string?)
  85. (message "Copying result to ~a" out)
  86. (copy-file result out))
  87. (_ (scm-error 'wrong-type-arg "disarchive-assemble"
  88. "Wrong type (expecting string or port): ~A"
  89. (list out) (list out)))))
  90. #t)))))
  91. (define* (disarchive-disassemble filename #:optional
  92. (algorithm (hash-algorithm sha256))
  93. #:key name)
  94. "Disassemble FILENAME into a Disarchive specification. If ALGORITHM
  95. is set, use it instead of the default (SHA-256). Normally, the
  96. filename is used for the specification name. If this is wrong, it can
  97. be corrected explicitly with NAME."
  98. (call-with-temporary-directory
  99. (lambda (workspace)
  100. (parameterize ((%disarchive-directory-cache workspace))
  101. (let ((blueprint (disassemble filename algorithm #:name name)))
  102. (message "Finished disassembly of ~a" filename)
  103. (start-message "Checking that it can be assembled... ")
  104. (without-logging
  105. (assemble blueprint workspace))
  106. (message "ok")
  107. (let ((serial (serialize-blueprint blueprint)))
  108. (start-message "Checking that it can be deserialized... ")
  109. (let ((blueprint* (without-logging
  110. (deserialize-blueprint serial))))
  111. (if (equal? blueprint blueprint*)
  112. (message "ok")
  113. (error "the deserialized value differs from the original")))
  114. (wrap-blueprint serial)))))))