aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2008-05-29 05:25:22 +0000
committerStefan Monnier2008-05-29 05:25:22 +0000
commit58d6a142ecdca31989692d5495ff50144abc5ace (patch)
tree3b5d6e8b65763ddabbc8a275e570de691a60a084
parent62057df6e6056ff949f374c469459a217f6ca31a (diff)
downloademacs-58d6a142ecdca31989692d5495ff50144abc5ace.tar.gz
emacs-58d6a142ecdca31989692d5495ff50144abc5ace.zip
(tar-header): New field `header-start'.
(tar-header-block-tokenize): Set it when useful. Drop "GNUtar " magic value, which even GNU Tar doesn't know about. (tar-header-data-end): New function. (tar-summarize-buffer): Use it. (tar-next-line): Fix goal column for long usernames. (tar-expunge-internal): Use header-start. (tar-rename-entry): Handle ustar-style long names. (tar-alter-one-field): Add optional `descriptor' argument. (tar-subfile-save-buffer): Use it.
-rw-r--r--lisp/ChangeLog17
-rw-r--r--lisp/tar-mode.el192
2 files changed, 112 insertions, 97 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 45596d45439..a0b16ac7e17 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,5 +1,22 @@
12008-05-29 Stefan Monnier <monnier@iro.umontreal.ca>
2
3 * tar-mode.el (tar-header): New field `header-start'.
4 (tar-header-block-tokenize): Set it when useful.
5 Drop "GNUtar " magic value, which even GNU Tar doesn't know about.
6 (tar-header-data-end): New function.
7 (tar-summarize-buffer): Use it.
8 (tar-next-line): Fix goal column for long usernames.
9 (tar-expunge-internal): Use header-start.
10 (tar-rename-entry): Handle ustar-style long names.
11 (tar-alter-one-field): Add optional `descriptor' argument.
12 (tar-subfile-save-buffer): Use it.
13
12008-05-28 Stefan Monnier <monnier@iro.umontreal.ca> 142008-05-28 Stefan Monnier <monnier@iro.umontreal.ca>
2 15
16 * tar-mode.el (tar-header): New field `header-start'.
17 (tar-header-block-tokenize): Set header-start for longlink entries.
18 (tar-expunge-internal): Use header-start to expunge longlink entries.
19
3 * files.el (hack-local-variables): Don't signal an error if the local 20 * files.el (hack-local-variables): Don't signal an error if the local
4 variable section is not properly terminated. 21 variable section is not properly terminated.
5 22
diff --git a/lisp/tar-mode.el b/lisp/tar-mode.el
index e3ca528ad8e..931ef8907fa 100644
--- a/lisp/tar-mode.el
+++ b/lisp/tar-mode.el
@@ -93,9 +93,8 @@
93 93
94;;; Bugs: 94;;; Bugs:
95 95
96;; - Expunge and rename on ././@LongLink files 96;; - Rename on ././@LongLink files
97;; - Revert confirmation displays the raw data temporarily. 97;; - Revert confirmation displays the raw data temporarily.
98;; - Incorrect goal-column if username is too long.
99 98
100;;; Code: 99;;; Code:
101 100
@@ -181,7 +180,10 @@ This information is useful, but it takes screen space away from file names."
181 make-tar-header (data-start name mode uid gid size date checksum 180 make-tar-header (data-start name mode uid gid size date checksum
182 link-type link-name magic uname gname dmaj dmin))) 181 link-type link-name magic uname gname dmaj dmin)))
183 data-start name mode uid gid size date checksum link-type link-name 182 data-start name mode uid gid size date checksum link-type link-name
184 magic uname gname dmaj dmin) 183 magic uname gname dmaj dmin
184 ;; Start of the header can be nil (meaning it's 512 bytes before data-start)
185 ;; or a marker (in case the header uses LongLink thingies).
186 header-start)
185 187
186(defconst tar-name-offset 0) 188(defconst tar-name-offset 0)
187(defconst tar-mode-offset (+ tar-name-offset 100)) 189(defconst tar-mode-offset (+ tar-name-offset 100))
@@ -223,8 +225,7 @@ write-date, checksum, link-type, and link-name."
223 (link-p (aref string tar-linkp-offset)) 225 (link-p (aref string tar-linkp-offset))
224 (magic-str (substring string tar-magic-offset 226 (magic-str (substring string tar-magic-offset
225 (1- tar-uname-offset))) 227 (1- tar-uname-offset)))
226 (uname-valid-p (member magic-str 228 (uname-valid-p (car (member magic-str '("ustar " "ustar\0\0"))))
227 '("ustar " "GNUtar " "ustar\0\0")))
228 name linkname 229 name linkname
229 (nulsexp "[^\000]*\000")) 230 (nulsexp "[^\000]*\000"))
230 (when (string-match nulsexp string tar-name-offset) 231 (when (string-match nulsexp string tar-name-offset)
@@ -240,7 +241,7 @@ write-date, checksum, link-type, and link-name."
240 nil 241 nil
241 (- link-p ?0))) 242 (- link-p ?0)))
242 (setq linkname (substring string tar-link-offset link-end)) 243 (setq linkname (substring string tar-link-offset link-end))
243 (when (and uname-valid-p 244 (when (and (equal uname-valid-p "ustar\0\0")
244 (string-match nulsexp string tar-prefix-offset) 245 (string-match nulsexp string tar-prefix-offset)
245 (> (match-end 0) (1+ tar-prefix-offset))) 246 (> (match-end 0) (1+ tar-prefix-offset)))
246 (setq name (concat (substring string tar-prefix-offset 247 (setq name (concat (substring string tar-prefix-offset
@@ -271,6 +272,8 @@ write-date, checksum, link-type, and link-name."
271 (setf (tar-header-link-name descriptor) name)) 272 (setf (tar-header-link-name descriptor) name))
272 (t 273 (t
273 (message "Unrecognized GNU Tar @LongLink format"))) 274 (message "Unrecognized GNU Tar @LongLink format")))
275 (setf (tar-header-header-start descriptor)
276 (copy-marker (- pos 512) t))
274 descriptor) 277 descriptor)
275 278
276 (make-tar-header 279 (make-tar-header
@@ -291,6 +294,19 @@ write-date, checksum, link-type, and link-name."
291 (tar-parse-octal-integer string tar-dmin-offset tar-prefix-offset) 294 (tar-parse-octal-integer string tar-dmin-offset tar-prefix-offset)
292 )))))) 295 ))))))
293 296
297;; Pseudo-field.
298(defun tar-header-data-end (descriptor)
299 (let* ((data-start (tar-header-data-start descriptor))
300 (link-type (tar-header-link-type descriptor))
301 (size (tar-header-size descriptor))
302 (fudge (cond
303 ;; Foo. There's an extra empty block after these.
304 ((memq link-type '(20 55)) 512)
305 (t 0))))
306 (+ data-start fudge
307 (if (and (null link-type) (> size 0))
308 (tar-roundup-512 size)
309 0))))
294 310
295(defun tar-parse-octal-integer (string &optional start end) 311(defun tar-parse-octal-integer (string &optional start end)
296 (if (null start) (setq start 0)) 312 (if (null start) (setq start 0))
@@ -331,7 +347,6 @@ write-date, checksum, link-type, and link-name."
331(defun tar-header-block-checksum (string) 347(defun tar-header-block-checksum (string)
332 "Compute and return a tar-acceptable checksum for this block." 348 "Compute and return a tar-acceptable checksum for this block."
333 (assert (not (multibyte-string-p string))) 349 (assert (not (multibyte-string-p string)))
334 (setq string (string-as-unibyte string))
335 (let* ((chk-field-start tar-chk-offset) 350 (let* ((chk-field-start tar-chk-offset)
336 (chk-field-end (+ chk-field-start 8)) 351 (chk-field-end (+ chk-field-start 8))
337 (sum 0) 352 (sum 0)
@@ -449,27 +464,20 @@ MODE should be an integer which is a file mode value."
449 (with-current-buffer tar-data-buffer 464 (with-current-buffer tar-data-buffer
450 (while (and (<= (+ pos 512) (point-max)) 465 (while (and (<= (+ pos 512) (point-max))
451 (setq descriptor (tar-header-block-tokenize pos))) 466 (setq descriptor (tar-header-block-tokenize pos)))
452 (setq pos (marker-position (tar-header-data-start descriptor)))
453 (progress-reporter-update progress-reporter pos)
454 (if (memq (tar-header-link-type descriptor) '(20 55))
455 ;; Foo. There's an extra empty block after these.
456 (setq pos (+ pos 512)))
457 (let ((size (tar-header-size descriptor))) 467 (let ((size (tar-header-size descriptor)))
458 (if (< size 0) 468 (if (< size 0)
459 (error "%s has size %s - corrupted" 469 (error "%s has size %s - corrupted"
460 (tar-header-name descriptor) size)) 470 (tar-header-name descriptor) size)))
461 ;; 471 ;;
462 ;; This is just too slow. Don't really need it anyway.... 472 ;; This is just too slow. Don't really need it anyway....
463 ;;(tar-header-block-check-checksum 473 ;;(tar-header-block-check-checksum
464 ;; hblock (tar-header-block-checksum hblock) 474 ;; hblock (tar-header-block-checksum hblock)
465 ;; (tar-header-name descriptor)) 475 ;; (tar-header-name descriptor))
466 476
467 (push descriptor result) 477 (push descriptor result)
468 478 (setq pos (tar-header-data-end descriptor))
469 (and (null (tar-header-link-type descriptor)) 479 (progress-reporter-update progress-reporter pos)))
470 (> size 0) 480
471 (setq pos (+ pos (tar-roundup-512 size)))))))
472
473 (set (make-local-variable 'tar-parse-info) (nreverse result)) 481 (set (make-local-variable 'tar-parse-info) (nreverse result))
474 ;; A tar file should end with a block or two of nulls, 482 ;; A tar file should end with a block or two of nulls,
475 ;; but let's not get a fatal error if it doesn't. 483 ;; but let's not get a fatal error if it doesn't.
@@ -617,7 +625,7 @@ See also: variables `tar-update-datestamp' and `tar-anal-blocksize'.
617 (add-hook 'kill-buffer-hook 'tar-mode-kill-buffer-hook nil t) 625 (add-hook 'kill-buffer-hook 'tar-mode-kill-buffer-hook nil t)
618 (add-hook 'change-major-mode-hook 'tar-change-major-mode-hook nil t) 626 (add-hook 'change-major-mode-hook 'tar-change-major-mode-hook nil t)
619 ;; Tar data is made of bytes, not chars. 627 ;; Tar data is made of bytes, not chars.
620 (set-buffer-multibyte nil) 628 (set-buffer-multibyte nil) ;Hopefully a no-op.
621 (set (make-local-variable 'tar-data-buffer) 629 (set (make-local-variable 'tar-data-buffer)
622 (generate-new-buffer (format " *tar-data %s*" 630 (generate-new-buffer (format " *tar-data %s*"
623 (file-name-nondirectory 631 (file-name-nondirectory
@@ -674,7 +682,7 @@ appear on disk when you save the tar-file's buffer."
674 "Move cursor vertically down ARG lines and to the start of the filename." 682 "Move cursor vertically down ARG lines and to the start of the filename."
675 (interactive "p") 683 (interactive "p")
676 (forward-line arg) 684 (forward-line arg)
677 (if (eobp) nil (forward-char (if tar-mode-show-date 54 36)))) 685 (goto-char (or (next-single-property-change (point) 'mouse-face) (point))))
678 686
679(defun tar-previous-line (arg) 687(defun tar-previous-line (arg)
680 "Move cursor vertically up ARG lines and to the start of the filename." 688 "Move cursor vertically up ARG lines and to the start of the filename."
@@ -905,14 +913,7 @@ With a prefix argument, un-mark that many files backward."
905 913
906(defun tar-expunge-internal () 914(defun tar-expunge-internal ()
907 "Expunge the tar-entry specified by the current line." 915 "Expunge the tar-entry specified by the current line."
908 (let* ((descriptor (tar-current-descriptor)) 916 (let ((descriptor (tar-current-descriptor)))
909 ;; (line (tar-header-data-start descriptor))
910 (name (tar-header-name descriptor))
911 (size (tar-header-size descriptor))
912 (link-p (tar-header-link-type descriptor))
913 (start (tar-header-data-start descriptor))
914 (following-descs (cdr (memq descriptor tar-parse-info))))
915 (if link-p (setq size 0)) ; size lies for hard-links.
916 ;; 917 ;;
917 ;; delete the current line... 918 ;; delete the current line...
918 (delete-region (line-beginning-position) (line-beginning-position 2)) 919 (delete-region (line-beginning-position) (line-beginning-position 2))
@@ -921,10 +922,10 @@ With a prefix argument, un-mark that many files backward."
921 (setq tar-parse-info (delq descriptor tar-parse-info)) 922 (setq tar-parse-info (delq descriptor tar-parse-info))
922 ;; 923 ;;
923 ;; delete the data from inside the file... 924 ;; delete the data from inside the file...
924 (let* ((data-start (- start 512)) 925 (with-current-buffer tar-data-buffer
925 (data-end (+ start (tar-roundup-512 size)))) 926 (delete-region (or (tar-header-header-start descriptor)
926 (with-current-buffer tar-data-buffer 927 (- (tar-header-data-start descriptor) 512))
927 (delete-region data-start data-end))))) 928 (tar-header-data-end descriptor)))))
928 929
929 930
930(defun tar-expunge (&optional noconfirm) 931(defun tar-expunge (&optional noconfirm)
@@ -1019,12 +1020,29 @@ for this to be permanent."
1019 (tar-header-name (tar-current-descriptor))))) 1020 (tar-header-name (tar-current-descriptor)))))
1020 (if (string= "" new-name) (error "zero length name")) 1021 (if (string= "" new-name) (error "zero length name"))
1021 (let ((encoded-new-name (encode-coding-string new-name 1022 (let ((encoded-new-name (encode-coding-string new-name
1022 tar-file-name-coding-system))) 1023 tar-file-name-coding-system))
1024 (descriptor (tar-current-descriptor))
1025 (prefix nil))
1026 (when (tar-header-header-start descriptor)
1027 ;; FIXME: Make it work for ././@LongLink.
1028 (error "Rename with @LongLink format is not implemented"))
1029
1030 (when (and (> (length encoded-new-name) 98)
1031 (string-match "/" encoded-new-name
1032 (- (length encoded-new-name) 99))
1033 (< (match-beginning 0) 155))
1034 (unless (equal (tar-header-magic descriptor) "ustar\0\0")
1035 (tar-alter-one-field tar-magic-offset "ustar\0\0"))
1036 (setq prefix (substring encoded-new-name 0 (match-beginning 0)))
1037 (setq encoded-new-name (substring encoded-new-name (match-end 0))))
1038
1023 (if (> (length encoded-new-name) 98) (error "name too long")) 1039 (if (> (length encoded-new-name) 98) (error "name too long"))
1024 (setf (tar-header-name (tar-current-descriptor)) new-name) 1040 (setf (tar-header-name descriptor) new-name)
1025 ;; FIXME: Make it work for ././@LongLink.
1026 (tar-alter-one-field 0 1041 (tar-alter-one-field 0
1027 (substring (concat encoded-new-name (make-string 99 0)) 0 99)))) 1042 (substring (concat encoded-new-name (make-string 99 0)) 0 99))
1043 (if prefix
1044 (tar-alter-one-field tar-prefix-offset
1045 (substring (concat prefix (make-string 155 0)) 0 155)))))
1028 1046
1029 1047
1030(defun tar-chmod-entry (new-mode) 1048(defun tar-chmod-entry (new-mode)
@@ -1038,41 +1056,43 @@ for this to be permanent."
1038 (concat (substring (format "%6o" new-mode) 0 6) "\000 "))) 1056 (concat (substring (format "%6o" new-mode) 0 6) "\000 ")))
1039 1057
1040 1058
1041(defun tar-alter-one-field (data-position new-data-string) 1059(defun tar-alter-one-field (data-position new-data-string &optional descriptor)
1042 (let* ((descriptor (tar-current-descriptor))) 1060 (unless descriptor (setq descriptor (tar-current-descriptor)))
1043 ;; 1061 ;;
1044 ;; update the header-line. 1062 ;; update the header-line.
1045 (let ((col (current-column))) 1063 (let ((col (current-column)))
1046 (delete-region (line-beginning-position) (line-beginning-position 2)) 1064 (delete-region (line-beginning-position)
1047 (insert (tar-header-block-summarize descriptor) "\n") 1065 (prog2 (forward-line 1)
1048 (forward-line -1) (move-to-column col)) 1066 (point)
1067 ;; Insert the new text after the old, before deleting,
1068 ;; to preserve markers such as the window start.
1069 (insert (tar-header-block-summarize descriptor) "\n")))
1070 (forward-line -1) (move-to-column col))
1049 1071
1050 (with-current-buffer tar-data-buffer 1072 (assert (tar-data-swapped-p))
1051 (let* ((start (- (tar-header-data-start descriptor) 512))) 1073 (with-current-buffer tar-data-buffer
1074 (let* ((start (- (tar-header-data-start descriptor) 512)))
1052 ;; 1075 ;;
1053 ;; delete the old field and insert a new one. 1076 ;; delete the old field and insert a new one.
1054 (goto-char (+ start data-position)) 1077 (goto-char (+ start data-position))
1055 (delete-region (point) (+ (point) (length new-data-string))) ; <-- 1078 (delete-region (point) (+ (point) (length new-data-string))) ; <--
1056
1057 (assert (not (or enable-multibyte-characters 1079 (assert (not (or enable-multibyte-characters
1058 (multibyte-string-p new-data-string)))) 1080 (multibyte-string-p new-data-string))))
1059 (insert new-data-string) 1081 (insert new-data-string)
1060 ;; 1082 ;;
1061 ;; compute a new checksum and insert it. 1083 ;; compute a new checksum and insert it.
1062 (let ((chk (tar-header-block-checksum 1084 (let ((chk (tar-header-block-checksum
1063 (buffer-substring start (+ start 512))))) 1085 (buffer-substring start (+ start 512)))))
1064 (goto-char (+ start tar-chk-offset)) 1086 (goto-char (+ start tar-chk-offset))
1065 (delete-region (point) (+ (point) 8)) 1087 (delete-region (point) (+ (point) 8))
1066 (insert (format "%6o" chk)) 1088 (insert (format "%6o\0 " chk))
1067 (insert 0) 1089 (setf (tar-header-checksum descriptor) chk)
1068 (insert ? ) 1090 ;;
1069 (setf (tar-header-checksum descriptor) chk) 1091 ;; ok, make sure we didn't botch it.
1070 ;; 1092 (tar-header-block-check-checksum
1071 ;; ok, make sure we didn't botch it. 1093 (buffer-substring start (+ start 512))
1072 (tar-header-block-check-checksum 1094 chk (tar-header-name descriptor))
1073 (buffer-substring start (+ start 512)) 1095 ))))
1074 chk (tar-header-name descriptor))
1075 )))))
1076 1096
1077 1097
1078(defun tar-octal-time (timeval) 1098(defun tar-octal-time (timeval)
@@ -1129,41 +1149,19 @@ to make your changes permanent."
1129 (setf (tar-header-size descriptor) subfile-size) 1149 (setf (tar-header-size descriptor) subfile-size)
1130 ;; 1150 ;;
1131 ;; Update the size field in the header block. 1151 ;; Update the size field in the header block.
1132 (widen) 1152 (widen))))
1133 (let ((header-start (- data-start 512)))
1134 (goto-char (+ header-start tar-size-offset))
1135 (delete-region (point) (+ (point) 12))
1136 (insert (format "%11o " subfile-size))
1137 ;;
1138 ;; Maybe update the datestamp.
1139 (if (not tar-update-datestamp)
1140 nil
1141 (goto-char (+ header-start tar-time-offset))
1142 (delete-region (point) (+ (point) 12))
1143 (insert (tar-octal-time (current-time)))
1144 (insert ?\s))
1145 ;;
1146 ;; compute a new checksum and insert it.
1147 (let ((chk (tar-header-block-checksum
1148 (buffer-substring header-start data-start))))
1149 (goto-char (+ header-start tar-chk-offset))
1150 (delete-region (point) (+ (point) 8))
1151 (insert (format "%6o\0 " chk))
1152 (setf (tar-header-checksum descriptor) chk))))))
1153 ;; 1153 ;;
1154 ;; alter the descriptor-line... 1154 ;; alter the descriptor-line and header
1155 ;; 1155 ;;
1156 (let ((position (- (length tar-parse-info) (length head)))) 1156 (let ((position (- (length tar-parse-info) (length head))))
1157 (goto-char (point-min)) 1157 (goto-char (point-min))
1158 (forward-line position) 1158 (forward-line position)
1159 (let ((p (point)) 1159 (tar-alter-one-field tar-size-offset (format "%11o " subfile-size))
1160 (after (line-beginning-position 2))) 1160 ;;
1161 (goto-char after) 1161 ;; Maybe update the datestamp.
1162 ;; Insert the new text after the old, before deleting, 1162 (when tar-update-datestamp
1163 ;; to preserve the window start. 1163 (tar-alter-one-field tar-time-offset
1164 (let ((line (tar-header-block-summarize descriptor t))) 1164 (concat (tar-octal-time (current-time)) " "))))
1165 (insert-before-markers line "\n"))
1166 (delete-region p after)))
1167 ;; After doing the insertion, add any necessary final padding. 1165 ;; After doing the insertion, add any necessary final padding.
1168 (tar-pad-to-blocksize)) 1166 (tar-pad-to-blocksize))
1169 (set-buffer-modified-p t) ; mark the tar file as modified 1167 (set-buffer-modified-p t) ; mark the tar file as modified