Mercurial > hg > xemacs-beta
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) |