(in-package :ybackup) #| (defun fetch-configuration () (let ((xdg (uiop:getenv "XDG_CONFIG_DIR"))) (if xdg (setf xdg (concatenate 'string xdg "/ybackup.lisp")) (setf xdg (concatenate 'string (uiop:getenv "HOME") "/.config/ybackup.lisp"))) (ensure-directories-exist xdg) (with-open-file (file xdg :if-does-not-exist :create) (uiop:with-safe-io-syntax (uiop:slurp-stream-form :at nil))))) |# (opts:define-opts (:name :help :description "Print this text" :short #\h :long "help") (:name :backup :description "Directory to backup" :short #\b :long "backup" :arg-parser #'identity :meta-var "DIRECTORY") (:name :read :description "Read a backup to a directory" :short #\r :long "read" :arg-parser #'identity :meta-var "DIRECTORY") (:name :list-files :description "List the files in the repository." :short #\l :long "list") (:name :file-backend :description "Use this for a file-based local backup. The argument is the directory that will contain the ERIS chunks." :long "file-backend" :arg-parser #'identity :meta-var "DIRECTORY") (:name :http-backend :description "Use this for an HTTP-based backup. The argument is the URL which will accept the ERIS chunks." :long "http" :arg-parser #'identity :meta-var "URL") (:name :backend :description "An S-expression that returns a valid eris:backend object." :long "backend" :meta-var "SEXP" :arg-parser #'read-from-string) (:name :filter :description "A one-argument lambda S-expression that takes a filename as an argument and returns nil if the file is to be read or t if it is to be skipped." :long "filter" :meta-var "SEXP" :arg-parser #'read-from-string) (:name :overwrite :description "Set if the program should overwrite existing files when writing from backup. " :long "overwrite") (:name :incremental :description "Set to enable incremental backup. Requires the --repo optio.." :short #\i :long "incremental") (:name :repo :description "The file that the URN will be written or read from." :long "repo" :meta-var "FILE OR URN" :arg-parser #'identity) (:name :secret :description "The secret used for encryption." :long "secret" :short #\s :meta-var "SECRET" :arg-parser #'identity) (:name :metadata :description "Print repository metadata when reading or listing files." :long "metadata" :short #\m)) (defun file-or-urn-to-urn (file-or-urn) (if (string-prefix-p "urn:" file-or-urn) file-or-urn (with-open-file (file file-or-urn :direction :input :if-does-not-exist :error) (read-line file)))) (defun main () (restart-case (destructuring-bind (&key help backup read list-files file-backend http-backend backend filter overwrite incremental repo secret metadata) (opts:get-opts) ;; some sanity checks ;; exclusive options (when (or (and backup read) (and backup list-files) (and read list-files)) (error "Choose one of read, backup, or list.")) (when (or (and backend http-backend) (and backend file-backend) (and file-backend http-backend)) (error "Choose one backend.")) (when help (opts:describe :prefix #.(format nil "ybackup version ~a" version)) (opts:exit)) ;; repo argument necessary except for backup #|(when (and (not repo) backup) (error "Please provide --repo argument."))|# ;; don't save urns to files named urn: #|(when (and backup repo (string-prefix-p "urn:" repo)) (error "No urns as filenames."))|# (let ((backend (cond (file-backend (make-instance 'eris:file-backend :directory file-backend)) (http-backend (error "Unimplemented http-backend.")) (backend (eval backend)) (t (error "Choose backend."))))) ;; TODO: ;; ADD METADATA, SECRET HANDLING (!!!) (cond (list-files (print (list-files (file-or-urn-to-urn repo) backend)) ()) (backup (let ((urn (make-backup backup backend :incremental incremental))) (if repo (with-open-file (file repo :direction :output :if-does-not-exist :create :if-exists :new-version) (write-string urn file)) (princ urn)))) (read (read-backup (file-or-urn-to-urn repo) backend read :overwrite overwrite))) (opts:exit))) (exit () (opts:exit))))