comparison lisp/gnus/nnheader.el @ 98:0d2f883870bc r20-1b1

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