X-Git-Url: http://cgit.sxemacs.org/?a=blobdiff_plain;f=lisp%2Fnnmaildir.el;h=c1abf286e48e562f5c784c37d5668da3c3029259;hb=6cd2123aa7862c039847f216cc65b5275ffbd64c;hp=8b26fb7393ca170ff2074295aad1c27deb8a5629;hpb=d21975f2b289b49b3cd151257227bb0ed30ecba6;p=gnus diff --git a/lisp/nnmaildir.el b/lisp/nnmaildir.el index 8b26fb739..c1abf286e 100644 --- a/lisp/nnmaildir.el +++ b/lisp/nnmaildir.el @@ -41,10 +41,10 @@ ;; copying, restoring, etc. ;; ;; Todo: +;; * When moving an article for expiry, copy all the marks except 'expire +;; from the original article. ;; * Add a hook for when moving messages from new/ to cur/, to support ;; nnmail's duplicate detection. -;; * Allow each mark directory in a group to have its own inode for mark -;; files, to accommodate AFS. ;; * Improve generated Xrefs, so crossposts are detectable. ;; * Improve code readability. @@ -56,6 +56,7 @@ (put 'nnmaildir--with-work-buffer 'lisp-indent-function 0) (put 'nnmaildir--with-nov-buffer 'lisp-indent-function 0) (put 'nnmaildir--with-move-buffer 'lisp-indent-function 0) + (put 'nnmaildir--condcase 'lisp-indent-function 2) ) ] @@ -231,7 +232,6 @@ by nnmaildir-request-article.") (defmacro nnmaildir--nov-dir (dir) `(nnmaildir--subdir ,dir "nov")) (defmacro nnmaildir--marks-dir (dir) `(nnmaildir--subdir ,dir "marks")) (defmacro nnmaildir--num-dir (dir) `(nnmaildir--subdir ,dir "num")) -(defmacro nnmaildir--num-file (dir) `(concat ,dir ":")) (defmacro nnmaildir--unlink (file-arg) `(let ((file ,file-arg)) @@ -239,20 +239,36 @@ by nnmaildir-request-article.") (defun nnmaildir--mkdir (dir) (or (file-exists-p (file-name-as-directory dir)) (make-directory-internal (directory-file-name dir)))) +(defun nnmaildir--mkfile (file) + (write-region "" nil file nil 'no-message)) (defun nnmaildir--delete-dir-files (dir ls) (when (file-attributes dir) (mapcar 'delete-file (funcall ls dir 'full "\\`[^.]" 'nosort)) (delete-directory dir))) (defun nnmaildir--group-maxnum (server group) - (if (zerop (nnmaildir--grp-count group)) 0 - (let ((x (nnmaildir--srvgrp-dir (nnmaildir--srv-dir server) - (nnmaildir--grp-name group)))) - (setq x (nnmaildir--nndir x) - x (nnmaildir--num-dir x) - x (nnmaildir--num-file x) - x (file-attributes x)) - (if x (1- (nth 1 x)) 0)))) + (catch 'return + (if (zerop (nnmaildir--grp-count group)) (throw 'return 0)) + (let ((dir (nnmaildir--srvgrp-dir (nnmaildir--srv-dir server) + (nnmaildir--grp-name group))) + (number-opened 1) + attr ino-opened nlink number-linked) + (setq dir (nnmaildir--nndir dir) + dir (nnmaildir--num-dir dir)) + (while t + (setq attr (file-attributes + (concat dir (number-to-string number-opened)))) + (or attr (throw 'return (1- number-opened))) + (setq ino-opened (nth 10 attr) + nlink (nth 1 attr) + number-linked (+ number-opened nlink)) + (if (or (< nlink 1) (< number-linked nlink)) + (signal 'error '("Arithmetic overflow"))) + (setq attr (file-attributes + (concat dir (number-to-string number-linked)))) + (or attr (throw 'return (1- number-linked))) + (if (/= ino-opened (nth 10 attr)) + (setq number-opened number-linked)))))) ;; Make the given server, if non-nil, be the current server. Then make the ;; given group, if non-nil, be the current group of the current server. Then @@ -289,6 +305,64 @@ by nnmaildir-request-article.") (setq pos (match-end 0)))) string) +(defmacro nnmaildir--condcase (errsym body &rest handler) + `(condition-case ,errsym + (let ((system-messages-locale "C")) ,body) + (error . ,handler))) + +(defun nnmaildir--emlink-p (err) + (and (eq (car err) 'file-error) + (string= (downcase (caddr err)) "too many links"))) + +(defun nnmaildir--enoent-p (err) + (and (eq (car err) 'file-error) + (string= (downcase (caddr err)) "no such file or directory"))) + +(defun nnmaildir--eexist-p (err) + (eq (car err) 'file-already-exists)) + +(defun nnmaildir--new-number (nndir) + "Allocate a new article number by atomically creating a file under NNDIR." + (let ((numdir (nnmaildir--num-dir nndir)) + (make-new-file t) + (number-open 1) + number-link previous-number-link path-open path-link ino-open) + (nnmaildir--mkdir numdir) + (catch 'return + (while t + (setq path-open (concat numdir (number-to-string number-open))) + (if (not make-new-file) + (setq previous-number-link number-link) + (nnmaildir--mkfile path-open) + ;; If Emacs had O_CREAT|O_EXCL, we could return number-open here. + (setq make-new-file nil + previous-number-link 0)) + (let* ((attr (file-attributes path-open)) + (nlink (nth 1 attr))) + (setq ino-open (nth 10 attr) + number-link (+ number-open nlink)) + (if (or (< nlink 1) (< number-link nlink)) + (signal 'error '("Arithmetic overflow")))) + (if (= number-link previous-number-link) + ;; We've already tried this number, in the previous loop iteration, + ;; and failed. + (signal 'error `("Corrupt internal nnmaildir data" ,path-open))) + (setq path-link (concat numdir (number-to-string number-link))) + (nnmaildir--condcase err + (progn + (add-name-to-file path-open path-link) + (throw 'return number-link)) + (cond + ((nnmaildir--emlink-p err) + (setq make-new-file t + number-open number-link)) + ((nnmaildir--eexist-p err) + (let ((attr (file-attributes path-link))) + (if (/= (nth 10 attr) ino-open) + (setq number-open number-link + number-link 0)))) + (t (signal (car err) (cdr err))))))))) + (defun nnmaildir--update-nov (server group article) (let ((nnheader-file-coding-system 'binary) (srv-dir (nnmaildir--srv-dir server)) @@ -400,30 +474,7 @@ by nnmaildir-request-article.") nnmaildir--extra) num (nnmaildir--art-num article)) (unless num - ;; Allocate a new article number. - (erase-buffer) - (setq numdir (nnmaildir--num-dir dir) - file (nnmaildir--num-file numdir) - num -1) - (nnmaildir--mkdir numdir) - (write-region "" nil file nil 'no-message) - (while file - ;; Get the number of links to file. - (setq attr (nth 1 (file-attributes file))) - (if (= attr num) - ;; We've already tried this number, in the previous loop - ;; iteration, and failed. - (signal 'error `("Corrupt internal nnmaildir data" ,numdir))) - ;; If attr is 123, try to link file to "123". This atomically - ;; increases the link count and creates the "123" link, failing - ;; if that link was already created by another Gnus, just after - ;; we stat()ed file. - (condition-case nil - (progn - (add-name-to-file file (concat numdir (format "%x" attr))) - (setq file nil)) ;; Stop looping. - (file-already-exists nil)) - (setq num attr)) + (setq num (nnmaildir--new-number dir)) (setf (nnmaildir--art-num article) num)) ;; Store this new NOV data in a file (erase-buffer) @@ -684,8 +735,7 @@ by nnmaildir-request-article.") group (make-nnmaildir--grp :name gname :index 0)) (nnmaildir--mkdir nndir) (nnmaildir--mkdir (nnmaildir--nov-dir nndir)) - (nnmaildir--mkdir (nnmaildir--marks-dir nndir)) - (write-region "" nil (concat nndir "markfile") nil 'no-message)) + (nnmaildir--mkdir (nnmaildir--marks-dir nndir))) (setq read-only (nnmaildir--param pgname 'read-only) ls (or (nnmaildir--param pgname 'directory-files) srv-ls)) (unless read-only @@ -801,11 +851,13 @@ by nnmaildir-request-article.") (setq dirs (funcall srv-ls srv-dir nil "\\`[^.]" 'nosort) dirs (if (zerop (length target-prefix)) dirs - (remove-if (lambda (dir) - (and (>= (length dir) (length target-prefix)) - (string= (substring dir 0 (length target-prefix)) - target-prefix))) - dirs)) + (gnus-remove-if + (lambda (dir) + (and (>= (length dir) (length target-prefix)) + (string= (substring dir 0 + (length target-prefix)) + target-prefix))) + dirs)) seen (nnmaildir--up2-1 (length dirs)) seen (make-vector seen 0)) (mapcar @@ -1338,15 +1390,13 @@ by nnmaildir-request-article.") nnmaildir--cur-server) "24-hour timer expired") (throw 'return nil)))) - (condition-case nil - (add-name-to-file nnmaildir--file tmpfile) + (condition-case nil (add-name-to-file nnmaildir--file tmpfile) (error (write-region (point-min) (point-max) tmpfile nil 'no-message nil 'excl) (unix-sync))) ;; no fsync :( (cancel-timer 24h) - (condition-case err - (add-name-to-file tmpfile curfile) + (condition-case err (add-name-to-file tmpfile curfile) (error (setf (nnmaildir--srv-error nnmaildir--cur-server) (concat "Error linking: " (prin1-to-string err))) @@ -1469,7 +1519,12 @@ by nnmaildir-request-article.") (not (string-equal target pgname))) ;; Move it. (erase-buffer) (nnheader-insert-file-contents nnmaildir--file) - (gnus-request-accept-article target nil nil 'no-encode)) + (let ((group-art (gnus-request-accept-article + target nil nil 'no-encode))) + (when (consp group-art) + ;; Maybe also copy: dormant forward reply save tick + ;; (gnus-add-mark? gnus-request-set-mark?) + (gnus-group-mark-article-read target (cdr group-art))))) (if (equal target pgname) ;; Leave it here. (setq didnt (cons (nnmaildir--art-num article) didnt)) @@ -1483,9 +1538,9 @@ by nnmaildir-request-article.") (coding-system-for-write nnheader-file-coding-system) (buffer-file-coding-system nil) (file-coding-system-alist nil) - del-mark del-action add-action set-action marksdir markfile nlist - ranges begin end article all-marks todo-marks did-marks mdir mfile - pgname ls markfilenew deactivate-mark) + del-mark del-action add-action set-action marksdir nlist + ranges begin end article all-marks todo-marks mdir mfile + pgname ls permarkfile deactivate-mark) (setq del-mark (lambda (mark) (setq mfile (nnmaildir--subdir marksdir (symbol-name mark)) @@ -1497,20 +1552,21 @@ by nnmaildir-request-article.") (mapcar (lambda (mark) (setq mdir (nnmaildir--subdir marksdir (symbol-name mark)) + permarkfile (concat mdir ":") mfile (concat mdir (nnmaildir--art-prefix article))) - (unless (memq mark did-marks) - (nnmaildir--mkdir mdir) - (setq did-marks (cons mark did-marks))) - (unless (file-exists-p mfile) - (condition-case nil - (add-name-to-file markfile mfile) - (file-error - (unless (file-exists-p mfile) - ;; too many links, maybe - (write-region "" nil markfilenew nil 'no-message) - (add-name-to-file markfilenew mfile - 'ok-if-already-exists) - (rename-file markfilenew markfile 'replace)))))) + (nnmaildir--condcase err (add-name-to-file permarkfile mfile) + (cond + ((nnmaildir--eexist-p err)) + ((nnmaildir--enoent-p err) + (nnmaildir--mkdir mdir) + (nnmaildir--mkfile permarkfile) + (add-name-to-file permarkfile mfile)) + ((nnmaildir--emlink-p err) + (let ((permarkfilenew (concat permarkfile "{new}"))) + (nnmaildir--mkfile permarkfilenew) + (rename-file permarkfilenew permarkfile 'replace) + (add-name-to-file permarkfile mfile))) + (t (signal (car err) (cdr err)))))) todo-marks)) set-action (lambda (article) (funcall add-action) @@ -1530,8 +1586,6 @@ by nnmaildir-request-article.") marksdir (nnmaildir--srv-dir nnmaildir--cur-server) marksdir (nnmaildir--srvgrp-dir marksdir gname) marksdir (nnmaildir--nndir marksdir) - markfile (concat marksdir "markfile") - markfilenew (concat markfile "{new}") marksdir (nnmaildir--marks-dir marksdir) gname (nnmaildir--grp-name group) pgname (nnmaildir--pgname nnmaildir--cur-server gname) @@ -1588,7 +1642,7 @@ by nnmaildir-request-article.") dir (file-name-as-directory (car dir))) (mapcar (lambda (file) - (unless (intern-soft file flist) + (unless (or (intern-soft file flist) (string= file ":")) (setq file (concat dir file)) (delete-file file))) files)) @@ -1625,4 +1679,5 @@ by nnmaildir-request-article.") ;; fill-column: 77 ;; End: +;;; arch-tag: 0c4e44cd-dfde-4040-888e-5597ec771849 ;;; nnmaildir.el ends here