diff options
| author | Ivan Shmakov | 2015-01-27 21:25:56 +0000 |
|---|---|---|
| committer | Ivan Shmakov | 2015-01-27 21:56:57 +0000 |
| commit | a56eab8259568ea1389e972623e46359e73c0233 (patch) | |
| tree | 9ab9bc4810a6543f0a6f51c1659c3c374c1751ab | |
| parent | bd7a1e1564d04d7ea9c7f6587ffcf02ef8975512 (diff) | |
| download | emacs-a56eab8259568ea1389e972623e46359e73c0233.tar.gz emacs-a56eab8259568ea1389e972623e46359e73c0233.zip | |
Allow for adding new members to Tar archives.
* lisp/tar-mode.el: Allow for adding new archive members.
(tar-new-regular-file-header, tar--pad-to, tar--put-at)
(tar-header-serialize): New functions.
(tar-current-position): Split from tar-current-descriptor.
(tar-current-descriptor): Use it.
(tar-new-entry): New command.
(tar-mode-map): Bind it.
* doc/emacs/files.texi (File Archives): Document "I" for tar-new-entry.
* etc/NEWS: Mention the new tar-new-entry command.
Fixes: debbugs:19274
| -rw-r--r-- | doc/emacs/ChangeLog | 5 | ||||
| -rw-r--r-- | doc/emacs/files.texi | 7 | ||||
| -rw-r--r-- | etc/ChangeLog | 4 | ||||
| -rw-r--r-- | etc/NEWS | 4 | ||||
| -rw-r--r-- | lisp/ChangeLog | 10 | ||||
| -rw-r--r-- | lisp/tar-mode.el | 115 |
6 files changed, 141 insertions, 4 deletions
diff --git a/doc/emacs/ChangeLog b/doc/emacs/ChangeLog index a90c58725f8..b7853a7f118 100644 --- a/doc/emacs/ChangeLog +++ b/doc/emacs/ChangeLog | |||
| @@ -1,3 +1,8 @@ | |||
| 1 | 2015-01-27 Ivan Shmakov <ivan@siamics.net> | ||
| 2 | |||
| 3 | * files.texi (File Archives): Document "I" for tar-new-entry. | ||
| 4 | (Bug#19274) | ||
| 5 | |||
| 1 | 2014-12-31 Paul Eggert <eggert@cs.ucla.edu> | 6 | 2014-12-31 Paul Eggert <eggert@cs.ucla.edu> |
| 2 | 7 | ||
| 3 | Less 'make' chatter for Emacs doc | 8 | Less 'make' chatter for Emacs doc |
diff --git a/doc/emacs/files.texi b/doc/emacs/files.texi index 196c6bb0092..b12b28f9c17 100644 --- a/doc/emacs/files.texi +++ b/doc/emacs/files.texi | |||
| @@ -1689,6 +1689,13 @@ likewise. @kbd{v} extracts a file into a buffer in View mode | |||
| 1689 | another window, so you could edit the file and operate on the archive | 1689 | another window, so you could edit the file and operate on the archive |
| 1690 | simultaneously. | 1690 | simultaneously. |
| 1691 | 1691 | ||
| 1692 | The @kbd{I} key adds a new (regular) file to the archive. The file | ||
| 1693 | is initially empty, but can readily be edited using the commands | ||
| 1694 | above. The command inserts the new file before the current one, so | ||
| 1695 | that using it on the topmost line of the Tar buffer makes the new file | ||
| 1696 | the first one in the archive, and using it at the end of the buffer | ||
| 1697 | makes it the last one. | ||
| 1698 | |||
| 1692 | @kbd{d} marks a file for deletion when you later use @kbd{x}, and | 1699 | @kbd{d} marks a file for deletion when you later use @kbd{x}, and |
| 1693 | @kbd{u} unmarks a file, as in Dired. @kbd{C} copies a file from the | 1700 | @kbd{u} unmarks a file, as in Dired. @kbd{C} copies a file from the |
| 1694 | archive to disk and @kbd{R} renames a file within the archive. | 1701 | archive to disk and @kbd{R} renames a file within the archive. |
diff --git a/etc/ChangeLog b/etc/ChangeLog index b31e8a99383..0677e441b83 100644 --- a/etc/ChangeLog +++ b/etc/ChangeLog | |||
| @@ -1,3 +1,7 @@ | |||
| 1 | 2015-01-27 Ivan Shmakov <ivan@siamics.net> | ||
| 2 | |||
| 3 | * NEWS: Mention the new tar-new-entry command. (Bug#19274) | ||
| 4 | |||
| 1 | 2015-01-27 Thomas Fitzsimmons <fitzsim@fitzsim.org> | 5 | 2015-01-27 Thomas Fitzsimmons <fitzsim@fitzsim.org> |
| 2 | 6 | ||
| 3 | * NEWS: Document EUDC improvements. | 7 | * NEWS: Document EUDC improvements. |
| @@ -527,6 +527,10 @@ to avoid interfering with the kill ring. | |||
| 527 | allow overriding the regular expression that recognizes the ldapsearch | 527 | allow overriding the regular expression that recognizes the ldapsearch |
| 528 | command line's password prompt. | 528 | command line's password prompt. |
| 529 | 529 | ||
| 530 | +++ | ||
| 531 | ** tar-mode: new `tar-new-entry' command, allowing for new members to | ||
| 532 | be added to the archive. | ||
| 533 | |||
| 530 | ** Obsolete packages | 534 | ** Obsolete packages |
| 531 | 535 | ||
| 532 | --- | 536 | --- |
diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 80dfeef3750..182d7705bb6 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog | |||
| @@ -1,3 +1,13 @@ | |||
| 1 | 2015-01-27 Ivan Shmakov <ivan@siamics.net> | ||
| 2 | |||
| 3 | * tar-mode.el: Allow for adding new archive members. (Bug#19274) | ||
| 4 | (tar-new-regular-file-header, tar--pad-to, tar--put-at) | ||
| 5 | (tar-header-serialize): New functions. | ||
| 6 | (tar-current-position): Split from tar-current-descriptor. | ||
| 7 | (tar-current-descriptor): Use it. | ||
| 8 | (tar-new-entry): New command. | ||
| 9 | (tar-mode-map): Bind it. | ||
| 10 | |||
| 1 | 2015-01-27 Sam Steingold <sds@gnu.org> | 11 | 2015-01-27 Sam Steingold <sds@gnu.org> |
| 2 | 12 | ||
| 3 | * progmodes/python.el (python-check-custom-command): Buffer local | 13 | * progmodes/python.el (python-check-custom-command): Buffer local |
diff --git a/lisp/tar-mode.el b/lisp/tar-mode.el index 1ee54515bea..6c7f7553f82 100644 --- a/lisp/tar-mode.el +++ b/lisp/tar-mode.el | |||
| @@ -50,9 +50,6 @@ | |||
| 50 | ;; | 50 | ;; |
| 51 | ;; o chmod should understand "a+x,og-w". | 51 | ;; o chmod should understand "a+x,og-w". |
| 52 | ;; | 52 | ;; |
| 53 | ;; o It's not possible to add a NEW file to a tar archive; not that | ||
| 54 | ;; important, but still... | ||
| 55 | ;; | ||
| 56 | ;; o The code is less efficient that it could be - in a lot of places, I | 53 | ;; o The code is less efficient that it could be - in a lot of places, I |
| 57 | ;; pull a 512-character string out of the buffer and parse it, when I could | 54 | ;; pull a 512-character string out of the buffer and parse it, when I could |
| 58 | ;; be parsing it in place, not garbaging a string. Should redo that. | 55 | ;; be parsing it in place, not garbaging a string. Should redo that. |
| @@ -369,6 +366,80 @@ write-date, checksum, link-type, and link-name." | |||
| 369 | string) | 366 | string) |
| 370 | (tar-parse-octal-integer string)) | 367 | (tar-parse-octal-integer string)) |
| 371 | 368 | ||
| 369 | (defun tar-new-regular-file-header (filename &optional size time) | ||
| 370 | "Return a Tar header for a regular file. | ||
| 371 | The header will lack a proper checksum; use `tar-header-block-checksum' | ||
| 372 | to compute one, or request `tar-header-serialize' to do that. | ||
| 373 | |||
| 374 | Other tar-mode facilities may also require the data-start header | ||
| 375 | field to be set to a valid value. | ||
| 376 | |||
| 377 | If SIZE is not given or nil, it defaults to 0. | ||
| 378 | If TIME is not given or nil, assume now." | ||
| 379 | (make-tar-header | ||
| 380 | nil | ||
| 381 | filename | ||
| 382 | #o644 0 0 (or size 0) | ||
| 383 | (or time (current-time)) | ||
| 384 | nil ; checksum | ||
| 385 | nil nil | ||
| 386 | nil nil nil nil nil)) | ||
| 387 | |||
| 388 | (defun tar--pad-to (pos) | ||
| 389 | (make-string (+ pos (- (point)) (point-min)) 0)) | ||
| 390 | |||
| 391 | (defun tar--put-at (pos val &optional fmt mask) | ||
| 392 | (when val | ||
| 393 | (insert (tar--pad-to pos) | ||
| 394 | (if fmt | ||
| 395 | (format fmt (if mask (logand mask val) val)) | ||
| 396 | val)))) | ||
| 397 | |||
| 398 | (defun tar-header-serialize (header &optional update-checksum) | ||
| 399 | "Return the serialization of a Tar HEADER as a string. | ||
| 400 | This function calls `tar-header-block-check-checksum' to ensure the | ||
| 401 | checksum is correct. | ||
| 402 | |||
| 403 | If UPDATE-CHECKSUM is non-nil, update HEADER with the newly-computed | ||
| 404 | checksum before doing the check." | ||
| 405 | (with-temp-buffer | ||
| 406 | (set-buffer-multibyte nil) | ||
| 407 | (let ((encoded-name | ||
| 408 | (encode-coding-string (tar-header-name header) | ||
| 409 | tar-file-name-coding-system))) | ||
| 410 | (unless (< (length encoded-name) 99) | ||
| 411 | ;; FIXME: Implement it. | ||
| 412 | (error "Long file name support is not implemented")) | ||
| 413 | (insert encoded-name)) | ||
| 414 | (tar--put-at tar-mode-offset (tar-header-mode header) "%6o\0 " #o777777) | ||
| 415 | (tar--put-at tar-uid-offset (tar-header-uid header) "%6o\0 " #o777777) | ||
| 416 | (tar--put-at tar-gid-offset (tar-header-gid header) "%6o\0 " #o777777) | ||
| 417 | (tar--put-at tar-size-offset (tar-header-size header) "%11o ") | ||
| 418 | (insert (tar--pad-to tar-time-offset) | ||
| 419 | (tar-octal-time (tar-header-date header)) | ||
| 420 | " ") | ||
| 421 | ;; Omit tar-header-checksum (tar-chk-offset) for now. | ||
| 422 | (tar--put-at tar-linkp-offset (tar-header-link-type header)) | ||
| 423 | (tar--put-at tar-link-offset (tar-header-link-name header)) | ||
| 424 | (when (tar-header-magic header) | ||
| 425 | (tar--put-at tar-magic-offset (tar-header-magic header)) | ||
| 426 | (tar--put-at tar-uname-offset (tar-header-uname header)) | ||
| 427 | (tar--put-at tar-gname-offset (tar-header-gname header)) | ||
| 428 | (tar--put-at tar-dmaj-offset (tar-header-dmaj header) "%7o\0" #o7777777) | ||
| 429 | (tar--put-at tar-dmin-offset (tar-header-dmin header) "%7o\0" #o7777777)) | ||
| 430 | (tar--put-at 512 "") | ||
| 431 | (let ((ck (tar-header-block-checksum (buffer-string)))) | ||
| 432 | (goto-char (+ (point-min) tar-chk-offset)) | ||
| 433 | (delete-char 8) | ||
| 434 | (insert (format "%6o\0 " ck)) | ||
| 435 | (when update-checksum | ||
| 436 | (setf (tar-header-checksum header) ck)) | ||
| 437 | (tar-header-block-check-checksum (buffer-string) | ||
| 438 | (tar-header-checksum header) | ||
| 439 | (tar-header-name header))) | ||
| 440 | ;; . | ||
| 441 | (buffer-string))) | ||
| 442 | |||
| 372 | 443 | ||
| 373 | (defun tar-header-block-checksum (string) | 444 | (defun tar-header-block-checksum (string) |
| 374 | "Compute and return a tar-acceptable checksum for this block." | 445 | "Compute and return a tar-acceptable checksum for this block." |
| @@ -547,6 +618,7 @@ MODE should be an integer which is a file mode value." | |||
| 547 | (define-key map "p" 'tar-previous-line) | 618 | (define-key map "p" 'tar-previous-line) |
| 548 | (define-key map "\^P" 'tar-previous-line) | 619 | (define-key map "\^P" 'tar-previous-line) |
| 549 | (define-key map [up] 'tar-previous-line) | 620 | (define-key map [up] 'tar-previous-line) |
| 621 | (define-key map "I" 'tar-new-entry) | ||
| 550 | (define-key map "R" 'tar-rename-entry) | 622 | (define-key map "R" 'tar-rename-entry) |
| 551 | (define-key map "u" 'tar-unflag) | 623 | (define-key map "u" 'tar-unflag) |
| 552 | (define-key map "v" 'tar-view) | 624 | (define-key map "v" 'tar-view) |
| @@ -731,10 +803,14 @@ tar-file's buffer." | |||
| 731 | (interactive "p") | 803 | (interactive "p") |
| 732 | (tar-next-line (- arg))) | 804 | (tar-next-line (- arg))) |
| 733 | 805 | ||
| 806 | (defun tar-current-position () | ||
| 807 | "Return the `tar-parse-info' index for the current line." | ||
| 808 | (count-lines (point-min) (line-beginning-position))) | ||
| 809 | |||
| 734 | (defun tar-current-descriptor (&optional noerror) | 810 | (defun tar-current-descriptor (&optional noerror) |
| 735 | "Return the tar-descriptor of the current line, or signals an error." | 811 | "Return the tar-descriptor of the current line, or signals an error." |
| 736 | ;; I wish lines had plists, like in ZMACS... | 812 | ;; I wish lines had plists, like in ZMACS... |
| 737 | (or (nth (count-lines (point-min) (line-beginning-position)) | 813 | (or (nth (tar-current-position) |
| 738 | tar-parse-info) | 814 | tar-parse-info) |
| 739 | (if noerror | 815 | (if noerror |
| 740 | nil | 816 | nil |
| @@ -948,6 +1024,37 @@ the current tar-entry." | |||
| 948 | (write-region start end to-file nil nil nil t))) | 1024 | (write-region start end to-file nil nil nil t))) |
| 949 | (message "Copied tar entry %s to %s" name to-file))) | 1025 | (message "Copied tar entry %s to %s" name to-file))) |
| 950 | 1026 | ||
| 1027 | (defun tar-new-entry (filename &optional index) | ||
| 1028 | "Insert a new empty regular file before point." | ||
| 1029 | (interactive "*sFile name: ") | ||
| 1030 | (let* ((buffer (current-buffer)) | ||
| 1031 | (index (or index (tar-current-position))) | ||
| 1032 | (d-list (and (not (zerop index)) | ||
| 1033 | (nthcdr (+ -1 index) tar-parse-info))) | ||
| 1034 | (pos (if d-list | ||
| 1035 | (tar-header-data-end (car d-list)) | ||
| 1036 | (point-min))) | ||
| 1037 | (new-descriptor | ||
| 1038 | (tar-new-regular-file-header filename))) | ||
| 1039 | ;; Update the data buffer; fill the missing descriptor fields. | ||
| 1040 | (with-current-buffer tar-data-buffer | ||
| 1041 | (goto-char pos) | ||
| 1042 | (insert (tar-header-serialize new-descriptor t)) | ||
| 1043 | (setf (tar-header-data-start new-descriptor) | ||
| 1044 | (copy-marker (point) nil))) | ||
| 1045 | ;; Update tar-parse-info. | ||
| 1046 | (if d-list | ||
| 1047 | (setcdr d-list (cons new-descriptor (cdr d-list))) | ||
| 1048 | (setq tar-parse-info (cons new-descriptor tar-parse-info))) | ||
| 1049 | ;; Update the listing buffer. | ||
| 1050 | (save-excursion | ||
| 1051 | (goto-char (point-min)) | ||
| 1052 | (forward-line index) | ||
| 1053 | (let ((inhibit-read-only t)) | ||
| 1054 | (insert (tar-header-block-summarize new-descriptor) ?\n))) | ||
| 1055 | ;; . | ||
| 1056 | index)) | ||
| 1057 | |||
| 951 | (defun tar-flag-deleted (p &optional unflag) | 1058 | (defun tar-flag-deleted (p &optional unflag) |
| 952 | "In Tar mode, mark this sub-file to be deleted from the tar file. | 1059 | "In Tar mode, mark this sub-file to be deleted from the tar file. |
| 953 | With a prefix argument, mark that many files." | 1060 | With a prefix argument, mark that many files." |