comparison lisp/gnus/nnheader.el @ 70:131b0175ea99 r20-0b30

Import from CVS: tag r20-0b30
author cvs
date Mon, 13 Aug 2007 09:02:59 +0200
parents 8d2a9b52c682
children 0d2f883870bc
comparison
equal deleted inserted replaced
69:804d1389bcd6 70:131b0175ea99
1 ;;; nnheader.el --- header access macros for Gnus and its backends 1 ;;; nnheader.el --- header access macros for Gnus and its backends
2 ;; Copyright (C) 1987,88,89,90,93,94,95,96,97 Free Software Foundation, Inc. 2 ;; Copyright (C) 1987,88,89,90,93,94,95,96 Free Software Foundation, Inc.
3 3
4 ;; Author: Masanobu UMEDA <umerin@flab.flab.fujitsu.junet> 4 ;; Author: Masanobu UMEDA <umerin@flab.flab.fujitsu.junet>
5 ;; Lars Magne Ingebrigtsen <larsi@ifi.uio.no> 5 ;; Lars Magne Ingebrigtsen <larsi@ifi.uio.no>
6 ;; Keywords: news 6 ;; Keywords: news
7 7
36 ;; uses it for xrefs.) 36 ;; uses it for xrefs.)
37 37
38 ;;; Code: 38 ;;; Code:
39 39
40 (require 'mail-utils) 40 (require 'mail-utils)
41 (eval-when-compile (require 'cl))
41 42
42 (defvar nnheader-max-head-length 4096 43 (defvar nnheader-max-head-length 4096
43 "*Max length of the head of articles.") 44 "*Max length of the head of articles.")
44
45 (defvar nnheader-head-chop-length 2048
46 "*Length of each read operation when trying to fetch HEAD headers.")
47 45
48 (defvar nnheader-file-name-translation-alist nil 46 (defvar nnheader-file-name-translation-alist nil
49 "*Alist that says how to translate characters in file names. 47 "*Alist that says how to translate characters in file names.
50 For instance, if \":\" is illegal as a file character in file names 48 For instance, if \":\" is illegal as a file character in file names
51 on your system, you could say something like: 49 on your system, you could say something like:
52 50
53 \(setq nnheader-file-name-translation-alist '((?: . ?_)))") 51 \(setq nnheader-file-name-translation-alist '((?: . ?_)))")
54 52
55 (eval-and-compile
56 (autoload 'nnmail-message-id "nnmail")
57 (autoload 'mail-position-on-field "sendmail")
58 (autoload 'message-remove-header "message")
59 (autoload 'cancel-function-timers "timers")
60 (autoload 'gnus-point-at-eol "gnus-util"))
61
62 ;;; Header access macros. 53 ;;; Header access macros.
63 54
64 (defmacro mail-header-number (header) 55 (defmacro mail-header-number (header)
65 "Return article number in HEADER." 56 "Return article number in HEADER."
66 `(aref ,header 0)) 57 `(aref ,header 0))
137 128
138 (defun make-mail-header (&optional init) 129 (defun make-mail-header (&optional init)
139 "Create a new mail header structure initialized with INIT." 130 "Create a new mail header structure initialized with INIT."
140 (make-vector 9 init)) 131 (make-vector 9 init))
141 132
142 (defun make-full-mail-header (&optional number subject from date id
143 references chars lines xref)
144 "Create a new mail header structure initialized with the parameters given."
145 (vector number subject from date id references chars lines xref))
146
147 ;; fake message-ids: generation and detection
148
149 (defvar nnheader-fake-message-id 1)
150
151 (defsubst nnheader-generate-fake-message-id ()
152 (concat "fake+none+" (int-to-string (incf nnheader-fake-message-id))))
153
154 (defsubst nnheader-fake-message-id-p (id)
155 (save-match-data ; regular message-id's are <.*>
156 (string-match "\\`fake\\+none\\+[0-9]+\\'" id)))
157
158 ;; Parsing headers and NOV lines. 133 ;; Parsing headers and NOV lines.
159 134
160 (defsubst nnheader-header-value () 135 (defsubst nnheader-header-value ()
161 (buffer-substring (match-end 0) (gnus-point-at-eol))) 136 (buffer-substring (match-end 0) (gnus-point-at-eol)))
137
138 (defvar nnheader-newsgroup-none-id 1)
162 139
163 (defun nnheader-parse-head (&optional naked) 140 (defun nnheader-parse-head (&optional naked)
164 (let ((case-fold-search t) 141 (let ((case-fold-search t)
165 (cur (current-buffer)) 142 (cur (current-buffer))
166 (buffer-read-only nil) 143 (buffer-read-only nil)
167 in-reply-to lines p) 144 end ref in-reply-to lines p)
168 (goto-char (point-min)) 145 (goto-char (point-min))
169 (when naked 146 (when naked
170 (insert "\n")) 147 (insert "\n"))
171 ;; Search to the beginning of the next header. Error messages 148 ;; Search to the beginning of the next header. Error messages
172 ;; do not begin with 2 or 3. 149 ;; do not begin with 2 or 3.
173 (prog1 150 (prog1
174 (when (or naked (re-search-forward "^[23][0-9]+ " nil t)) 151 (when (or naked (re-search-forward "^[23][0-9]+ " nil t))
175 ;; This implementation of this function, with nine 152 ;; This implementation of this function, with nine
176 ;; search-forwards instead of the one re-search-forward and 153 ;; search-forwards instead of the one re-search-forward and
177 ;; a case (which basically was the old function) is actually 154 ;; a case (which basically was the old function) is actually
178 ;; about twice as fast, even though it looks messier. You 155 ;; about twice as fast, even though it looks messier. You
179 ;; can't have everything, I guess. Speed and elegance 156 ;; can't have everything, I guess. Speed and elegance
180 ;; don't always go hand in hand. 157 ;; doesn't always go hand in hand.
181 (vector 158 (vector
182 ;; Number. 159 ;; Number.
183 (if naked 160 (if naked
184 (progn 161 (progn
185 (setq p (point-min)) 162 (setq p (point-min))
208 (if (search-forward "\ndate: " nil t) 185 (if (search-forward "\ndate: " nil t)
209 (nnheader-header-value) "")) 186 (nnheader-header-value) ""))
210 ;; Message-ID. 187 ;; Message-ID.
211 (progn 188 (progn
212 (goto-char p) 189 (goto-char p)
213 (if (search-forward "\nmessage-id:" nil t) 190 (if (search-forward "\nmessage-id: " nil t)
214 (buffer-substring 191 (nnheader-header-value)
215 (1- (or (search-forward "<" nil t) (point)))
216 (or (search-forward ">" nil t) (point)))
217 ;; If there was no message-id, we just fake one to make 192 ;; If there was no message-id, we just fake one to make
218 ;; subsequent routines simpler. 193 ;; subsequent routines simpler.
219 (nnheader-generate-fake-message-id))) 194 (concat "none+"
195 (int-to-string
196 (incf nnheader-newsgroup-none-id)))))
220 ;; References. 197 ;; References.
221 (progn 198 (progn
222 (goto-char p) 199 (goto-char p)
223 (if (search-forward "\nreferences: " nil t) 200 (if (search-forward "\nreferences: " nil t)
224 (nnheader-header-value) 201 (nnheader-header-value)
247 (nnheader-header-value))))) 224 (nnheader-header-value)))))
248 (when naked 225 (when naked
249 (goto-char (point-min)) 226 (goto-char (point-min))
250 (delete-char 1))))) 227 (delete-char 1)))))
251 228
252 (defmacro nnheader-nov-skip-field ()
253 '(search-forward "\t" eol 'move))
254
255 (defmacro nnheader-nov-field ()
256 '(buffer-substring (point) (if (nnheader-nov-skip-field) (1- (point)) eol)))
257
258 (defmacro nnheader-nov-read-integer ()
259 '(prog1
260 (if (= (following-char) ?\t)
261 0
262 (let ((num (ignore-errors (read (current-buffer)))))
263 (if (numberp num) num 0)))
264 (or (eobp) (forward-char 1))))
265
266 ;; (defvar nnheader-none-counter 0)
267
268 (defun nnheader-parse-nov ()
269 (let ((eol (gnus-point-at-eol)))
270 (vector
271 (nnheader-nov-read-integer) ; number
272 (nnheader-nov-field) ; subject
273 (nnheader-nov-field) ; from
274 (nnheader-nov-field) ; date
275 (or (nnheader-nov-field)
276 (nnheader-generate-fake-message-id)) ; id
277 (nnheader-nov-field) ; refs
278 (nnheader-nov-read-integer) ; chars
279 (nnheader-nov-read-integer) ; lines
280 (if (= (following-char) ?\n)
281 nil
282 (nnheader-nov-field)) ; misc
283 )))
284
285 (defun nnheader-insert-nov (header) 229 (defun nnheader-insert-nov (header)
286 (princ (mail-header-number header) (current-buffer)) 230 (princ (mail-header-number header) (current-buffer))
287 (insert 231 (insert
288 "\t" 232 "\t"
289 (or (mail-header-subject header) "(none)") "\t" 233 (or (mail-header-subject header) "(none)") "\t"
290 (or (mail-header-from header) "(nobody)") "\t" 234 (or (mail-header-from header) "(nobody)") "\t"
291 (or (mail-header-date header) "") "\t" 235 (or (mail-header-date header) "") "\t"
292 (or (mail-header-id header) 236 (or (mail-header-id header)
293 (nnmail-message-id)) 237 (nnmail-message-id)) "\t"
294 "\t"
295 (or (mail-header-references header) "") "\t") 238 (or (mail-header-references header) "") "\t")
296 (princ (or (mail-header-chars header) 0) (current-buffer)) 239 (princ (or (mail-header-chars header) 0) (current-buffer))
297 (insert "\t") 240 (insert "\t")
298 (princ (or (mail-header-lines header) 0) (current-buffer)) 241 (princ (or (mail-header-lines header) 0) (current-buffer))
299 (insert "\t") 242 (insert "\t")
300 (when (mail-header-xref header) 243 (when (mail-header-xref header)
301 (insert "Xref: " (mail-header-xref header) "\t")) 244 (insert "Xref: " (mail-header-xref header) "\t"))
302 (insert "\n")) 245 (insert "\n"))
303 246
304 (defun nnheader-insert-article-line (article) 247 (defun nnheader-insert-article-line (article)
305 (goto-char (point-min)) 248 (goto-char (point-min))
309 (search-forward "\n\n" nil 'move) 252 (search-forward "\n\n" nil 'move)
310 (delete-region (point) (point-max)) 253 (delete-region (point) (point-max))
311 (forward-char -1) 254 (forward-char -1)
312 (insert ".")) 255 (insert "."))
313 256
314 (defun nnheader-nov-delete-outside-range (beg end)
315 "Delete all NOV lines that lie outside the BEG to END range."
316 ;; First we find the first wanted line.
317 (nnheader-find-nov-line beg)
318 (delete-region (point-min) (point))
319 ;; Then we find the last wanted line.
320 (when (nnheader-find-nov-line end)
321 (forward-line 1))
322 (delete-region (point) (point-max)))
323
324 (defun nnheader-find-nov-line (article)
325 "Put point at the NOV line that start with ARTICLE.
326 If ARTICLE doesn't exist, put point where that line
327 would have been. The function will return non-nil if
328 the line could be found."
329 ;; This function basically does a binary search.
330 (let ((max (point-max))
331 (min (goto-char (point-min)))
332 (cur (current-buffer))
333 (prev (point-min))
334 num found)
335 (while (not found)
336 (goto-char (/ (+ max min) 2))
337 (beginning-of-line)
338 (if (or (= (point) prev)
339 (eobp))
340 (setq found t)
341 (setq prev (point))
342 (cond ((> (setq num (read cur)) article)
343 (setq max (point)))
344 ((< num article)
345 (setq min (point)))
346 (t
347 (setq found 'yes)))))
348 ;; We may be at the first line.
349 (when (and (not num)
350 (not (eobp)))
351 (setq num (read cur)))
352 ;; Now we may have found the article we're looking for, or we
353 ;; may be somewhere near it.
354 (when (and (not (eq found 'yes))
355 (not (eq num article)))
356 (setq found (point))
357 (while (and (< (point) max)
358 (or (not (numberp num))
359 (< num article)))
360 (forward-line 1)
361 (setq found (point))
362 (or (eobp)
363 (= (setq num (read cur)) article)))
364 (unless (eq num article)
365 (goto-char found)))
366 (beginning-of-line)
367 (eq num article)))
368
369 ;; Various cruft the backends and Gnus need to communicate. 257 ;; Various cruft the backends and Gnus need to communicate.
370 258
371 (defvar nntp-server-buffer nil) 259 (defvar nntp-server-buffer nil)
372 (defvar gnus-verbose-backends 7 260 (defvar gnus-verbose-backends 7
373 "*A number that says how talkative the Gnus backends should be.") 261 "*A number that says how talkative the Gnus backends should be.")
379 (defvar nnheader-callback-function nil) 267 (defvar nnheader-callback-function nil)
380 268
381 (defun nnheader-init-server-buffer () 269 (defun nnheader-init-server-buffer ()
382 "Initialize the Gnus-backend communication buffer." 270 "Initialize the Gnus-backend communication buffer."
383 (save-excursion 271 (save-excursion
384 (unless (gnus-buffer-live-p nntp-server-buffer) 272 (setq nntp-server-buffer (get-buffer-create " *nntpd*"))
385 (setq nntp-server-buffer (get-buffer-create " *nntpd*")))
386 (set-buffer nntp-server-buffer) 273 (set-buffer nntp-server-buffer)
387 (buffer-disable-undo (current-buffer)) 274 (buffer-disable-undo (current-buffer))
388 (erase-buffer) 275 (erase-buffer)
389 (kill-all-local-variables) 276 (kill-all-local-variables)
390 (setq case-fold-search t) ;Should ignore case. 277 (setq case-fold-search t) ;Should ignore case.
391 t)) 278 t))
279
392 280
393 ;;; Various functions the backends use. 281 ;;; Various functions the backends use.
394 282
395 (defun nnheader-file-error (file) 283 (defun nnheader-file-error (file)
396 "Return a string that says what is wrong with FILE." 284 "Return a string that says what is wrong with FILE."
407 (defun nnheader-insert-head (file) 295 (defun nnheader-insert-head (file)
408 "Insert the head of the article." 296 "Insert the head of the article."
409 (when (file-exists-p file) 297 (when (file-exists-p file)
410 (if (eq nnheader-max-head-length t) 298 (if (eq nnheader-max-head-length t)
411 ;; Just read the entire file. 299 ;; Just read the entire file.
412 (nnheader-insert-file-contents file) 300 (nnheader-insert-file-contents-literally file)
413 ;; Read 1K blocks until we find a separator. 301 ;; Read 1K blocks until we find a separator.
414 (let ((beg 0) 302 (let ((beg 0)
415 format-alist) 303 format-alist
416 (while (and (eq nnheader-head-chop-length 304 (chop 1024))
417 (nth 1 (nnheader-insert-file-contents 305 (while (and (eq chop (nth 1 (insert-file-contents
418 file nil beg 306 file nil beg (incf beg chop))))
419 (incf beg nnheader-head-chop-length)))) 307 (prog1 (not (search-forward "\n\n" nil t))
420 (prog1 (not (search-forward "\n\n" nil t))
421 (goto-char (point-max))) 308 (goto-char (point-max)))
422 (or (null nnheader-max-head-length) 309 (or (null nnheader-max-head-length)
423 (< beg nnheader-max-head-length)))))) 310 (< beg nnheader-max-head-length))))))
424 t)) 311 t))
425 312
432 (goto-char (point-min)) 319 (goto-char (point-min))
433 (while (looking-at "[A-Z][^ \t]+:.*\n\\([ \t].*\n\\)*\\|From .*\n") 320 (while (looking-at "[A-Z][^ \t]+:.*\n\\([ \t].*\n\\)*\\|From .*\n")
434 (goto-char (match-end 0))) 321 (goto-char (match-end 0)))
435 (prog1 322 (prog1
436 (eobp) 323 (eobp)
437 (widen)))) 324 (widen))))
438 325
439 (defun nnheader-insert-references (references message-id) 326 (defun nnheader-insert-references (references message-id)
440 "Insert a References header based on REFERENCES and MESSAGE-ID." 327 "Insert a References header based on REFERENCES and MESSAGE-ID."
441 (if (and (not references) (not message-id)) 328 (if (and (not references) (not message-id))
442 () ; This is illegal, but not all articles have Message-IDs. 329 () ; This is illegal, but not all articles have Message-IDs.
443 (mail-position-on-field "References") 330 (mail-position-on-field "References")
444 (let ((begin (save-excursion (beginning-of-line) (point))) 331 (let ((begin (save-excursion (beginning-of-line) (point)))
445 (fill-column 78) 332 (fill-column 78)
446 (fill-prefix "\t")) 333 (fill-prefix "\t"))
447 (when references 334 (if references (insert references))
448 (insert references)) 335 (if (and references message-id) (insert " "))
449 (when (and references message-id) 336 (if message-id (insert message-id))
450 (insert " "))
451 (when message-id
452 (insert message-id))
453 ;; Fold long References lines to conform to RFC1036 (sort of). 337 ;; Fold long References lines to conform to RFC1036 (sort of).
454 ;; The region must end with a newline to fill the region 338 ;; The region must end with a newline to fill the region
455 ;; without inserting extra newline. 339 ;; without inserting extra newline.
456 (fill-region-as-paragraph begin (1+ (point)))))) 340 (fill-region-as-paragraph begin (1+ (point))))))
457 341
473 (if (search-forward "\n\n" nil t) 357 (if (search-forward "\n\n" nil t)
474 (1- (point)) 358 (1- (point))
475 (point-max))) 359 (point-max)))
476 (goto-char (point-min))) 360 (goto-char (point-min)))
477 361
478 (defun nnheader-set-temp-buffer (name &optional noerase) 362 (defun nnheader-set-temp-buffer (name)
479 "Set-buffer to an empty (possibly new) buffer called NAME with undo disabled." 363 "Set-buffer to an empty (possibly new) buffer called NAME with undo disabled."
480 (set-buffer (get-buffer-create name)) 364 (set-buffer (get-buffer-create name))
481 (buffer-disable-undo (current-buffer)) 365 (buffer-disable-undo (current-buffer))
482 (unless noerase 366 (erase-buffer)
483 (erase-buffer))
484 (current-buffer)) 367 (current-buffer))
485 368
486 (defmacro nnheader-temp-write (file &rest forms) 369 (defmacro nnheader-temp-write (file &rest forms)
487 "Create a new buffer, evaluate FORMS there, and write the buffer to FILE. 370 "Create a new buffer, evaluate FORM there, and write the buffer to FILE."
488 Return the value of FORMS. 371 `(save-excursion
489 If FILE is nil, just evaluate FORMS and don't save anything. 372 (let ((nnheader-temp-file ,file)
490 If FILE is t, return the buffer contents as a string." 373 (nnheader-temp-cur-buffer
491 (let ((temp-file (make-symbol "temp-file")) 374 (nnheader-set-temp-buffer
492 (temp-buffer (make-symbol "temp-buffer")) 375 (generate-new-buffer-name " *nnheader temp*"))))
493 (temp-results (make-symbol "temp-results"))) 376 (when (and nnheader-temp-file
494 `(save-excursion 377 (not (file-directory-p (file-name-directory
495 (let* ((,temp-file ,file) 378 nnheader-temp-file))))
496 (default-major-mode 'fundamental-mode) 379 (make-directory (file-name-directory nnheader-temp-file) t))
497 (,temp-buffer 380 (unwind-protect
498 (set-buffer 381 (prog1
499 (get-buffer-create 382 (progn
500 (generate-new-buffer-name " *nnheader temp*")))) 383 ,@forms)
501 ,temp-results) 384 (when nnheader-temp-file
502 (unwind-protect 385 (set-buffer nnheader-temp-cur-buffer)
503 (progn 386 (write-region (point-min) (point-max)
504 (setq ,temp-results (progn ,@forms)) 387 nnheader-temp-file nil 'nomesg)))
505 (cond 388 (when (buffer-name nnheader-temp-cur-buffer)
506 ;; Don't save anything. 389 (kill-buffer nnheader-temp-cur-buffer))))))
507 ((null ,temp-file)
508 ,temp-results)
509 ;; Return the buffer contents.
510 ((eq ,temp-file t)
511 (set-buffer ,temp-buffer)
512 (buffer-string))
513 ;; Save a file.
514 (t
515 (set-buffer ,temp-buffer)
516 ;; Make sure the directory where this file is
517 ;; to be saved exists.
518 (when (not (file-directory-p
519 (file-name-directory ,temp-file)))
520 (make-directory (file-name-directory ,temp-file) t))
521 ;; Save the file.
522 (write-region (point-min) (point-max)
523 ,temp-file nil 'nomesg)
524 ,temp-results)))
525 ;; Kill the buffer.
526 (when (buffer-name ,temp-buffer)
527 (kill-buffer ,temp-buffer)))))))
528 390
529 (put 'nnheader-temp-write 'lisp-indent-function 1) 391 (put 'nnheader-temp-write 'lisp-indent-function 1)
392 (put 'nnheader-temp-write 'lisp-indent-hook 1)
530 (put 'nnheader-temp-write 'edebug-form-spec '(form body)) 393 (put 'nnheader-temp-write 'edebug-form-spec '(form body))
531 394
532 (defvar jka-compr-compression-info-list) 395 (defvar jka-compr-compression-info-list)
533 (defvar nnheader-numerical-files 396 (defvar nnheader-numerical-files
534 (if (boundp 'jka-compr-compression-info-list) 397 (if (boundp 'jka-compr-compression-info-list)
535 (concat "\\([0-9]+\\)\\(" 398 (concat "\\([0-9]+\\)\\("
536 (mapconcat (lambda (i) (aref i 0)) 399 (mapconcat (lambda (i) (aref i 0))
537 jka-compr-compression-info-list "\\|") 400 jka-compr-compression-info-list "\\|")
538 "\\)?") 401 "\\)?")
539 "[0-9]+$") 402 "[0-9]+$")
540 "Regexp that match numerical files.") 403 "Regexp that match numerical files.")
553 (string-to-int (match-string 0 file)))) 416 (string-to-int (match-string 0 file))))
554 417
555 (defun nnheader-directory-files-safe (&rest args) 418 (defun nnheader-directory-files-safe (&rest args)
556 ;; It has been reported numerous times that `directory-files' 419 ;; It has been reported numerous times that `directory-files'
557 ;; fails with an alarming frequency on NFS mounted file systems. 420 ;; fails with an alarming frequency on NFS mounted file systems.
558 ;; This function executes that function twice and returns 421 ;; This function executes that function twice and returns
559 ;; the longest result. 422 ;; the longest result.
560 (let ((first (apply 'directory-files args)) 423 (let ((first (apply 'directory-files args))
561 (second (apply 'directory-files args))) 424 (second (apply 'directory-files args)))
562 (if (> (length first) (length second)) 425 (if (> (length first) (length second))
563 first 426 first
575 (nnheader-directory-files-safe 438 (nnheader-directory-files-safe
576 dir nil nnheader-numerical-short-files t))) 439 dir nil nnheader-numerical-short-files t)))
577 440
578 (defun nnheader-fold-continuation-lines () 441 (defun nnheader-fold-continuation-lines ()
579 "Fold continuation lines in the current buffer." 442 "Fold continuation lines in the current buffer."
580 (nnheader-replace-regexp "\\(\r?\n[ \t]+\\)+" " ")) 443 (goto-char (point-min))
444 (while (re-search-forward "\\(\r?\n[ \t]+\\)+" nil t)
445 (replace-match " " t t)))
581 446
582 (defun nnheader-translate-file-chars (file) 447 (defun nnheader-translate-file-chars (file)
583 (if (null nnheader-file-name-translation-alist) 448 (if (null nnheader-file-name-translation-alist)
584 ;; No translation is necessary. 449 ;; No translation is necessary.
585 file 450 file
586 ;; We translate -- but only the file name. We leave the directory 451 ;; We translate -- but only the file name. We leave the directory
587 ;; alone. 452 ;; alone.
588 (let* ((i 0) 453 (let* ((i 0)
589 trans leaf path len) 454 trans leaf path len)
590 (if (string-match "/[^/]+\\'" file) 455 (if (string-match "/[^/]+\\'" file)
610 (car args) 475 (car args)
611 (apply 'format args))) 476 (apply 'format args)))
612 nil) 477 nil)
613 478
614 (defun nnheader-get-report (backend) 479 (defun nnheader-get-report (backend)
615 "Get the most recent report from BACKEND." 480 (message "%s" (symbol-value (intern (format "%s-status-string" backend)))))
616 (condition-case ()
617 (message "%s" (symbol-value (intern (format "%s-status-string"
618 backend))))
619 (error (message ""))))
620 481
621 (defun nnheader-insert (format &rest args) 482 (defun nnheader-insert (format &rest args)
622 "Clear the communication buffer and insert FORMAT and ARGS into the buffer. 483 "Clear the communicaton buffer and insert FORMAT and ARGS into the buffer.
623 If FORMAT isn't a format string, it and all ARGS will be inserted 484 If FORMAT isn't a format string, it and all ARGS will be inserted
624 without formatting." 485 without formatting."
625 (save-excursion 486 (save-excursion
626 (set-buffer nntp-server-buffer) 487 (set-buffer nntp-server-buffer)
627 (erase-buffer) 488 (erase-buffer)
628 (if (string-match "%" format) 489 (if (string-match "%" format)
629 (insert (apply 'format format args)) 490 (insert (apply 'format format args))
630 (apply 'insert format args)) 491 (apply 'insert format args))
631 t)) 492 t))
632 493
494 (defun nnheader-mail-file-mbox-p (file)
495 "Say whether FILE looks like an Unix mbox file."
496 (when (and (file-exists-p file)
497 (file-readable-p file)
498 (file-regular-p file))
499 (save-excursion
500 (nnheader-set-temp-buffer " *mail-file-mbox-p*")
501 (nnheader-insert-file-contents-literally file)
502 (goto-char (point-min))
503 (prog1
504 (looking-at message-unix-mail-delimiter)
505 (kill-buffer (current-buffer))))))
506
633 (defun nnheader-replace-chars-in-string (string from to) 507 (defun nnheader-replace-chars-in-string (string from to)
634 "Replace characters in STRING from FROM to TO." 508 "Replace characters in STRING from FROM to TO."
635 (let ((string (substring string 0)) ;Copy string. 509 (let ((string (substring string 0)) ;Copy string.
636 (len (length string)) 510 (len (length string))
637 (idx 0)) 511 (idx 0))
638 ;; Replace all occurrences of FROM with TO. 512 ;; Replace all occurrences of FROM with TO.
639 (while (< idx len) 513 (while (< idx len)
640 (when (= (aref string idx) from) 514 (if (= (aref string idx) from)
641 (aset string idx to)) 515 (aset string idx to))
642 (setq idx (1+ idx))) 516 (setq idx (1+ idx)))
643 string)) 517 string))
644 518
645 (defun nnheader-file-to-group (file &optional top) 519 (defun nnheader-file-to-group (file &optional top)
646 "Return a group name based on FILE and TOP." 520 "Return a group name based on FILE and TOP."
647 (nnheader-replace-chars-in-string 521 (nnheader-replace-chars-in-string
648 (if (not top) 522 (if (not top)
649 file 523 file
650 (condition-case () 524 (condition-case ()
651 (substring (expand-file-name file) 525 (substring (expand-file-name file)
652 (length 526 (length
653 (expand-file-name 527 (expand-file-name
654 (file-name-as-directory top)))) 528 (file-name-as-directory top))))
655 (error ""))) 529 (error "")))
656 ?/ ?.)) 530 ?/ ?.))
657 531
683 (defun nnheader-functionp (form) 557 (defun nnheader-functionp (form)
684 "Return non-nil if FORM is funcallable." 558 "Return non-nil if FORM is funcallable."
685 (or (and (symbolp form) (fboundp form)) 559 (or (and (symbolp form) (fboundp form))
686 (and (listp form) (eq (car form) 'lambda)))) 560 (and (listp form) (eq (car form) 'lambda))))
687 561
688 (defun nnheader-concat (dir &rest files) 562 (defun nnheader-concat (dir file)
689 "Concat DIR as directory to FILE." 563 "Concat DIR as directory to FILE."
690 (apply 'concat (file-name-as-directory dir) files)) 564 (concat (file-name-as-directory dir) file))
691 565
692 (defun nnheader-ms-strip-cr () 566 (defun nnheader-ms-strip-cr ()
693 "Strip ^M from the end of all lines." 567 "Strip ^M from the end of all lines."
694 (save-excursion 568 (save-excursion
695 (goto-char (point-min)) 569 (goto-char (point-min))
698 572
699 (defun nnheader-file-size (file) 573 (defun nnheader-file-size (file)
700 "Return the file size of FILE or 0." 574 "Return the file size of FILE or 0."
701 (or (nth 7 (file-attributes file)) 0)) 575 (or (nth 7 (file-attributes file)) 0))
702 576
703 (defun nnheader-find-etc-directory (package &optional file) 577 (defun nnheader-find-etc-directory (package)
704 "Go through the path and find the \".../etc/PACKAGE\" directory. 578 "Go through the path and find the \".../etc/PACKAGE\" directory."
705 If FILE, find the \".../etc/PACKAGE\" file instead."
706 (let ((path load-path) 579 (let ((path load-path)
707 dir result) 580 dir result)
708 ;; We try to find the dir by looking at the load path, 581 ;; We try to find the dir by looking at the load path,
709 ;; stripping away the last component and adding "etc/". 582 ;; stripping away the last component and adding "etc/".
710 (while path 583 (while path
711 (if (and (car path) 584 (if (and (car path)
712 (file-exists-p 585 (file-exists-p
713 (setq dir (concat 586 (setq dir (concat
714 (file-name-directory 587 (file-name-directory
715 (directory-file-name (car path))) 588 (directory-file-name (car path)))
716 "etc/" package 589 "etc/" package "/")))
717 (if file "" "/")))) 590 (file-directory-p dir))
718 (or file (file-directory-p dir)))
719 (setq result dir 591 (setq result dir
720 path nil) 592 path nil)
721 (setq path (cdr path)))) 593 (setq path (cdr path))))
722 result)) 594 result))
723 595
724 (defvar ange-ftp-path-format) 596 (defvar ange-ftp-path-format)
725 (defvar efs-path-regexp) 597 (defvar efs-path-regexp)
726 (defun nnheader-re-read-dir (path) 598 (defun nnheader-re-read-dir (path)
727 "Re-read directory PATH if PATH is on a remote system." 599 "Re-read directory PATH if PATH is on a remote system."
728 (if (and (fboundp 'efs-re-read-dir) (boundp 'efs-path-regexp)) 600 (if (and (fboundp 'efs-re-read-dir) (boundp 'efs-path-regexp))
729 (when (string-match efs-path-regexp path) 601 (when (string-match efs-path-regexp path)
730 (efs-re-read-dir path)) 602 (efs-re-read-dir path))
731 (when (and (fboundp 'ange-ftp-re-read-dir) (boundp 'ange-ftp-path-format)) 603 (if (and (fboundp 'ange-ftp-re-read-dir) (boundp 'ange-ftp-path-format))
732 (when (string-match (car ange-ftp-path-format) path) 604 (when (string-match (car ange-ftp-path-format) path)
733 (ange-ftp-re-read-dir path))))) 605 (ange-ftp-re-read-dir path)))))
734 606
735 (defun nnheader-insert-file-contents (filename &optional visit beg end replace)
736 "Like `insert-file-contents', q.v., but only reads in the file.
737 A buffer may be modified in several ways after reading into the buffer due
738 to advanced Emacs features, such as file-name-handlers, format decoding,
739 find-file-hooks, etc.
740 This function ensures that none of these modifications will take place."
741 (let ((format-alist nil)
742 (auto-mode-alist (nnheader-auto-mode-alist))
743 (default-major-mode 'fundamental-mode)
744 (after-insert-file-functions nil))
745 (insert-file-contents filename visit beg end replace)))
746
747 (defun nnheader-find-file-noselect (&rest args)
748 (let ((format-alist nil)
749 (auto-mode-alist (nnheader-auto-mode-alist))
750 (default-major-mode 'fundamental-mode)
751 (enable-local-variables nil)
752 (after-insert-file-functions nil))
753 (apply 'find-file-noselect args)))
754
755 (defun nnheader-auto-mode-alist ()
756 "Return an `auto-mode-alist' with only the .gz (etc) thingies."
757 (let ((alist auto-mode-alist)
758 out)
759 (while alist
760 (when (listp (cdar alist))
761 (push (car alist) out))
762 (pop alist))
763 (nreverse out)))
764
765 (defun nnheader-directory-regular-files (dir)
766 "Return a list of all regular files in DIR."
767 (let ((files (directory-files dir t))
768 out)
769 (while files
770 (when (file-regular-p (car files))
771 (push (car files) out))
772 (pop files))
773 (nreverse out)))
774
775 (defmacro nnheader-skeleton-replace (from &optional to regexp)
776 `(let ((new (generate-new-buffer " *nnheader replace*"))
777 (cur (current-buffer))
778 (start (point-min)))
779 (set-buffer new)
780 (buffer-disable-undo (current-buffer))
781 (set-buffer cur)
782 (goto-char (point-min))
783 (while (,(if regexp 're-search-forward 'search-forward)
784 ,from nil t)
785 (insert-buffer-substring
786 cur start (prog1 (match-beginning 0) (set-buffer new)))
787 (goto-char (point-max))
788 ,(when to `(insert ,to))
789 (set-buffer cur)
790 (setq start (point)))
791 (insert-buffer-substring
792 cur start (prog1 (point-max) (set-buffer new)))
793 (copy-to-buffer cur (point-min) (point-max))
794 (kill-buffer (current-buffer))
795 (set-buffer cur)))
796
797 (defun nnheader-replace-string (from to)
798 "Do a fast replacement of FROM to TO from point to point-max."
799 (nnheader-skeleton-replace from to))
800
801 (defun nnheader-replace-regexp (from to)
802 "Do a fast regexp replacement of FROM to TO from point to point-max."
803 (nnheader-skeleton-replace from to t))
804
805 (defun nnheader-strip-cr ()
806 "Strip all \r's from the current buffer."
807 (nnheader-skeleton-replace "\r"))
808
809 (fset 'nnheader-run-at-time 'run-at-time) 607 (fset 'nnheader-run-at-time 'run-at-time)
810 (fset 'nnheader-cancel-timer 'cancel-timer) 608 (fset 'nnheader-cancel-timer 'cancel-timer)
811 (fset 'nnheader-cancel-function-timers 'cancel-function-timers) 609 (fset 'nnheader-find-file-noselect 'find-file-noselect)
610 (fset 'nnheader-insert-file-contents-literally
611 'insert-file-contents-literally)
812 612
813 (when (string-match "XEmacs\\|Lucid" emacs-version) 613 (when (string-match "XEmacs\\|Lucid" emacs-version)
814 (require 'nnheaderxm)) 614 (require 'nnheaderxm))
815 615
816 (run-hooks 'nnheader-load-hook) 616 (run-hooks 'nnheader-load-hook)