comparison lisp/packages/tar-mode.el @ 187:b405438285a2 r20-3b20

Import from CVS: tag r20-3b20
author cvs
date Mon, 13 Aug 2007 09:56:28 +0200
parents 2d532a89d707
children 41ff10fd062f
comparison
equal deleted inserted replaced
186:24ac94803b48 187:b405438285a2
88 ;; o When using crypt-mode, you can't save a compressed or encrypted subfile 88 ;; o When using crypt-mode, you can't save a compressed or encrypted subfile
89 ;; of a tar file back into the tar file: it is saved uncompressed. 89 ;; of a tar file back into the tar file: it is saved uncompressed.
90 90
91 ;;; Code: 91 ;;; Code:
92 92
93 (defvar tar-anal-blocksize 20 93 (defgroup tar ()
94 "Simple editing of tar files from GNU emacs."
95 :group 'unix
96 :group 'data)
97
98
99 (defcustom tar-anal-blocksize 20
94 "*The blocksize of tar files written by Emacs, or nil, meaning don't care. 100 "*The blocksize of tar files written by Emacs, or nil, meaning don't care.
95 The blocksize of a tar file is not really the size of the blocks; rather, it is 101 The blocksize of a tar file is not really the size of the blocks; rather, it is
96 the number of blocks written with one system call. When tarring to a tape, 102 the number of blocks written with one system call. When tarring to a tape,
97 this is the size of the *tape* blocks, but when writing to a file, it doesn't 103 this is the size of the *tape* blocks, but when writing to a file, it doesn't
98 matter much. The only noticeable difference is that if a tar file does not 104 matter much. The only noticeable difference is that if a tar file does not
99 have a blocksize of 20, the tar program will issue a warning; all this really 105 have a blocksize of 20, the tar program will issue a warning; all this really
100 controls is how many null padding bytes go on the end of the tar file.") 106 controls is how many null padding bytes go on the end of the tar file."
101 107 :type 'integer
102 (defvar tar-update-datestamp (or (fboundp 'current-time) 108 :group 'tar)
103 (fboundp 'current-time-seconds)) 109
110 (defcustom tar-update-datestamp t
104 "*Whether tar-mode should play fast and loose with sub-file datestamps; 111 "*Whether tar-mode should play fast and loose with sub-file datestamps;
105 if this is true, then editing and saving a tar file entry back into its 112 if this is true, then editing and saving a tar file entry back into its
106 tar file will update its datestamp. If false, the datestamp is unchanged. 113 tar file will update its datestamp. If false, the datestamp is unchanged.
107 You may or may not want this - it is good in that you can tell when a file 114 You may or may not want this - it is good in that you can tell when a file
108 in a tar archive has been changed, but it is bad for the same reason that 115 in a tar archive has been changed, but it is bad for the same reason that
109 editing a file in the tar archive at all is bad - the changed version of 116 editing a file in the tar archive at all is bad - the changed version of
110 the file never exists on disk. 117 the file never exists on disk.
111 118
112 This does not work in Emacs 18, because there's no way to get the current 119 This does not work in Emacs 18, because there's no way to get the current
113 time as an integer - if this var is true, then editing a file sets its date 120 time as an integer - if this var is true, then editing a file sets its date
114 to midnight, Jan 1 1970 GMT, which happens to be what 0 encodes.") 121 to midnight, Jan 1 1970 GMT, which happens to be what 0 encodes."
122 :type 'boolean
123 :group 'tar)
115 124
116 125
117 ;;; First, duplicate some Common Lisp functions; I used to just (require 'cl) 126 ;;; First, duplicate some Common Lisp functions; I used to just (require 'cl)
118 ;;; but "cl.el" was messing some people up (also it's really big). 127 ;;; but "cl.el" was messing some people up (also it's really big).
119 128
120 (defmacro tar-setf (form val) 129 ;; No need for that stuff anymore -- XEmacs preloads cl.el anyway.
121 "A mind-numbingly simple implementation of setf."
122 (let ((mform (macroexpand form (and (boundp 'byte-compile-macro-environment)
123 byte-compile-macro-environment))))
124 (cond ((symbolp mform) (list 'setq mform val))
125 ((not (consp mform)) (error "can't setf %s" form))
126 ((eq (car mform) 'aref)
127 (list 'aset (nth 1 mform) (nth 2 mform) val))
128 ((eq (car mform) 'car)
129 (list 'setcar (nth 1 mform) val))
130 ((eq (car mform) 'cdr)
131 (list 'setcdr (nth 1 mform) val))
132 (t (error "don't know how to setf %s" form)))))
133
134 (defmacro tar-dolist (control &rest body)
135 "syntax: (dolist (var-name list-expr &optional return-value) &body body)"
136 (let ((var (car control))
137 (init (car (cdr control)))
138 (val (car (cdr (cdr control)))))
139 (list 'let (list (list '_dolist_iterator_ init))
140 (list 'while '_dolist_iterator_
141 (cons 'let
142 (cons (list (list var '(car _dolist_iterator_)))
143 (append body
144 (list (list 'setq '_dolist_iterator_
145 (list 'cdr '_dolist_iterator_)))))))
146 val)))
147
148 (defmacro tar-dotimes (control &rest body)
149 "syntax: (dotimes (var-name count-expr &optional return-value) &body body)"
150 (let ((var (car control))
151 (n (car (cdr control)))
152 (val (car (cdr (cdr control)))))
153 (list 'let (list (list '_dotimes_end_ n)
154 (list var 0))
155 (cons 'while
156 (cons (list '< var '_dotimes_end_)
157 (append body
158 (list (list 'setq var (list '1+ var))))))
159 val)))
160 130
161 131
162 ;;; down to business. 132 ;;; down to business.
163 133
164 (defmacro make-tar-header (name mode uid git size date ck lt ln 134 (defmacro make-tar-header (name mode uid git size date ck lt ln
165 magic uname gname devmaj devmin) 135 magic uname gname devmaj devmin)
166 (list 'vector name mode uid git size date ck lt ln 136 (list 'vector name mode uid git size date ck lt ln
167 magic uname gname devmaj devmin)) 137 magic uname gname devmaj devmin))
168 138
169 (defmacro tar-header-name (x) (list 'aref x 0)) 139 (defmacro tar-header-name (x) (list 'aref x 0))
170 (defmacro tar-header-mode (x) (list 'aref x 1)) 140 (defmacro tar-header-mode (x) (list 'aref x 1))
206 176
207 (defun tokenize-tar-header-block (string) 177 (defun tokenize-tar-header-block (string)
208 "Returns a 'tar-header' structure (a list of name, mode, uid, gid, size, 178 "Returns a 'tar-header' structure (a list of name, mode, uid, gid, size,
209 write-date, checksum, link-type, and link-name)." 179 write-date, checksum, link-type, and link-name)."
210 (cond ((< (length string) 512) nil) 180 (cond ((< (length string) 512) nil)
211 (;(some 'plusp string) ; <-- oops, massive cycle hog! 181 ( ;(some 'plusp string) ; <-- oops, massive cycle hog!
212 (or (not (= 0 (aref string 0))) ; This will do. 182 (or (not (= 0 (aref string 0))) ; This will do.
213 (not (= 0 (aref string 101)))) 183 (not (= 0 (aref string 101))))
214 (let* ((name-end (1- tar-mode-offset)) 184 (let* ((name-end (1- tar-mode-offset))
215 (link-end (1- tar-magic-offset)) 185 (link-end (1- tar-magic-offset))
216 (uname-end (1- tar-gname-offset)) 186 (uname-end (1- tar-gname-offset))
228 link-p (if (or (= link-p 0) (= link-p ?0)) 198 link-p (if (or (= link-p 0) (= link-p ?0))
229 nil 199 nil
230 (- link-p ?0))) 200 (- link-p ?0)))
231 (if (and (null link-p) (string-match "/$" name)) (setq link-p 5)) ; directory 201 (if (and (null link-p) (string-match "/$" name)) (setq link-p 5)) ; directory
232 (make-tar-header 202 (make-tar-header
233 name 203 name
234 (tar-parse-octal-integer string tar-mode-offset (1- tar-uid-offset)) 204 (tar-parse-octal-integer string tar-mode-offset (1- tar-uid-offset))
235 (tar-parse-octal-integer string tar-uid-offset (1- tar-gid-offset)) 205 (tar-parse-octal-integer string tar-uid-offset (1- tar-gid-offset))
236 (tar-parse-octal-integer string tar-gid-offset (1- tar-size-offset)) 206 (tar-parse-octal-integer string tar-gid-offset (1- tar-size-offset))
237 (tar-parse-octal-integer string tar-size-offset (1- tar-time-offset)) 207 (tar-parse-octal-integer string tar-size-offset (1- tar-time-offset))
238 (tar-parse-octal-integer-32 string tar-time-offset (1- tar-chk-offset)) 208 (tar-parse-octal-integer-32 string tar-time-offset (1- tar-chk-offset))
239 (tar-parse-octal-integer string tar-chk-offset (1- tar-linkp-offset)) 209 (tar-parse-octal-integer string tar-chk-offset (1- tar-linkp-offset))
240 link-p 210 link-p
241 (substring string tar-link-offset link-end) 211 (substring string tar-link-offset link-end)
242 uname-valid-p 212 uname-valid-p
243 (and uname-valid-p (substring string tar-uname-offset uname-end)) 213 (and uname-valid-p (substring string tar-uname-offset uname-end))
244 (and uname-valid-p (substring string tar-gname-offset gname-end)) 214 (and uname-valid-p (substring string tar-gname-offset gname-end))
245 (tar-parse-octal-integer string tar-dmaj-offset (1- tar-dmin-offset)) 215 (tar-parse-octal-integer string tar-dmaj-offset (1- tar-dmin-offset))
246 (tar-parse-octal-integer string tar-dmin-offset (1- tar-end-offset)) 216 (tar-parse-octal-integer string tar-dmin-offset (1- tar-end-offset))
247 ))) 217 )))
248 (t 'empty-tar-block))) 218 (t 'empty-tar-block)))
249 219
250 220
251 (defun tar-parse-octal-integer (string &optional start end) 221 (defun tar-parse-octal-integer (string &optional start end)
252 "deletes all your files, and then reboots." 222 "deletes all your files, and then reboots."
273 (cons top bot))) 243 (cons top bot)))
274 244
275 (defun tar-parse-octal-integer-safe (string) 245 (defun tar-parse-octal-integer-safe (string)
276 (let ((L (length string))) 246 (let ((L (length string)))
277 (if (= L 0) (error "empty string")) 247 (if (= L 0) (error "empty string"))
278 (tar-dotimes (i L) 248 (dotimes (i L)
279 (if (or (< (aref string i) ?0) 249 (if (or (< (aref string i) ?0)
280 (> (aref string i) ?7)) 250 (> (aref string i) ?7))
281 (error "'%c' is not an octal digit.")))) 251 (error "'%c' is not an octal digit."))))
282 (tar-parse-octal-integer string)) 252 (tar-parse-octal-integer string))
283 253
284 254
285 (defun checksum-tar-header-block (string) 255 (defun checksum-tar-header-block (string)
286 "Computes and returns a tar-acceptable checksum for this block." 256 "Computes and returns a tar-acceptable checksum for this block."
309 (let* ((chk (checksum-tar-header-block hblock)) 279 (let* ((chk (checksum-tar-header-block hblock))
310 (chk-string (format "%6o" chk)) 280 (chk-string (format "%6o" chk))
311 (l (length chk-string))) 281 (l (length chk-string)))
312 (aset hblock 154 0) 282 (aset hblock 154 0)
313 (aset hblock 155 32) 283 (aset hblock 155 32)
314 (tar-dotimes (i l) (aset hblock (- 153 i) (aref chk-string (- l i 1))))) 284 (dotimes (i l) (aset hblock (- 153 i) (aref chk-string (- l i 1)))))
315 hblock) 285 hblock)
316 286
317 287
318 (defun tar-grind-file-mode (mode string start) 288 (defun tar-grind-file-mode (mode string start)
319 "Write a \"-rw--r--r-\" representing MODE into STRING beginning at START." 289 "Write a \"-rw--r--r-\" representing MODE into STRING beginning at START."
360 (string (make-string (+ namestart (length name) (if link-p (+ 5 (length link-name)) 0)) 32)) 330 (string (make-string (+ namestart (length name) (if link-p (+ 5 (length link-name)) 0)) 32))
361 (type (tar-header-link-type tar-hblock))) 331 (type (tar-header-link-type tar-hblock)))
362 (aset string 0 (if mod-p ?* ? )) 332 (aset string 0 (if mod-p ?* ? ))
363 (aset string 1 333 (aset string 1
364 (cond ((or (eq type nil) (eq type 0)) ?-) 334 (cond ((or (eq type nil) (eq type 0)) ?-)
365 ((eq type 1) ?l) ; link 335 ((eq type 1) ?l) ; link
366 ((eq type 2) ?s) ; symlink 336 ((eq type 2) ?s) ; symlink
367 ((eq type 3) ?c) ; char special 337 ((eq type 3) ?c) ; char special
368 ((eq type 4) ?b) ; block special 338 ((eq type 4) ?b) ; block special
369 ((eq type 5) ?d) ; directory 339 ((eq type 5) ?d) ; directory
370 ((eq type 6) ?p) ; FIFO/pipe 340 ((eq type 6) ?p) ; FIFO/pipe
371 ((eq type 20) ?*) ; directory listing 341 ((eq type 20) ?*) ; directory listing
372 ((eq type 29) ?M) ; multivolume continuation 342 ((eq type 29) ?M) ; multivolume continuation
373 ((eq type 35) ?S) ; sparse 343 ((eq type 35) ?S) ; sparse
374 ((eq type 38) ?V) ; volume header 344 ((eq type 38) ?V) ; volume header
375 )) 345 ))
376 (tar-grind-file-mode mode string 2) 346 (tar-grind-file-mode mode string 2)
377 (setq uid (if (= 0 (length uname)) (int-to-string uid) uname)) 347 (setq uid (if (= 0 (length uname)) (int-to-string uid) uname))
378 (setq gid (if (= 0 (length gname)) (int-to-string gid) gname)) 348 (setq gid (if (= 0 (length gname)) (int-to-string gid) gname))
379 (setq size (int-to-string size)) 349 (setq size (int-to-string size))
380 (tar-dotimes (i (min (1- namew) (length uid))) (aset string (- slash i) (aref uid (- (length uid) i 1)))) 350 (dotimes (i (min (1- namew) (length uid))) (aset string (- slash i) (aref uid (- (length uid) i 1))))
381 (aset string (1+ slash) ?/) 351 (aset string (1+ slash) ?/)
382 (tar-dotimes (i (min (1- groupw) (length gid))) (aset string (+ (+ slash 2) i) (aref gid i))) 352 (dotimes (i (min (1- groupw) (length gid))) (aset string (+ (+ slash 2) i) (aref gid i)))
383 (tar-dotimes (i (min sizew (length size))) (aset string (- lastdigit i) (aref size (- (length size) i 1)))) 353 (dotimes (i (min sizew (length size))) (aset string (- lastdigit i) (aref size (- (length size) i 1))))
384 354
385 (if tar-can-print-dates 355 (if tar-can-print-dates
386 (let* ((year (substring (current-time-string) -4)) 356 (let* ((year (substring (current-time-string) -4))
387 ;; in v18, current-time-string doesn't take an argument 357 ;; in v18, current-time-string doesn't take an argument
388 (file (current-time-string time)) 358 (file (current-time-string time))
389 (file-year (substring file -4)) 359 (file-year (substring file -4))
390 (str (if (equal year file-year) 360 (str (if (equal year file-year)
391 (substring file 4 16) 361 (substring file 4 16)
392 (concat (substring file 4 11) " " file-year)))) 362 (concat (substring file 4 11) " " file-year))))
393 (tar-dotimes (i 12) (aset string (- namestart (- 13 i)) (aref str i))))) 363 (dotimes (i 12) (aset string (- namestart (- 13 i)) (aref str i)))))
394 364
395 (tar-dotimes (i (length name)) (aset string (+ namestart i) (aref name i))) 365 (dotimes (i (length name)) (aset string (+ namestart i) (aref name i)))
396 (if (or (eq link-p 1) (eq link-p 2)) 366 (if (or (eq link-p 1) (eq link-p 2))
397 (progn 367 (progn
398 (tar-dotimes (i 3) (aset string (+ namestart 1 (length name) i) (aref (if (= link-p 1) "==>" "-->") i))) 368 (dotimes (i 3) (aset string (+ namestart 1 (length name) i) (aref (if (= link-p 1) "==>" "-->") i)))
399 (tar-dotimes (i (length link-name)) (aset string (+ namestart 5 (length name) i) (aref link-name i))))) 369 (dotimes (i (length link-name)) (aset string (+ namestart 5 (length name) i) (aref link-name i)))))
400 string))) 370 string)))
401 371
402 372
403 ;; buffer-local variables in the tar file's buffer: 373 ;; buffer-local variables in the tar file's buffer:
404 ;; 374 ;;
405 (defvar tar-parse-info) ; the header structures 375 (defvar tar-parse-info) ; the header structures
406 (defvar tar-header-offset) ; the end of the "pretty" data 376 (defvar tar-header-offset) ; the end of the "pretty" data
407 377
408 (defun tar-summarize-buffer () 378 (defun tar-summarize-buffer ()
409 "Parse the contents of the tar file in the current buffer, and place a 379 "Parse the contents of the tar file in the current buffer, and place a
410 dired-like listing on the front; then narrow to it, so that only that listing 380 dired-like listing on the front; then narrow to it, so that only that listing
411 is visible (and the real data of the buffer is hidden)." 381 is visible (and the real data of the buffer is hidden)."
412 (message "parsing tar file...") 382 (message "parsing tar file...")
413 (let* ((result '()) 383 (let* ((result '())
414 (pos 1) 384 (pos 1)
415 (bs (max 1 (- (buffer-size) 1024))) ; always 2+ empty blocks at end. 385 (bs (max 1 (- (buffer-size) 1024))) ; always 2+ empty blocks at end.
416 (bs100 (max 1 (/ bs 100))) 386 (bs100 (max 1 (/ bs 100)))
417 (tokens nil)) 387 (tokens nil))
418 (while (not (eq tokens 'empty-tar-block)) 388 (while (not (eq tokens 'empty-tar-block))
419 (if (> (+ pos 512) (point-max)) 389 (if (> (+ pos 512) (point-max))
420 (error "truncated tar file")) 390 (error "truncated tar file"))
421 (let* ((hblock (buffer-substring pos (+ pos 512)))) 391 (let* ((hblock (buffer-substring pos (+ pos 512))))
422 (setq tokens (tokenize-tar-header-block hblock)) 392 (setq tokens (tokenize-tar-header-block hblock))
423 (setq pos (+ pos 512)) 393 (setq pos (+ pos 512))
424 (message "parsing tar file...%s%%" 394 (message "parsing tar file...%s%%"
425 ;(/ (* pos 100) bs) ; this gets round-off lossage 395 ;(/ (* pos 100) bs) ; this gets round-off lossage
426 (/ pos bs100) ; this doesn't 396 (/ pos bs100) ; this doesn't
427 ) 397 )
428 (if (eq tokens 'empty-tar-block) 398 (if (eq tokens 'empty-tar-block)
429 nil 399 nil
430 (if (null tokens) (error "premature EOF parsing tar file.")) 400 (if (null tokens) (error "premature EOF parsing tar file."))
431 (if (eq (tar-header-link-type tokens) 20) 401 (if (eq (tar-header-link-type tokens) 20)
433 (setq pos (+ pos 512))) 403 (setq pos (+ pos 512)))
434 (let ((size (tar-header-size tokens))) 404 (let ((size (tar-header-size tokens)))
435 (if (< size 0) 405 (if (< size 0)
436 (error "%s has size %s - corrupted." 406 (error "%s has size %s - corrupted."
437 (tar-header-name tokens) size)) 407 (tar-header-name tokens) size))
438 ; 408 ;
439 ; This is just too slow. Don't really need it anyway.... 409 ; This is just too slow. Don't really need it anyway....
440 ;(check-tar-header-block-checksum 410 ;(check-tar-header-block-checksum
441 ; hblock (checksum-tar-header-block hblock) 411 ; hblock (checksum-tar-header-block hblock)
442 ; (tar-header-name tokens)) 412 ; (tar-header-name tokens))
443 413
444 (setq result (cons (make-tar-desc pos tokens) result)) 414 (setq result (cons (make-tar-desc pos tokens) result))
445 415
446 (if (and (null (tar-header-link-type tokens)) 416 (if (and (null (tar-header-link-type tokens))
447 (> size 0)) 417 (> size 0))
448 (setq pos 418 (setq pos
449 (+ pos 512 (ash (ash (1- size) -9) 9)) ; this works 419 (+ pos 512 (ash (ash (1- size) -9) 9)) ; this works
450 ;(+ pos (+ size (- 512 (rem (1- size) 512)))) ; this doesn't 420 ;(+ pos (+ size (- 512 (rem (1- size) 512)))) ; this doesn't
451 )) 421 ))
452 )))) 422 ))))
453 (make-local-variable 'tar-parse-info) 423 (make-local-variable 'tar-parse-info)
454 (setq tar-parse-info (nreverse result))) 424 (setq tar-parse-info (nreverse result)))
455 (message "parsing tar file...formatting...") 425 (message "parsing tar file...formatting...")
456 (save-excursion 426 (save-excursion
457 (goto-char (point-min)) 427 (goto-char (point-min))
458 (let ((buffer-read-only nil)) 428 (let ((buffer-read-only nil))
459 (tar-dolist (tar-desc tar-parse-info) 429 (dolist (tar-desc tar-parse-info)
460 (insert 430 (insert
461 (summarize-tar-header-block (tar-desc-tokens tar-desc)) 431 (summarize-tar-header-block (tar-desc-tokens tar-desc))
462 "\n")) 432 "\n"))
463 (make-local-variable 'tar-header-offset) 433 (make-local-variable 'tar-header-offset)
464 (setq tar-header-offset (point)) 434 (setq tar-header-offset (point))
465 (narrow-to-region 1 tar-header-offset) 435 (narrow-to-region 1 tar-header-offset)
466 (set-buffer-modified-p nil))) 436 (set-buffer-modified-p nil)))
467 (message "parsing tar file...done.")) 437 (message "parsing tar file...done."))
575 ;; this is not interactive because you shouldn't be turning this 545 ;; this is not interactive because you shouldn't be turning this
576 ;; mode on and off. You can corrupt things that way. 546 ;; mode on and off. You can corrupt things that way.
577 (make-local-variable 'tar-header-offset) 547 (make-local-variable 'tar-header-offset)
578 (make-local-variable 'tar-parse-info) 548 (make-local-variable 'tar-parse-info)
579 (make-local-variable 'require-final-newline) 549 (make-local-variable 'require-final-newline)
580 (setq require-final-newline nil) ; binary data, dude... 550 (setq require-final-newline nil) ; binary data, dude...
581 (make-local-variable 'revert-buffer-function) 551 (make-local-variable 'revert-buffer-function)
582 (setq revert-buffer-function 'tar-mode-revert) 552 (setq revert-buffer-function 'tar-mode-revert)
583 (setq major-mode 'tar-mode) 553 (setq major-mode 'tar-mode)
584 (setq mode-name "Tar") 554 (setq mode-name "Tar")
585 (use-local-map tar-mode-map) 555 (use-local-map tar-mode-map)
586 (auto-save-mode 0) 556 (auto-save-mode 0)
587 (widen) 557 (widen)
588 (if (and (boundp 'tar-header-offset) tar-header-offset) 558 (if (and (boundp 'tar-header-offset) tar-header-offset)
589 (narrow-to-region 1 tar-header-offset) 559 (narrow-to-region 1 tar-header-offset)
590 (tar-summarize-buffer)) 560 (tar-summarize-buffer))
591 561
592 (cond ((string-match "XEmacs" emacs-version) 562 (cond ((string-match "XEmacs" emacs-version)
593 (require 'mode-motion) 563 (require 'mode-motion)
594 (setq mode-motion-hook 'mode-motion-highlight-line) 564 (setq mode-motion-hook 'mode-motion-highlight-line)
595 (if (and current-menubar (not (assoc "Tar" current-menubar))) 565 (if (and current-menubar (not (assoc "Tar" current-menubar)))
621 (list '(tar-subfile-mode " TarFile"))))) 591 (list '(tar-subfile-mode " TarFile")))))
622 (make-local-variable 'tar-subfile-mode) 592 (make-local-variable 'tar-subfile-mode)
623 (setq tar-subfile-mode 593 (setq tar-subfile-mode
624 (if (null p) 594 (if (null p)
625 (not tar-subfile-mode) 595 (not tar-subfile-mode)
626 (> (prefix-numeric-value p) 0))) 596 (> (prefix-numeric-value p) 0)))
627 (cond (tar-subfile-mode 597 (cond (tar-subfile-mode
628 ;; copy the local keymap so that we don't accidentally 598 ;; copy the local keymap so that we don't accidentally
629 ;; alter a keymap like 'lisp-mode-map' which is shared 599 ;; alter a keymap like 'lisp-mode-map' which is shared
630 ;; by all buffers in that mode. 600 ;; by all buffers in that mode.
631 (let ((m (current-local-map))) 601 (let ((m (current-local-map)))
678 (or (nth (count-lines (point-min) 648 (or (nth (count-lines (point-min)
679 (save-excursion (beginning-of-line) (point))) 649 (save-excursion (beginning-of-line) (point)))
680 tar-parse-info) 650 tar-parse-info)
681 (if noerror 651 (if noerror
682 nil 652 nil
683 (error "This line does not describe a tar-file entry.")))) 653 (error "This line does not describe a tar-file entry."))))
684 654
685 655
686 (defun tar-extract (&optional other-window-p) 656 (defun tar-extract (&optional other-window-p)
687 "*In tar-mode, extract this entry of the tar file into its own buffer." 657 "*In tar-mode, extract this entry of the tar file into its own buffer."
688 (interactive) 658 (interactive)
703 ((eq link-p 38) "volume header") 673 ((eq link-p 38) "volume header")
704 (t "link")))) 674 (t "link"))))
705 (if (zerop size) (error "This is a zero-length file.")) 675 (if (zerop size) (error "This is a zero-length file."))
706 (let* ((tar-buffer (current-buffer)) 676 (let* ((tar-buffer (current-buffer))
707 (bufname (file-name-nondirectory name)) 677 (bufname (file-name-nondirectory name))
708 (bufid (concat ;" (" name " in " 678 (bufid (concat ;" (" name " in "
709 " (in " 679 " (in "
710 (file-name-nondirectory (buffer-file-name)) 680 (file-name-nondirectory (buffer-file-name))
711 ")")) 681 ")"))
712 (read-only-p (or buffer-read-only view-p)) 682 (read-only-p (or buffer-read-only view-p))
713 (buffer nil) 683 (buffer nil)
714 (buffers (buffer-list)) 684 (buffers (buffer-list))
715 (just-created nil)) 685 (just-created nil))
716 ;; find a buffer visiting this subfile from this tar file. 686 ;; find a buffer visiting this subfile from this tar file.
718 (set-buffer (car buffers)) 688 (set-buffer (car buffers))
719 (if (and (null (buffer-file-name (car buffers))) 689 (if (and (null (buffer-file-name (car buffers)))
720 (boundp 'superior-tar-descriptor) 690 (boundp 'superior-tar-descriptor)
721 (eq superior-tar-descriptor descriptor)) 691 (eq superior-tar-descriptor descriptor))
722 (setq buffer (car buffers)) 692 (setq buffer (car buffers))
723 (setq buffers (cdr buffers)))) 693 (setq buffers (cdr buffers))))
724 (set-buffer tar-buffer) 694 (set-buffer tar-buffer)
725 (if buffer 695 (if buffer
726 nil 696 nil
727 (setq buffer (generate-new-buffer bufname)) 697 (setq buffer (generate-new-buffer bufname))
728 (setq just-created t) 698 (setq just-created t)
733 (set-buffer buffer) 703 (set-buffer buffer)
734 (insert-buffer-substring tar-buffer start end) 704 (insert-buffer-substring tar-buffer start end)
735 (goto-char 0) 705 (goto-char 0)
736 (let ((lock-directory nil)) ; disable locking 706 (let ((lock-directory nil)) ; disable locking
737 (set-visited-file-name name) ; give it a name to decide mode. 707 (set-visited-file-name name) ; give it a name to decide mode.
738 ;; (normal-mode) ; pick a mode. 708 ;; (normal-mode) ; pick a mode.
739 ;; (after-find-file nil nil) ; pick a mode; works with crypt.el 709 ;; (after-find-file nil nil) ; pick a mode; works with crypt.el
740 ;; Ok, instead of running after-find-file, just invoke the 710 ;; Ok, instead of running after-find-file, just invoke the
741 ;; find-file-hooks instead. This does everything we want 711 ;; find-file-hooks instead. This does everything we want
742 ;; from after-find-file, without losing when visiting .tar 712 ;; from after-find-file, without losing when visiting .tar
743 ;; files via ange-ftp: doesn't probe the ftp site for the 713 ;; files via ange-ftp: doesn't probe the ftp site for the
744 ;; name of the subfile. 714 ;; name of the subfile.
821 default-file nil)))) 791 default-file nil))))
822 (if (or (string= "" (file-name-nondirectory target)) 792 (if (or (string= "" (file-name-nondirectory target))
823 (file-directory-p target)) 793 (file-directory-p target))
824 (setq target (concat (if (string-match "/$" target) 794 (setq target (concat (if (string-match "/$" target)
825 (substring target 0 (1- (match-end 0))) 795 (substring target 0 (1- (match-end 0)))
826 target) 796 target)
827 "/" 797 "/"
828 (file-name-nondirectory default-file)))) 798 (file-name-nondirectory default-file))))
829 target)) 799 target))
830 800
831 801
864 (defun tar-flag-deleted (p &optional unflag) 834 (defun tar-flag-deleted (p &optional unflag)
865 "*In tar mode, mark this sub-file to be deleted from the tar file. 835 "*In tar mode, mark this sub-file to be deleted from the tar file.
866 With a prefix argument, mark that many files." 836 With a prefix argument, mark that many files."
867 (interactive "p") 837 (interactive "p")
868 (beginning-of-line) 838 (beginning-of-line)
869 (tar-dotimes (i (if (< p 0) (- p) p)) 839 (dotimes (i (if (< p 0) (- p) p))
870 (if (tar-current-descriptor unflag) ; barf if we're not on an entry-line. 840 (if (tar-current-descriptor unflag) ; barf if we're not on an entry-line.
871 (progn 841 (progn
872 (delete-char 1) 842 (delete-char 1)
873 (insert (if unflag " " "D")))) 843 (insert (if unflag " " "D"))))
874 (forward-line (if (< p 0) -1 1))) 844 (forward-line (if (< p 0) -1 1)))
895 (name (tar-header-name tokens)) 865 (name (tar-header-name tokens))
896 (size (tar-header-size tokens)) 866 (size (tar-header-size tokens))
897 (link-p (tar-header-link-type tokens)) 867 (link-p (tar-header-link-type tokens))
898 (start (tar-desc-data-start descriptor)) 868 (start (tar-desc-data-start descriptor))
899 (following-descs (cdr (memq descriptor tar-parse-info)))) 869 (following-descs (cdr (memq descriptor tar-parse-info))))
900 (if link-p (setq size 0)) ; size lies for hard-links. 870 (if link-p (setq size 0)) ; size lies for hard-links.
901 ;; 871 ;;
902 ;; delete the current line... 872 ;; delete the current line...
903 (beginning-of-line) 873 (beginning-of-line)
904 (let ((line-start (point))) 874 (let ((line-start (point)))
905 (end-of-line) (forward-char) 875 (end-of-line) (forward-char)
922 ;; entries in the archive. This is a pig when deleting a bunch 892 ;; entries in the archive. This is a pig when deleting a bunch
923 ;; of files at once - we could optimize this to only do the 893 ;; of files at once - we could optimize this to only do the
924 ;; iteration over the files that remain, or only iterate up to 894 ;; iteration over the files that remain, or only iterate up to
925 ;; the next file to be deleted. 895 ;; the next file to be deleted.
926 (let ((data-length (- data-end data-start))) 896 (let ((data-length (- data-end data-start)))
927 (tar-dolist (desc following-descs) 897 (dolist (desc following-descs)
928 (tar-setf (tar-desc-data-start desc) 898 (setf (tar-desc-data-start desc)
929 (- (tar-desc-data-start desc) data-length)))) 899 (- (tar-desc-data-start desc) data-length))))
930 )) 900 ))
931 (narrow-to-region 1 tar-header-offset)) 901 (narrow-to-region 1 tar-header-offset))
932 902
933 903
934 (defun tar-expunge (&optional noconfirm) 904 (defun tar-expunge (&optional noconfirm)
943 (goto-char 0) 913 (goto-char 0)
944 (while (not (eobp)) 914 (while (not (eobp))
945 (if (looking-at "D") 915 (if (looking-at "D")
946 (progn (tar-expunge-internal) 916 (progn (tar-expunge-internal)
947 (setq n (1+ n))) 917 (setq n (1+ n)))
948 (forward-line 1))) 918 (forward-line 1)))
949 ;; after doing the deletions, add any padding that may be necessary. 919 ;; after doing the deletions, add any padding that may be necessary.
950 (tar-pad-to-blocksize) 920 (tar-pad-to-blocksize)
951 (narrow-to-region 1 tar-header-offset) 921 (narrow-to-region 1 tar-header-offset)
952 ) 922 )
953 (if (zerop n) 923 (if (zerop n)
954 (message "nothing to expunge.") 924 (message "nothing to expunge.")
955 (message "%s expunged. Be sure to save this buffer." n))))) 925 (message "%s expunged. Be sure to save this buffer." n)))))
956 926
957 927
958 (defun tar-clear-modification-flags () 928 (defun tar-clear-modification-flags ()
959 "remove the stars at the beginning of each line." 929 "remove the stars at the beginning of each line."
960 (save-excursion 930 (save-excursion
971 the user id as a string; otherwise, you must edit it as a number. 941 the user id as a string; otherwise, you must edit it as a number.
972 You can force editing as a number by calling this with a prefix arg. 942 You can force editing as a number by calling this with a prefix arg.
973 This does not modify the disk image; you must save the tar file itself 943 This does not modify the disk image; you must save the tar file itself
974 for this to be permanent." 944 for this to be permanent."
975 (interactive (list 945 (interactive (list
976 (let ((tokens (tar-desc-tokens (tar-current-descriptor)))) 946 (let ((tokens (tar-desc-tokens (tar-current-descriptor))))
977 (if (or current-prefix-arg 947 (if (or current-prefix-arg
978 (not (tar-header-magic tokens))) 948 (not (tar-header-magic tokens)))
979 (let (n) 949 (let (n)
980 (while (not (numberp (setq n (read-minibuffer 950 (while (not (numberp (setq n (read-minibuffer
981 "New UID number: " 951 "New UID number: "
982 (format "%s" (tar-header-uid tokens))))))) 952 (format "%s" (tar-header-uid tokens)))))))
983 n) 953 n)
984 (read-string "New UID string: " (tar-header-uname tokens)))))) 954 (read-string "New UID string: " (tar-header-uname tokens))))))
985 (cond ((stringp new-uid) 955 (cond ((stringp new-uid)
986 (tar-setf (tar-header-uname (tar-desc-tokens (tar-current-descriptor))) 956 (setf (tar-header-uname (tar-desc-tokens (tar-current-descriptor)))
987 new-uid) 957 new-uid)
988 (tar-alter-one-field tar-uname-offset (concat new-uid "\000"))) 958 (tar-alter-one-field tar-uname-offset (concat new-uid "\000")))
989 (t 959 (t
990 (tar-setf (tar-header-uid (tar-desc-tokens (tar-current-descriptor))) 960 (setf (tar-header-uid (tar-desc-tokens (tar-current-descriptor)))
991 new-uid) 961 new-uid)
992 (tar-alter-one-field tar-uid-offset 962 (tar-alter-one-field tar-uid-offset
993 (concat (substring (format "%6o" new-uid) 0 6) "\000 "))))) 963 (concat (substring (format "%6o" new-uid) 0 6) "\000 ")))))
994 964
995 965
996 (defun tar-chgrp-entry (new-gid) 966 (defun tar-chgrp-entry (new-gid)
997 "*Change the group-id associated with this entry in the tar file. 967 "*Change the group-id associated with this entry in the tar file.
998 If this tar file was written by GNU tar, then you will be able to edit 968 If this tar file was written by GNU tar, then you will be able to edit
999 the group id as a string; otherwise, you must edit it as a number. 969 the group id as a string; otherwise, you must edit it as a number.
1000 You can force editing as a number by calling this with a prefix arg. 970 You can force editing as a number by calling this with a prefix arg.
1001 This does not modify the disk image; you must save the tar file itself 971 This does not modify the disk image; you must save the tar file itself
1002 for this to be permanent." 972 for this to be permanent."
1003 (interactive (list 973 (interactive (list
1004 (let ((tokens (tar-desc-tokens (tar-current-descriptor)))) 974 (let ((tokens (tar-desc-tokens (tar-current-descriptor))))
1005 (if (or current-prefix-arg 975 (if (or current-prefix-arg
1006 (not (tar-header-magic tokens))) 976 (not (tar-header-magic tokens)))
1007 (let (n) 977 (let (n)
1008 (while (not (numberp (setq n (read-minibuffer 978 (while (not (numberp (setq n (read-minibuffer
1009 "New GID number: " 979 "New GID number: "
1010 (format "%s" (tar-header-gid tokens))))))) 980 (format "%s" (tar-header-gid tokens)))))))
1011 n) 981 n)
1012 (read-string "New GID string: " (tar-header-gname tokens)))))) 982 (read-string "New GID string: " (tar-header-gname tokens))))))
1013 (cond ((stringp new-gid) 983 (cond ((stringp new-gid)
1014 (tar-setf (tar-header-gname (tar-desc-tokens (tar-current-descriptor))) 984 (setf (tar-header-gname (tar-desc-tokens (tar-current-descriptor)))
1015 new-gid) 985 new-gid)
1016 (tar-alter-one-field tar-gname-offset 986 (tar-alter-one-field tar-gname-offset
1017 (concat new-gid "\000"))) 987 (concat new-gid "\000")))
1018 (t 988 (t
1019 (tar-setf (tar-header-gid (tar-desc-tokens (tar-current-descriptor))) 989 (setf (tar-header-gid (tar-desc-tokens (tar-current-descriptor)))
1020 new-gid) 990 new-gid)
1021 (tar-alter-one-field tar-gid-offset 991 (tar-alter-one-field tar-gid-offset
1022 (concat (substring (format "%6o" new-gid) 0 6) "\000 "))))) 992 (concat (substring (format "%6o" new-gid) 0 6) "\000 ")))))
1023 993
1024 (defun tar-rename-entry (new-name) 994 (defun tar-rename-entry (new-name)
1025 "*Change the name associated with this entry in the tar file. 995 "*Change the name associated with this entry in the tar file.
1026 This does not modify the disk image; you must save the tar file itself 996 This does not modify the disk image; you must save the tar file itself
1027 for this to be permanent." 997 for this to be permanent."
1028 (interactive 998 (interactive
1029 (list (read-string "New name: " 999 (list (read-string "New name: "
1030 (tar-header-name (tar-desc-tokens (tar-current-descriptor)))))) 1000 (tar-header-name (tar-desc-tokens (tar-current-descriptor))))))
1031 (if (string= "" new-name) (error "zero length name.")) 1001 (if (string= "" new-name) (error "zero length name."))
1032 (if (> (length new-name) 98) (error "name too long.")) 1002 (if (> (length new-name) 98) (error "name too long."))
1033 (tar-setf (tar-header-name (tar-desc-tokens (tar-current-descriptor))) 1003 (setf (tar-header-name (tar-desc-tokens (tar-current-descriptor)))
1034 new-name) 1004 new-name)
1035 (tar-alter-one-field 0 1005 (tar-alter-one-field 0
1036 (substring (concat new-name (make-string 99 0)) 0 99))) 1006 (substring (concat new-name (make-string 99 0)) 0 99)))
1037 1007
1038 1008
1039 (defun tar-chmod-entry (new-mode) 1009 (defun tar-chmod-entry (new-mode)
1040 "*Change the protection bits associated with this entry in the tar file. 1010 "*Change the protection bits associated with this entry in the tar file.
1041 This does not modify the disk image; you must save the tar file itself 1011 This does not modify the disk image; you must save the tar file itself
1042 for this to be permanent." 1012 for this to be permanent."
1043 (interactive (list (tar-parse-octal-integer-safe 1013 (interactive (list (tar-parse-octal-integer-safe
1044 (read-string "New protection (octal): ")))) 1014 (read-string "New protection (octal): "))))
1045 (tar-setf (tar-header-mode (tar-desc-tokens (tar-current-descriptor))) 1015 (setf (tar-header-mode (tar-desc-tokens (tar-current-descriptor)))
1046 new-mode) 1016 new-mode)
1047 (tar-alter-one-field tar-mode-offset 1017 (tar-alter-one-field tar-mode-offset
1048 (concat (substring (format "%6o" new-mode) 0 6) "\000 "))) 1018 (concat (substring (format "%6o" new-mode) 0 6) "\000 ")))
1049 1019
1050 1020
1051 (defun tar-alter-one-field (data-position new-data-string) 1021 (defun tar-alter-one-field (data-position new-data-string)
1052 (let* ((descriptor (tar-current-descriptor)) 1022 (let* ((descriptor (tar-current-descriptor))
1053 (tokens (tar-desc-tokens descriptor))) 1023 (tokens (tar-desc-tokens descriptor)))
1066 (let* ((start (+ (tar-desc-data-start descriptor) tar-header-offset -513))) 1036 (let* ((start (+ (tar-desc-data-start descriptor) tar-header-offset -513)))
1067 ;; 1037 ;;
1068 ;; delete the old field and insert a new one. 1038 ;; delete the old field and insert a new one.
1069 (goto-char (+ start data-position)) 1039 (goto-char (+ start data-position))
1070 (delete-region (point) (+ (point) (length new-data-string))) ; <-- 1040 (delete-region (point) (+ (point) (length new-data-string))) ; <--
1071 (insert new-data-string) ; <-- 1041 (insert new-data-string) ; <--
1072 ;; 1042 ;;
1073 ;; compute a new checksum and insert it. 1043 ;; compute a new checksum and insert it.
1074 (let ((chk (checksum-tar-header-block 1044 (let ((chk (checksum-tar-header-block
1075 (buffer-substring start (+ start 512))))) 1045 (buffer-substring start (+ start 512)))))
1076 (goto-char (+ start tar-chk-offset)) 1046 (goto-char (+ start tar-chk-offset))
1077 (delete-region (point) (+ (point) 8)) 1047 (delete-region (point) (+ (point) 8))
1078 (insert (format "%6o" chk)) 1048 (insert (format "%6o" chk))
1079 (insert 0) 1049 (insert 0)
1080 (insert ? ) 1050 (insert ? )
1081 (tar-setf (tar-header-checksum tokens) chk) 1051 (setf (tar-header-checksum tokens) chk)
1082 ;; 1052 ;;
1083 ;; ok, make sure we didn't botch it. 1053 ;; ok, make sure we didn't botch it.
1084 (check-tar-header-block-checksum 1054 (check-tar-header-block-checksum
1085 (buffer-substring start (+ start 512)) 1055 (buffer-substring start (+ start 512))
1086 chk (tar-header-name tokens)) 1056 chk (tar-header-name tokens))
1087 ))) 1057 )))
1088 (narrow-to-region 1 tar-header-offset)))) 1058 (narrow-to-region 1 tar-header-offset))))
1089 1059
1090 1060
1091 (defun tar-subfile-save-buffer () 1061 (defun tar-subfile-save-buffer ()
1113 ;; Notice when crypt.el has uncompressed while reading the subfile, and 1083 ;; Notice when crypt.el has uncompressed while reading the subfile, and
1114 ;; signal an error if the user tries to save back into the parent file 1084 ;; signal an error if the user tries to save back into the parent file
1115 ;; (because it won't work - the .Z subfile it writes won't really be 1085 ;; (because it won't work - the .Z subfile it writes won't really be
1116 ;; compressed.) 1086 ;; compressed.)
1117 ;; 1087 ;;
1118 ; ;; These are for the old crypt.el 1088 ; ;; These are for the old crypt.el
1119 ; (if (and (boundp 'buffer-save-encrypted) buffer-save-encrypted) 1089 ; (if (and (boundp 'buffer-save-encrypted) buffer-save-encrypted)
1120 ; (error "Don't know how to encrypt back into a tar file.")) 1090 ; (error "Don't know how to encrypt back into a tar file."))
1121 ; (if (and (boundp 'buffer-save-compacted) buffer-save-compacted) 1091 ; (if (and (boundp 'buffer-save-compacted) buffer-save-compacted)
1122 ; (error "Don't know how to compact back into a tar file.")) 1092 ; (error "Don't know how to compact back into a tar file."))
1123 ; (if (and (boundp 'buffer-save-compressed) buffer-save-compressed) 1093 ; (if (and (boundp 'buffer-save-compressed) buffer-save-compressed)
1124 ; (error "Don't know how to compress back into a tar file.")) 1094 ; (error "Don't know how to compress back into a tar file."))
1125 ; (if (and (boundp 'buffer-save-gzipped) buffer-save-gzipped) 1095 ; (if (and (boundp 'buffer-save-gzipped) buffer-save-gzipped)
1126 ; (error "Don't know how to gzip back into a tar file.")) 1096 ; (error "Don't know how to gzip back into a tar file."))
1127 1097
1128 ;; These are for the new crypt++.el 1098 ;; These are for the new crypt++.el
1129 (if (and (boundp 'crypt-buffer-save-encrypted) crypt-buffer-save-encrypted) 1099 (if (and (boundp 'crypt-buffer-save-encrypted) crypt-buffer-save-encrypted)
1130 (error "Don't know how to encrypt back into a tar file.")) 1100 (error "Don't know how to encrypt back into a tar file."))
1131 (if (and (boundp 'crypt-buffer-save-compact) crypt-buffer-save-compact) 1101 (if (and (boundp 'crypt-buffer-save-compact) crypt-buffer-save-compact)
1136 (error "Don't know how to gzip back into a tar file.")) 1106 (error "Don't know how to gzip back into a tar file."))
1137 (if (and (boundp 'crypt-buffer-save-freeze) crypt-buffer-save-freeze) 1107 (if (and (boundp 'crypt-buffer-save-freeze) crypt-buffer-save-freeze)
1138 (error "Don't know how to freeze back into a tar file.")) 1108 (error "Don't know how to freeze back into a tar file."))
1139 1109
1140 (save-excursion 1110 (save-excursion
1141 (let ((subfile (current-buffer)) 1111 (let ((subfile (current-buffer))
1142 (subfile-size (buffer-size)) 1112 (subfile-size (buffer-size))
1143 (descriptor superior-tar-descriptor)) 1113 (descriptor superior-tar-descriptor))
1144 (set-buffer superior-tar-buffer) 1114 (set-buffer superior-tar-buffer)
1145 (let* ((tokens (tar-desc-tokens descriptor)) 1115 (let* ((tokens (tar-desc-tokens descriptor))
1146 (start (tar-desc-data-start descriptor)) 1116 (start (tar-desc-data-start descriptor))
1147 (name (tar-header-name tokens)) 1117 (name (tar-header-name tokens))
1148 (size (tar-header-size tokens)) 1118 (size (tar-header-size tokens))
1149 (size-pad (ash (ash (+ size 511) -9) 9)) 1119 (size-pad (ash (ash (+ size 511) -9) 9))
1150 (head (memq descriptor tar-parse-info)) 1120 (head (memq descriptor tar-parse-info))
1151 (following-descs (cdr head))) 1121 (following-descs (cdr head)))
1152 (if (not head) 1122 (if (not head)
1153 (error "Can't find this tar file entry in its parent tar file!")) 1123 (error "Can't find this tar file entry in its parent tar file!"))
1154 (unwind-protect 1124 (unwind-protect
1155 (save-excursion 1125 (save-excursion
1156 (widen) 1126 (widen)
1157 ;; delete the old data... 1127 ;; delete the old data...
1158 (let* ((data-start (+ start tar-header-offset -1)) 1128 (let* ((data-start (+ start tar-header-offset -1))
1159 (data-end (+ data-start (ash (ash (+ size 511) -9) 9)))) 1129 (data-end (+ data-start (ash (ash (+ size 511) -9) 9))))
1160 (delete-region data-start data-end) 1130 (delete-region data-start data-end)
1161 ;; insert the new data... 1131 ;; insert the new data...
1162 (goto-char data-start) 1132 (goto-char data-start)
1163 (insert-buffer subfile) 1133 (insert-buffer subfile)
1164 ;; 1134 ;;
1165 ;; pad the new data out to a multiple of 512... 1135 ;; pad the new data out to a multiple of 512...
1166 (let ((subfile-size-pad (ash (ash (+ subfile-size 511) -9) 9))) 1136 (let ((subfile-size-pad (ash (ash (+ subfile-size 511) -9) 9)))
1167 (goto-char (+ data-start subfile-size)) 1137 (goto-char (+ data-start subfile-size))
1168 (insert (make-string (- subfile-size-pad subfile-size) 0)) 1138 (insert (make-string (- subfile-size-pad subfile-size) 0))
1169 ;; 1139 ;;
1170 ;; update the data pointer of this and all following files... 1140 ;; update the data pointer of this and all following files...
1171 (tar-setf (tar-header-size tokens) subfile-size) 1141 (setf (tar-header-size tokens) subfile-size)
1172 (let ((difference (- subfile-size-pad size-pad))) 1142 (let ((difference (- subfile-size-pad size-pad)))
1173 (tar-dolist (desc following-descs) 1143 (dolist (desc following-descs)
1174 (tar-setf (tar-desc-data-start desc) 1144 (setf (tar-desc-data-start desc)
1175 (+ (tar-desc-data-start desc) difference)))) 1145 (+ (tar-desc-data-start desc) difference))))
1176 ;; 1146 ;;
1177 ;; Update the size field in the header block. 1147 ;; Update the size field in the header block.
1178 (let ((header-start (- data-start 512))) 1148 (let ((header-start (- data-start 512)))
1179 (goto-char (+ header-start tar-size-offset)) 1149 (goto-char (+ header-start tar-size-offset))
1180 (delete-region (point) (+ (point) 12)) 1150 (delete-region (point) (+ (point) 12))
1181 (insert (format "%11o" subfile-size)) 1151 (insert (format "%11o" subfile-size))
1182 (insert ? ) 1152 (insert ? )
1183 ;; 1153 ;;
1184 ;; Maybe update the datestamp. 1154 ;; Maybe update the datestamp.
1185 (if (not tar-update-datestamp) 1155 (if (not tar-update-datestamp)
1186 nil 1156 nil
1187 (goto-char (+ header-start tar-time-offset)) 1157 (goto-char (+ header-start tar-time-offset))
1188 (delete-region (point) (+ (point) 12)) 1158 (delete-region (point) (+ (point) 12))
1189 (let (now top bot) 1159 (let (now top bot)
1190 (cond ((fboundp 'current-time) 1160 (cond ((fboundp 'current-time)
1191 (setq now (current-time)) 1161 (setq now (current-time))
1192 (setcdr now (car (cdr now)))) 1162 (setcdr now (car (cdr now))))
1193 ; ((fboundp 'current-time-seconds) 1163 ; ((fboundp 'current-time-seconds)
1194 ; (setq now (current-time-seconds))) 1164 ; (setq now (current-time-seconds)))
1195 ) 1165 )
1196 (setq top (car now) 1166 (setq top (car now)
1197 bot (cdr now)) 1167 bot (cdr now))
1198 (cond 1168 (cond
1199 (now 1169 (now
1200 (tar-setf (tar-header-date tokens) now) 1170 (setf (tar-header-date tokens) now)
1201 ;; hair to print two 16-bit numbers as one octal number. 1171 ;; hair to print two 16-bit numbers as one octal number.
1202 (setq bot (logior (ash (logand top 3) 16) bot)) 1172 (setq bot (logior (ash (logand top 3) 16) bot))
1203 (setq top (ash top -2)) 1173 (setq top (ash top -2))
1204 (insert (format "%5o" top)) 1174 (insert (format "%5o" top))
1205 (insert (format "%06o " bot))) 1175 (insert (format "%06o " bot)))
1206 (t 1176 (t
1207 ;; otherwise, set it to the epoch. 1177 ;; otherwise, set it to the epoch.
1208 (insert (format "%11o " 0)) 1178 (insert (format "%11o " 0))
1209 (tar-setf (tar-header-date tokens) (cons 0 0)) 1179 (setf (tar-header-date tokens) (cons 0 0))
1210 )))) 1180 ))))
1211 ;; 1181 ;;
1212 ;; compute a new checksum and insert it. 1182 ;; compute a new checksum and insert it.
1213 (let ((chk (checksum-tar-header-block 1183 (let ((chk (checksum-tar-header-block
1214 (buffer-substring header-start data-start)))) 1184 (buffer-substring header-start data-start))))
1215 (goto-char (+ header-start tar-chk-offset)) 1185 (goto-char (+ header-start tar-chk-offset))
1216 (delete-region (point) (+ (point) 8)) 1186 (delete-region (point) (+ (point) 8))
1217 (insert (format "%6o" chk)) 1187 (insert (format "%6o" chk))
1218 (insert 0) 1188 (insert 0)
1219 (insert ? ) 1189 (insert ? )
1220 (tar-setf (tar-header-checksum tokens) chk))) 1190 (setf (tar-header-checksum tokens) chk)))
1221 ;; 1191 ;;
1222 ;; alter the descriptor-line... 1192 ;; alter the descriptor-line...
1223 ;; 1193 ;;
1224 (let ((position (- (length tar-parse-info) (length head)))) 1194 (let ((position (- (length tar-parse-info) (length head))))
1225 (goto-char 1) 1195 (goto-char 1)
1226 (next-line position) 1196 (next-line position)
1227 (beginning-of-line) 1197 (beginning-of-line)
1228 (let ((p (point)) 1198 (let ((p (point))
1229 (m (set-marker (make-marker) tar-header-offset))) 1199 (m (set-marker (make-marker) tar-header-offset)))
1230 (forward-line 1) 1200 (forward-line 1)
1231 (delete-region p (point)) 1201 (delete-region p (point))
1232 (insert-before-markers (summarize-tar-header-block tokens t) "\n") 1202 (insert-before-markers (summarize-tar-header-block tokens t) "\n")
1233 (setq tar-header-offset (marker-position m))) 1203 (setq tar-header-offset (marker-position m)))
1234 ))) 1204 )))
1235 ;; after doing the insertion, add any final padding that may be necessary. 1205 ;; after doing the insertion, add any final padding that may be necessary.
1236 (tar-pad-to-blocksize)) 1206 (tar-pad-to-blocksize))
1237 (narrow-to-region 1 tar-header-offset))) 1207 (narrow-to-region 1 tar-header-offset)))
1238 (set-buffer-modified-p t) ; mark the tar file as modified 1208 (set-buffer-modified-p t) ; mark the tar file as modified
1239 (set-buffer subfile) 1209 (set-buffer subfile)
1240 (set-buffer-modified-p nil) ; mark the tar subfile as unmodified 1210 (set-buffer-modified-p nil) ; mark the tar subfile as unmodified
1241 (message "saved into tar-buffer \"%s\" - remember to save that buffer!" 1211 (message "saved into tar-buffer \"%s\" - remember to save that buffer!"
1242 (buffer-name superior-tar-buffer)) 1212 (buffer-name superior-tar-buffer))
1243 ))) 1213 )))
1244 1214
1245 1215
1246 (defun tar-pad-to-blocksize () 1216 (defun tar-pad-to-blocksize ()
1247 "If we are being anal about tar file blocksizes, fix up the current buffer. 1217 "If we are being anal about tar file blocksizes, fix up the current buffer.
1248 Leaves the region wide." 1218 Leaves the region wide."
1255 (link-p (tar-header-link-type tokens)) 1225 (link-p (tar-header-link-type tokens))
1256 (size (if link-p 0 (tar-header-size tokens))) 1226 (size (if link-p 0 (tar-header-size tokens)))
1257 (data-end (+ start size)) 1227 (data-end (+ start size))
1258 (bbytes (ash tar-anal-blocksize 9)) 1228 (bbytes (ash tar-anal-blocksize 9))
1259 (pad-to (+ bbytes (* bbytes (/ (1- data-end) bbytes)))) 1229 (pad-to (+ bbytes (* bbytes (/ (1- data-end) bbytes))))
1260 (buffer-read-only nil) ; ## 1230 (buffer-read-only nil) ; ##
1261 ) 1231 )
1262 ;; If the padding after the last data is too long, delete some; 1232 ;; If the padding after the last data is too long, delete some;
1263 ;; else insert some until we are padded out to the right number of blocks. 1233 ;; else insert some until we are padded out to the right number of blocks.
1264 ;; 1234 ;;
1265 (goto-char (+ (or tar-header-offset 0) data-end)) 1235 (goto-char (+ (or tar-header-offset 0) data-end))
1266 (if (> (1+ (buffer-size)) (+ (or tar-header-offset 0) pad-to)) 1236 (if (> (1+ (buffer-size)) (+ (or tar-header-offset 0) pad-to))
1267 (delete-region (+ (or tar-header-offset 0) pad-to) (1+ (buffer-size))) 1237 (delete-region (+ (or tar-header-offset 0) pad-to) (1+ (buffer-size)))
1268 (insert (make-string (- (+ (or tar-header-offset 0) pad-to) 1238 (insert (make-string (- (+ (or tar-header-offset 0) pad-to)
1269 (1+ (buffer-size))) 1239 (1+ (buffer-size)))
1270 0))) 1240 0)))
1271 ))) 1241 )))
1272 1242
1273 1243
1274 (defun maybe-write-tar-file () 1244 (defun maybe-write-tar-file ()
1275 "Used as a write-file-hook to write tar-files out correctly." 1245 "Used as a write-file-hook to write tar-files out correctly."
1300 write-file-hooks))) 1270 write-file-hooks)))
1301 (remaining-hooks (cdr (memq 'maybe-write-tar-file hooks))) 1271 (remaining-hooks (cdr (memq 'maybe-write-tar-file hooks)))
1302 header-string 1272 header-string
1303 done) 1273 done)
1304 (save-excursion 1274 (save-excursion
1305 (save-restriction 1275 (save-restriction
1306 (widen) 1276 (widen)
1307 (tar-clear-modification-flags) 1277 (tar-clear-modification-flags)
1308 (setq header-string (buffer-substring 1 tar-header-offset)) 1278 (setq header-string (buffer-substring 1 tar-header-offset))
1309 (delete-region 1 tar-header-offset) 1279 (delete-region 1 tar-header-offset)
1310 (unwind-protect 1280 (unwind-protect
1311 (progn 1281 (progn
1312 (while (and remaining-hooks 1282 (while (and remaining-hooks
1313 (not (setq done (funcall (car remaining-hooks))))) 1283 (not (setq done (funcall (car remaining-hooks)))))
1314 (setq remaining-hooks (cdr remaining-hooks))) 1284 (setq remaining-hooks (cdr remaining-hooks)))
1315 (cond ((not done) 1285 (cond ((not done)
1316 (write-region 1 (1+ (buffer-size)) 1286 (write-region 1 (1+ (buffer-size))
1317 buffer-file-name nil t) 1287 buffer-file-name nil t)
1318 (setq done t)))) 1288 (setq done t))))
1319 (goto-char 1) 1289 (goto-char 1)
1320 (insert header-string) 1290 (insert header-string)
1321 (set-buffer-modified-p nil)))) 1291 (set-buffer-modified-p nil))))
1322 done))) 1292 done)))
1323 1293
1324 1294
1325 ;;; Patch it in. 1295 ;;; Patch it in.
1326 1296
1332 1302
1333 ;; Note: the tar write-file-hook should go on the list *before* any other 1303 ;; Note: the tar write-file-hook should go on the list *before* any other
1334 ;; hooks which might write the file. Since things like crypt-mode add things 1304 ;; hooks which might write the file. Since things like crypt-mode add things
1335 ;; to the end of the write-file-hooks, this will normally be the case. 1305 ;; to the end of the write-file-hooks, this will normally be the case.
1336 1306
1337 ;(or (boundp 'write-file-hooks) (setq write-file-hooks nil)) 1307 ;(or (boundp 'write-file-hooks) (setq write-file-hooks nil))
1338 ;(or (listp write-file-hooks) 1308 ;(or (listp write-file-hooks)
1339 ; (setq write-file-hooks (list write-file-hooks))) 1309 ; (setq write-file-hooks (list write-file-hooks)))
1340 ;(or (memq 'maybe-write-tar-file write-file-hooks) 1310 ;(or (memq 'maybe-write-tar-file write-file-hooks)
1341 ; (setq write-file-hooks 1311 ; (setq write-file-hooks
1342 ; (cons 'maybe-write-tar-file write-file-hooks))) 1312 ; (cons 'maybe-write-tar-file write-file-hooks)))
1343 1313
1344 (add-hook 'write-file-hooks 'maybe-write-tar-file); ####write-contents-hooks?? 1314 (add-hook 'write-file-hooks 'maybe-write-tar-file) ; ####write-contents-hooks??
1345 (cond ((boundp 'after-save-hook) 1315 (cond ((boundp 'after-save-hook)
1346 (add-hook 'after-save-hook 'tar-subfile-after-write-file-hook)) 1316 (add-hook 'after-save-hook 'tar-subfile-after-write-file-hook))
1347 ((boundp 'after-write-file-hooks) 1317 ((boundp 'after-write-file-hooks)
1348 (add-hook 'after-write-file-hooks 'tar-subfile-after-write-file-hook)) 1318 (add-hook 'after-write-file-hooks 'tar-subfile-after-write-file-hook))
1349 (t (error "neither after-save-hook nor after-write-file-hooks?"))) 1319 (t (error "neither after-save-hook nor after-write-file-hooks?")))
1371 itself." 1341 itself."
1372 (interactive) 1342 (interactive)
1373 (if (and buffer-file-name 1343 (if (and buffer-file-name
1374 (string-match tar-regexp buffer-file-name)) 1344 (string-match tar-regexp buffer-file-name))
1375 (tar-mode) 1345 (tar-mode)
1376 (tar-real-normal-mode find-file))) 1346 (tar-real-normal-mode find-file)))
1377 1347
1378 ;; We have to shadow this as well to get along with crypt.el. 1348 ;; We have to shadow this as well to get along with crypt.el.
1379 ;; Shadowing this alone isn't enough, though; we need to shadow 1349 ;; Shadowing this alone isn't enough, though; we need to shadow
1380 ;; tar-normal-mode in order to inhibit the local variables of the 1350 ;; tar-normal-mode in order to inhibit the local variables of the
1381 ;; last file in the tar archive. 1351 ;; last file in the tar archive.
1393 itself." 1363 itself."
1394 (interactive) 1364 (interactive)
1395 (if (and buffer-file-name 1365 (if (and buffer-file-name
1396 (string-match tar-regexp buffer-file-name)) 1366 (string-match tar-regexp buffer-file-name))
1397 (tar-mode) 1367 (tar-mode)
1398 (tar-real-set-auto-mode))) 1368 (tar-real-set-auto-mode)))
1399 1369
1400 (if (not (fboundp 'tar-real-normal-mode)) 1370 (if (not (fboundp 'tar-real-normal-mode))
1401 (fset 'tar-real-normal-mode (symbol-function 'normal-mode))) 1371 (fset 'tar-real-normal-mode (symbol-function 'normal-mode)))
1402 (fset 'normal-mode 'tar-normal-mode) 1372 (fset 'normal-mode 'tar-normal-mode)
1403 1373