Synch with Gnus.
[elisp/gnus.git-] / lisp / nnheader.el
index e0ec1e1..c777023 100644 (file)
@@ -1,5 +1,8 @@
 ;;; nnheader.el --- header access macros for Semi-gnus and its backends
-;; Copyright (C) 1987-1990,1993-1999 Free Software Foundation, Inc.
+
+;; Copyright (C) 1987, 1988, 1989, 1990, 1993, 1994, 1995, 1996,
+;;        1997, 1998, 2000
+;;        Free Software Foundation, Inc.
 
 ;; Author: Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
 ;;     Lars Magne Ingebrigtsen <larsi@gnus.org>
@@ -29,6 +32,7 @@
 ;;; Code:
 
 (eval-when-compile (require 'cl))
+(eval-when-compile (require 'static))
 
 (require 'mail-utils)
 (require 'mime)
@@ -46,11 +50,21 @@ on your system, you could say something like:
 
 \(setq nnheader-file-name-translation-alist '((?: . ?_)))")
 
+(defvar nnheader-text-coding-system
+  (if (memq system-type '(windows-nt ms-dos ms-windows))
+      'raw-text-dos
+    'raw-text)
+  "Text-safe coding system (For removing ^M).
+This variable is a substitute for `mm-text-coding-system'.")
+
+(defvar nnheader-text-coding-system-for-write nil
+  "Text coding system for write.
+This variable is a substitute for `mm-text-coding-system-for-write'.")
+
 (eval-and-compile
   (autoload 'nnmail-message-id "nnmail")
   (autoload 'mail-position-on-field "sendmail")
   (autoload 'message-remove-header "message")
-  (autoload 'cancel-function-timers "timers")
   (autoload 'gnus-point-at-eol "gnus-util")
   (autoload 'gnus-delete-line "gnus-util")
   (autoload 'gnus-buffer-live-p "gnus-util"))
@@ -175,7 +189,10 @@ on your system, you could say something like:
 ;; Parsing headers and NOV lines.
 
 (defsubst nnheader-header-value ()
-  (buffer-substring (match-end 0) (gnus-point-at-eol)))
+  (let ((pt (point)))
+    (prog1
+       (buffer-substring (match-end 0) (std11-field-end))
+      (goto-char pt))))
 
 (defun nnheader-parse-head (&optional naked)
   (let ((case-fold-search t)
@@ -217,7 +234,8 @@ on your system, you could say something like:
           ;; From.
           (progn
             (goto-char p)
-            (if (search-forward "\nfrom: " nil t)
+            (if (or (search-forward "\nfrom: " nil t)
+                    (search-forward "\nfrom:" nil t))
                 (nnheader-header-value) "(nobody)"))
           ;; Date.
           (progn
@@ -334,36 +352,43 @@ on your system, you could say something like:
      (nnheader-nov-read-integer)       ; lines
      (if (eq (char-after) ?\n)
         nil
-       (nnheader-nov-field))           ; misc
+       (if (looking-at "Xref: ")
+          (goto-char (match-end 0)))
+       (nnheader-nov-field))           ; Xref
      (nnheader-nov-parse-extra))))     ; extra
 
 (defun nnheader-insert-nov (header)
   (princ (mail-header-number header) (current-buffer))
-  (insert
-   "\t"
-   (or (mime-entity-fetch-field header 'Subject) "(none)") "\t"
-   (or (mime-entity-fetch-field header 'From) "(nobody)") "\t"
-   (or (mail-header-date header) "") "\t"
-   (or (mail-header-id header)
-       (nnmail-message-id))
-   "\t"
-   (or (mail-header-references header) "") "\t")
-  (princ (or (mail-header-chars header) 0) (current-buffer))
-  (insert "\t")
-  (princ (or (mail-header-lines header) 0) (current-buffer))
-  (insert "\t")
-  (when (mail-header-xref header)
-    (insert "Xref: " (mail-header-xref header)))
-  (when (or (mail-header-xref header)
-           (mail-header-extra header))
-    (insert "\t"))
-  (when (mail-header-extra header)
-    (let ((extra (mail-header-extra header)))
-      (while extra
-       (insert (symbol-name (caar extra))
-               ": " (cdar extra) "\t")
-        (pop extra))))
-  (insert "\n"))
+  (let ((p (point)))
+    (insert
+     "\t"
+     (or (mime-entity-fetch-field header 'Subject) "(none)") "\t"
+     (or (mime-entity-fetch-field header 'From) "(nobody)") "\t"
+     (or (mail-header-date header) "") "\t"
+     (or (mail-header-id header)
+        (nnmail-message-id))
+     "\t"
+     (or (mail-header-references header) "") "\t")
+    (princ (or (mail-header-chars header) 0) (current-buffer))
+    (insert "\t")
+    (princ (or (mail-header-lines header) 0) (current-buffer))
+    (insert "\t")
+    (when (mail-header-xref header)
+      (insert "Xref: " (mail-header-xref header)))
+    (when (or (mail-header-xref header)
+             (mail-header-extra header))
+      (insert "\t"))
+    (when (mail-header-extra header)
+      (let ((extra (mail-header-extra header)))
+       (while extra
+         (insert (symbol-name (caar extra))
+                 ": " (cdar extra) "\t")
+         (pop extra))))
+    (insert "\n")
+    (backward-char 1)
+    (while (search-backward "\n" p t)
+      (delete-char 1))
+    (forward-line 1)))
 
 (defun nnheader-insert-header (header)
   (insert
@@ -453,6 +478,7 @@ the line could be found."
     (let* ((file nil)
           (number (length articles))
           (count 0)
+          (file-name-coding-system 'binary)
           (pathname-coding-system 'binary)
           (case-fold-search t)
           (cur (current-buffer))
@@ -748,7 +774,7 @@ list of headers that match SEQUENCE (see `nntp-retrieve-headers')."
     (erase-buffer))
   (current-buffer))
 
-(defvar jka-compr-compression-info-list)
+(eval-when-compile (defvar jka-compr-compression-info-list))
 (defvar nnheader-numerical-files
   (if (boundp 'jka-compr-compression-info-list)
       (concat "\\([0-9]+\\)\\("
@@ -765,17 +791,20 @@ list of headers that match SEQUENCE (see `nntp-retrieve-headers')."
   "Regexp that matches numerical full file paths.")
 
 (defsubst nnheader-file-to-number (file)
-  "Take a file name and return the article number."
+  "Take a FILE name and return the article number."
   (if (string= nnheader-numerical-short-files "^[0-9]+$")
       (string-to-int file)
     (string-match nnheader-numerical-short-files file)
     (string-to-int (match-string 0 file))))
 
+(defvar nnheader-directory-files-is-safe nil
+  "If non-nil, Gnus believes `directory-files' is safe.
+It has been reported numerous times that `directory-files' fails with
+an alarming frequency on NFS mounted file systems. If it is nil,
+`nnheader-directory-files-safe' is used.")
+
 (defun nnheader-directory-files-safe (&rest args)
-  ;; It has been reported numerous times that `directory-files'
-  ;; fails with an alarming frequency on NFS mounted file systems.
-  ;; This function executes that function twice and returns
-  ;; the longest result.
+  "Execute `directory-files' twice and returns the longer result."
   (let ((first (apply 'directory-files args))
        (second (apply 'directory-files args)))
     (if (> (length first) (length second))
@@ -783,16 +812,22 @@ list of headers that match SEQUENCE (see `nntp-retrieve-headers')."
       second)))
 
 (defun nnheader-directory-articles (dir)
-  "Return a list of all article files in a directory."
+  "Return a list of all article files in directory DIR."
   (mapcar 'nnheader-file-to-number
-         (nnheader-directory-files-safe
-          dir nil nnheader-numerical-short-files t)))
+         (if nnheader-directory-files-is-safe 
+             (directory-files
+              dir nil nnheader-numerical-short-files t)
+           (nnheader-directory-files-safe
+            dir nil nnheader-numerical-short-files t))))
 
 (defun nnheader-article-to-file-alist (dir)
   "Return an alist of article/file pairs in DIR."
   (mapcar (lambda (file) (cons (nnheader-file-to-number file) file))
-         (nnheader-directory-files-safe
-          dir nil nnheader-numerical-short-files t)))
+         (if nnheader-directory-files-is-safe 
+             (directory-files
+              dir nil nnheader-numerical-short-files t)
+           (nnheader-directory-files-safe
+            dir nil nnheader-numerical-short-files t))))
 
 (defun nnheader-fold-continuation-lines ()
   "Fold continuation lines in the current buffer."
@@ -810,15 +845,30 @@ If FULL, translate everything."
          ;; Do complete translation.
          (setq leaf (copy-sequence file)
                path ""
-               i (if (and (< 1 (length leaf)) (eq ?: (aref leaf 1))) 
+               i (if (and (< 1 (length leaf)) (eq ?: (aref leaf 1)))
                      2 0))
        ;; We translate -- but only the file name.  We leave the directory
        ;; alone.
-       (if (string-match "/[^/]+\\'" file)
-           ;; This is needed on NT's and stuff.
-           (setq leaf (substring file (1+ (match-beginning 0)))
-                 path (substring file 0 (1+ (match-beginning 0))))
-         ;; Fall back on this.
+       (if (and (featurep 'xemacs)
+                (memq system-type '(win32 w32 mswindows windows-nt)))
+           ;; This is needed on NT and stuff, because
+           ;; file-name-nondirectory is not enough to split
+           ;; file names, containing ':', e.g.
+           ;; "d:\\Work\\News\\nntp+news.fido7.ru:fido7.ru.gnu.SCORE"
+           ;; 
+           ;; we are trying to correctly split such names:
+           ;; "d:file.name" -> "a:" "file.name"
+           ;; "aaa:bbb.ccc" -> "" "aaa:bbb.ccc"
+           ;; "d:aaa\\bbb:ccc"   -> "d:aaa\\" "bbb:ccc"
+           ;; etc.
+           ;; to translate then only the file name part.
+           (progn
+             (setq leaf file
+                   path "")
+             (if (string-match "\\(^\\w:\\|[/\\]\\)\\([^/\\]+\\)$" file)
+                 (setq leaf (substring file (match-beginning 2))
+                       path (substring file 0 (match-beginning 2)))))
+         ;; Emacs DTRT, says andrewi.
          (setq leaf (file-name-nondirectory file)
                path (file-name-directory file))))
       (setq len (length leaf))
@@ -857,17 +907,20 @@ without formatting."
       (apply 'insert format args))
     t))
 
-(defun nnheader-replace-chars-in-string (string from to)
-  "Replace characters in STRING from FROM to TO."
-  (let ((string (substring string 0))  ;Copy string.
-       (len (length string))
-       (idx 0))
-    ;; Replace all occurrences of FROM with TO.
-    (while (< idx len)
-      (when (= (aref string idx) from)
-       (aset string idx to))
-      (setq idx (1+ idx)))
-    string))
+(static-if (fboundp 'subst-char-in-string)
+    (defsubst nnheader-replace-chars-in-string (string from to)
+      (subst-char-in-string from to string))
+  (defun nnheader-replace-chars-in-string (string from to)
+    "Replace characters in STRING from FROM to TO."
+    (let ((string (substring string 0))        ;Copy string.
+         (len (length string))
+         (idx 0))
+      ;; Replace all occurrences of FROM with TO.
+      (while (< idx len)
+       (when (= (aref string idx) from)
+         (aset string idx to))
+       (setq idx (1+ idx)))
+      string)))
 
 (defun nnheader-replace-duplicate-chars-in-string (string from to)
   "Replace characters in STRING from FROM to TO."
@@ -917,14 +970,14 @@ without formatting."
   (concat
    (let ((dir (file-name-as-directory (expand-file-name dir))))
      ;; If this directory exists, we use it directly.
-     (if (file-directory-p (concat dir group))
-        (concat dir group "/")
-       ;; If not, we translate dots into slashes.
-       (concat dir
-              (encode-coding-string
-               (nnheader-replace-chars-in-string group ?. ?/)
-               nnheader-pathname-coding-system)
-              "/")))
+     (file-name-as-directory
+      (if (file-directory-p (concat dir group))
+         (expand-file-name group dir)
+       ;; If not, we translate dots into slashes.
+       (expand-file-name (encode-coding-string
+                          (nnheader-replace-chars-in-string group ?. ?/)
+                          nnheader-pathname-coding-system)
+                         dir))))
    (cond ((null file) "")
         ((numberp file) (int-to-string file))
         (t file))))
@@ -935,7 +988,7 @@ without formatting."
       (and (listp form) (eq (car form) 'lambda))))
 
 (defun nnheader-concat (dir &rest files)
-  "Concat DIR as directory to FILE."
+  "Concat DIR as directory to FILES."
   (apply 'concat (file-name-as-directory dir) files))
 
 (defun nnheader-ms-strip-cr ()
@@ -1062,20 +1115,20 @@ find-file-hooks, etc.
      (set-buffer cur)))
 
 (defun nnheader-replace-string (from to)
-  "Do a fast replacement of FROM to TO from point to point-max."
+  "Do a fast replacement of FROM to TO from point to `point-max'."
   (nnheader-skeleton-replace from to))
 
 (defun nnheader-replace-regexp (from to)
-  "Do a fast regexp replacement of FROM to TO from point to point-max."
+  "Do a fast regexp replacement of FROM to TO from point to `point-max'."
   (nnheader-skeleton-replace from to t))
 
 (defun nnheader-strip-cr ()
   "Strip all \r's from the current buffer."
   (nnheader-skeleton-replace "\r"))
 
-(fset 'nnheader-run-at-time 'run-at-time)
-(fset 'nnheader-cancel-timer 'cancel-timer)
-(fset 'nnheader-cancel-function-timers 'cancel-function-timers)
+(defalias 'nnheader-run-at-time 'run-at-time)
+(defalias 'nnheader-cancel-timer 'cancel-timer)
+(defalias 'nnheader-cancel-function-timers 'cancel-function-timers)
 
 (defun nnheader-Y-or-n-p (prompt)
   "Ask user a \"Y/n\" question. Return t if answer is neither \"n\", \"N\" nor \"C-g\"."
@@ -1094,7 +1147,7 @@ find-file-hooks, etc.
       (message "%s(Y/n) Yes" prompt)
       t)))
 
-(when (string-match "XEmacs\\|Lucid" emacs-version)
+(when (featurep 'xemacs)
   (require 'nnheaderxm))
 
 (run-hooks 'nnheader-load-hook)