1 ;;; nnshimbun.el --- interfacing with web newspapers -*- coding: junet; -*-
3 ;; Authors: TSUCHIYA Masatoshi <tsuchiya@pine.kuee.kyoto-u.ac.jp>
4 ;; Akihiro Arisawa <ari@atesoft.advantest.co.jp>
9 ;; This file is a part of Semi-Gnus.
11 ;; This program is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; This program is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with this program; if not, you can either send email to this
23 ;; program's maintainer or write to: The Free Software Foundation,
24 ;; Inc.; 59 Temple Place, Suite 330; Boston, MA 02111-1307, USA.
28 ;; Gnus backend to read newspapers on WEB.
33 (gnus-declare-backend "nnshimbun" 'address)
35 (eval-when-compile (require 'cl))
44 ;; Report failure to find w3 at load time if appropriate.
45 (eval '(require 'nnweb))
48 (nnoo-declare nnshimbun)
50 (defvar nnshimbun-check-interval 300)
52 (defconst nnshimbun-mew-groups
53 '(("meadow-develop" "meadow-develop" nil t)
54 ("meadow-users-jp" "meadow-users-jp")
55 ("mule-win32" "mule-win32")
56 ("mew-win32" "mew-win32")
57 ("mew-dist" "mew-dist/3300" t)
58 ("mgp-users-jp" "mgp-users-jp/A" t t)))
60 (defvar nnshimbun-type-definition
62 (url . "http://spin.asahi.com/")
63 (groups "national" "business" "politics" "international" "sports" "personal" "feneral")
64 (coding-system . ,(if (boundp 'MULE) '*sjis* 'shift_jis))
65 (generate-nov . nnshimbun-generate-nov-for-each-group)
66 (get-headers . nnshimbun-asahi-get-headers)
67 (index-url . (format "%sp%s.html" nnshimbun-url nnshimbun-current-group))
68 (from-address . "webmaster@www.asahi.com")
69 (make-contents . nnshimbun-make-text-or-html-contents)
70 (contents-start . "\n<!-- Start of kiji -->\n")
71 (contents-end . "\n<!-- End of kiji -->\n"))
73 (url . "http://www.sponichi.co.jp/")
74 (groups "baseball" "soccer" "usa" "others" "society" "entertainment" "horseracing")
75 (coding-system . ,(if (boundp 'MULE) '*sjis* 'shift_jis))
76 (generate-nov . nnshimbun-generate-nov-for-each-group)
77 (get-headers . nnshimbun-sponichi-get-headers)
78 (index-url . (format "%s%s/index.html" nnshimbun-url nnshimbun-current-group))
79 (from-address . "webmaster@www.sponichi.co.jp")
80 (make-contents . nnshimbun-make-text-or-html-contents)
81 (contents-start . "\n<span class=\"text\">
\e$B!!
\e(B")
82 (contents-end . "\n"))
84 (url . "http://cnet.sphere.ne.jp/")
86 (coding-system . ,(if (boundp 'MULE) '*sjis* 'shift_jis))
87 (generate-nov . nnshimbun-generate-nov-for-each-group)
88 (get-headers . nnshimbun-cnet-get-headers)
89 (index-url . (format "%s/News/Oneweek/" nnshimbun-url))
90 (from-address . "cnet@sphere.ad.jp")
91 (make-contents . nnshimbun-make-html-contents)
92 (contents-start . "\n<!--KIJI-->\n")
93 (contents-end . "\n<!--/KIJI-->\n"))
95 (url . "http://www.hotwired.co.jp/")
96 (groups "business" "culture" "technology")
97 (coding-system . ,(if (boundp 'MULE) '*euc-japan* 'euc-jp))
98 (generate-nov . nnshimbun-generate-nov-for-all-groups)
99 (get-headers . nnshimbun-wired-get-all-headers)
101 (from-address . "webmaster@www.hotwired.co.jp")
102 (make-contents . nnshimbun-make-html-contents)
103 (contents-start . "\n<!-- START_OF_BODY -->\n")
104 (contents-end . "\n<!-- END_OF_BODY -->\n"))
106 (url . "http://www.yomiuri.co.jp/")
107 (groups "shakai" "sports" "seiji" "keizai" "kokusai" "fuho")
108 (coding-system . ,(if (boundp 'MULE) '*sjis* 'shift_jis))
109 (generate-nov . nnshimbun-generate-nov-for-all-groups)
110 (get-headers . nnshimbun-yomiuri-get-all-headers)
111 (index-url . (concat nnshimbun-url "main.htm"))
112 (from-address . "webmaster@www.yomiuri.co.jp")
113 (make-contents . nnshimbun-make-text-or-html-contents)
114 (contents-start . "\n<!-- honbun start -->\n")
115 (contents-end . "\n<!-- honbun end -->\n"))
117 (url . "http://zdseek.pub.softbank.co.jp/news/")
119 (coding-system . ,(if (boundp 'MULE) '*sjis* 'shift_jis))
120 (generate-nov . nnshimbun-generate-nov-for-each-group)
121 (get-headers . nnshimbun-zdnet-get-headers)
122 (index-url . nnshimbun-url)
123 (from-address . "zdnn@softbank.co.jp")
124 (make-contents . nnshimbun-make-html-contents)
125 (contents-start . "\\(<!--BODY-->\\|[0-9]+
\e$BG/
\e(B[0-9]+
\e$B7n
\e(B[0-9]+
\e$BF|
\e(B[^<]*</font></td>[ \t\r\f\n]*</tr>[ \t\r\f\n]*</table>[ \t\r\f\n]*\\(</p>\\)?\\)")
126 (contents-end . "\\(<!--BODYEND-->\\|<div align=\"right\">\\|<\\(b\\|strong\\)>\\[</\\2>[^<]*<\\2>ZDNet/\\(JAPAN\\|USA\\)\\]\\(<[^>]+>\\)?</\\2>\\)"))
128 (url . "http://www.mew.org/archive/")
129 (groups ,@(mapcar #'car nnshimbun-mew-groups))
130 (coding-system . ,(if (boundp 'MULE) '*iso-2022-jp* 'iso-2022-jp))
131 (generate-nov . nnshimbun-generate-nov-for-each-group)
132 (get-headers . nnshimbun-mew-get-headers)
133 (index-url . (nnshimbun-mew-concat-url "index.html"))
134 (make-contents . nnshimbun-make-mhonarc-contents))
136 (url . "http://www.xemacs.org/list-archives/")
137 (groups "xemacs-announce" "xemacs-beta-ja" "xemacs-beta"
138 "xemacs-build-reports" "xemacs-cvs" "xemacs-mule"
139 "xemacs-nt" "xemacs-patches" "xemacs-users-ja" "xemacs")
140 (coding-system . ,(if (boundp 'MULE) '*euc-japan* 'euc-jp))
141 (generate-nov . nnshimbun-generate-nov-for-each-group)
142 (get-headers . nnshimbun-xemacs-get-headers)
143 (index-url . (nnshimbun-xemacs-concat-url nil))
144 (make-contents . nnshimbun-make-mhonarc-contents))
147 (defvar nnshimbun-x-face-alist
150 "X-Face: Ygq$6P.,%Xt$U)DS)cRY@k$VkW!7(X'X'?U{{osjjFG\"E]hND;SPJ-J?O?R|a?L
151 g2$0rVng=O3Lt}?~IId8Jj&vP^3*o=LKUyk(`t%0c!;t6REk=JbpsEn9MrN7gZ%"))))
152 "Alist of server vs. alist of group vs. X-Face field. It looks like:
154 \((\"asahi\" . ((\"national\" . \"X-face: ***\")
155 (\"business\" . \"X-Face: ***\")
158 (\"default\" . \"X-face: ***\")))
159 (\"sponichi\" . ((\"baseball\" . \"X-face: ***\")
160 (\"soccer\" . \"X-Face: ***\")
163 (\"default\" . \"X-face: ***\")))
165 (\"default\" . ((\"default\" . \"X-face: ***\")))")
167 (defvoo nnshimbun-directory (nnheader-concat gnus-directory "shimbun/")
168 "Where nnshimbun will save its files.")
170 (defvoo nnshimbun-nov-is-evil nil
171 "*Non-nil means that nnshimbun will never retrieve NOV headers.")
173 (defvoo nnshimbun-nov-file-name ".overview")
175 (defvoo nnshimbun-pre-fetch-article nil
176 "*Non nil means that nnshimbun fetch unread articles when scanning groups.")
178 ;; set by nnshimbun-possibly-change-group
179 (defvoo nnshimbun-buffer nil)
180 (defvoo nnshimbun-current-directory nil)
181 (defvoo nnshimbun-current-group nil)
183 ;; set by nnshimbun-open-server
184 (defvoo nnshimbun-url nil)
185 (defvoo nnshimbun-coding-system nil)
186 (defvoo nnshimbun-groups nil)
187 (defvoo nnshimbun-generate-nov nil)
188 (defvoo nnshimbun-get-headers nil)
189 (defvoo nnshimbun-index-url nil)
190 (defvoo nnshimbun-from-address nil)
191 (defvoo nnshimbun-make-contents nil)
192 (defvoo nnshimbun-contents-start nil)
193 (defvoo nnshimbun-contents-end nil)
194 (defvoo nnshimbun-server-directory nil)
196 (defvoo nnshimbun-status-string "")
197 (defvoo nnshimbun-nov-last-check nil)
198 (defvoo nnshimbun-nov-buffer-alist nil)
199 (defvoo nnshimbun-nov-buffer-file-name nil)
201 (defvoo nnshimbun-keep-backlog 300)
202 (defvoo nnshimbun-backlog-articles nil)
203 (defvoo nnshimbun-backlog-hashtb nil)
208 (defmacro nnshimbun-backlog (&rest form)
209 `(let ((gnus-keep-backlog nnshimbun-keep-backlog)
210 (gnus-backlog-buffer (format " *nnshimbun backlog %s*" (nnoo-current-server 'nnshimbun)))
211 (gnus-backlog-articles nnshimbun-backlog-articles)
212 (gnus-backlog-hashtb nnshimbun-backlog-hashtb))
215 (setq nnshimbun-backlog-articles gnus-backlog-articles
216 nnshimbun-backlog-hashtb gnus-backlog-hashtb))))
217 (put 'nnshimbun-backlog 'lisp-indent-function 0)
218 (put 'nnshimbun-backlog 'edebug-form-spec '(form body))
222 ;;; Interface Functions
223 (nnoo-define-basics nnshimbun)
225 (deffoo nnshimbun-open-server (server &optional defs)
226 ;; Set default values.
227 (dolist (default (cdr (assoc server nnshimbun-type-definition)))
228 (let ((symbol (intern (concat "nnshimbun-" (symbol-name (car default))))))
229 (unless (assq symbol defs)
230 (push (list symbol (cdr default)) defs))))
231 ;; Set directory for server working files.
232 (push (list 'nnshimbun-server-directory
233 (file-name-as-directory
234 (expand-file-name server nnshimbun-directory)))
236 (nnoo-change-server 'nnshimbun server defs)
237 (nnshimbun-possibly-change-group nil server)
239 (unless (file-exists-p nnshimbun-directory)
240 (ignore-errors (make-directory nnshimbun-directory t)))
242 ((not (file-exists-p nnshimbun-directory))
243 (nnshimbun-close-server)
244 (nnheader-report 'nnshimbun "Couldn't create directory: %s" nnshimbun-directory))
245 ((not (file-directory-p (file-truename nnshimbun-directory)))
246 (nnshimbun-close-server)
247 (nnheader-report 'nnshimbun "Not a directory: %s" nnshimbun-directory))
249 (unless (file-exists-p nnshimbun-server-directory)
250 (ignore-errors (make-directory nnshimbun-server-directory t)))
252 ((not (file-exists-p nnshimbun-server-directory))
253 (nnshimbun-close-server)
254 (nnheader-report 'nnshimbun "Couldn't create directory: %s" nnshimbun-server-directory))
255 ((not (file-directory-p (file-truename nnshimbun-server-directory)))
256 (nnshimbun-close-server)
257 (nnheader-report 'nnshimbun "Not a directory: %s" nnshimbun-server-directory))
259 (nnheader-report 'nnshimbun "Opened server %s using directory %s"
260 server nnshimbun-server-directory)
263 (deffoo nnshimbun-close-server (&optional server)
264 (and (nnshimbun-server-opened server)
265 (gnus-buffer-live-p nnshimbun-buffer)
266 (kill-buffer nnshimbun-buffer))
267 (nnshimbun-backlog (gnus-backlog-shutdown))
269 (nnoo-close-server 'nnshimbun server)
272 (defun nnshimbun-retrieve-url (url &optional no-cache)
273 "Rertrieve URL contents and insert to current buffer."
274 (let ((coding-system-for-read 'binary)
275 (coding-system-for-write 'binary))
276 (set-buffer-multibyte nil)
277 ;; Following code is imported from `url-insert-file-contents'.
279 (let ((old-asynch (default-value 'url-be-asynchronous))
280 (old-caching (default-value 'url-automatic-caching))
281 (old-mode (default-value 'url-standalone-mode)))
284 (setq-default url-be-asynchronous nil)
286 (setq-default url-automatic-caching nil)
287 (setq-default url-standalone-mode nil))
288 (let ((buf (current-buffer))
289 (url-working-buffer (cdr (url-retrieve url no-cache))))
290 (set-buffer url-working-buffer)
293 (insert-buffer url-working-buffer)
295 (set-buffer url-working-buffer)
296 (set-buffer-modified-p nil))
297 (kill-buffer url-working-buffer)))
298 (setq-default url-be-asynchronous old-asynch)
299 (setq-default url-automatic-caching old-caching)
300 (setq-default url-standalone-mode old-mode))))
301 ;; Modify buffer coding system.
302 (decode-coding-region (point-min) (point-max) nnshimbun-coding-system)
303 (set-buffer-multibyte t)))
305 (deffoo nnshimbun-request-article (article &optional group server to-buffer)
306 (when (nnshimbun-possibly-change-group group server)
307 (if (stringp article)
308 (setq article (nnshimbun-search-id group article)))
309 (if (integerp article)
310 (nnshimbun-request-article-1 article group server to-buffer)
311 (nnheader-report 'nnml "Couldn't retrieve article: %s" (prin1-to-string article))
314 (defun nnshimbun-request-article-1 (article &optional group server to-buffer)
315 (if (nnshimbun-backlog
316 (gnus-backlog-request-article
317 group article (or to-buffer nntp-server-buffer)))
319 (let (header contents)
320 (when (setq header (save-excursion
321 (set-buffer (nnshimbun-open-nov group))
322 (and (nnheader-find-nov-line article)
323 (nnheader-parse-nov))))
324 (let* ((xref (substring (mail-header-xref header) 6))
325 (x-faces (cdr (or (assoc (or server
326 (nnoo-current-server 'nnshimbun))
327 nnshimbun-x-face-alist)
328 (assoc "default" nnshimbun-x-face-alist))))
329 (x-face (cdr (or (assoc group x-faces)
330 (assoc "default" x-faces)))))
332 (set-buffer nnshimbun-buffer)
334 (nnshimbun-retrieve-url xref)
335 (nnheader-message 6 "nnshimbun: Make contents...")
336 (goto-char (point-min))
337 (setq contents (funcall nnshimbun-make-contents header x-face))
338 (nnheader-message 6 "nnshimbun: Make contents...done"))))
341 (set-buffer (or to-buffer nntp-server-buffer))
345 (gnus-backlog-enter-article group article (current-buffer)))
346 (nnheader-report 'nnshimbun "Article %s retrieved" (mail-header-id header))
347 (cons group (mail-header-number header)))))))
349 (deffoo nnshimbun-request-group (group &optional server dont-check)
350 (let ((pathname-coding-system 'binary))
352 ((not (nnshimbun-possibly-change-group group server))
353 (nnheader-report 'nnshimbun "Invalid group (no such directory)"))
354 ((not (file-exists-p nnshimbun-current-directory))
355 (nnheader-report 'nnshimbun "Directory %s does not exist"
356 nnshimbun-current-directory))
357 ((not (file-directory-p nnshimbun-current-directory))
358 (nnheader-report 'nnshimbun "%s is not a directory" nnshimbun-current-directory))
360 (nnheader-report 'nnshimbun "Group %s selected" group)
365 (set-buffer (nnshimbun-open-nov group))
366 (goto-char (point-min))
367 (setq beg (ignore-errors (read (current-buffer))))
368 (goto-char (point-max))
370 (setq end (ignore-errors (read (current-buffer)))
371 lines (count-lines (point-min) (point-max))))
372 (nnheader-report 'nnshimbunw "Selected group %s" group)
373 (nnheader-insert "211 %d %d %d %s\n"
374 lines (or beg 0) (or end 0) group))))))
376 (deffoo nnshimbun-request-scan (&optional group server)
377 (nnshimbun-possibly-change-group group server)
378 (nnshimbun-generate-nov-database group))
380 (deffoo nnshimbun-close-group (group &optional server)
381 (nnshimbun-write-nov group)
384 (deffoo nnshimbun-request-list (&optional server)
386 (set-buffer nntp-server-buffer)
388 (dolist (group nnshimbun-groups)
389 (when (nnshimbun-possibly-change-group group server)
392 (set-buffer (nnshimbun-open-nov group))
393 (goto-char (point-min))
394 (setq beg (ignore-errors (read (current-buffer))))
395 (goto-char (point-max))
397 (setq end (ignore-errors (read (current-buffer)))))
398 (insert (format "%s %d %d n\n" group (or end 0) (or beg 0)))))))
402 (if (fboundp 'mime-entity-fetch-field)
404 (defun nnshimbun-insert-header (header)
405 (insert "Subject: " (or (mime-entity-fetch-field header 'Subject) "(none)") "\n"
406 "From: " (or (mime-entity-fetch-field header 'From) "(nobody)") "\n"
407 "Date: " (or (mail-header-date header) "") "\n"
408 "Message-ID: " (or (mail-header-id header) (nnmail-message-id)) "\n"
409 "References: " (or (mail-header-references header) "") "\n"
411 (princ (or (mail-header-lines header) 0) (current-buffer))
413 (if (mail-header-xref header)
414 (insert (mail-header-xref header) "\n")))
416 (defun nnshimbun-insert-header (header)
417 (nnheader-insert-header header)
419 (if (mail-header-xref header)
420 (insert (mail-header-xref header) "\n")))))
422 (deffoo nnshimbun-retrieve-headers (articles &optional group server fetch-old)
423 (when (nnshimbun-possibly-change-group group server)
424 (if (nnshimbun-retrieve-headers-with-nov articles fetch-old)
427 (set-buffer nntp-server-buffer)
430 (dolist (art articles)
432 (setq art (nnshimbun-search-id group art)))
436 (set-buffer (nnshimbun-open-nov group))
437 (and (nnheader-find-nov-line art)
438 (nnheader-parse-nov))))
439 (insert (format "220 %d Article retrieved.\n" art))
440 (nnshimbun-insert-header header)
442 (delete-region (point) (point-max))))))
445 (defun nnshimbun-retrieve-headers-with-nov (articles &optional fetch-old)
446 (if (or gnus-nov-is-evil nnshimbun-nov-is-evil)
448 (let ((nov (expand-file-name nnshimbun-nov-file-name nnshimbun-current-directory)))
449 (when (file-exists-p nov)
451 (set-buffer nntp-server-buffer)
453 (nnheader-insert-file-contents nov)
454 (if (and fetch-old (not (numberp fetch-old)))
455 t ; Don't remove anything.
456 (nnheader-nov-delete-outside-range
457 (if fetch-old (max 1 (- (car articles) fetch-old))
459 (car (last articles)))
464 ;;; Nov Database Operations
466 (defun nnshimbun-generate-nov-database (group)
467 (prog1 (funcall nnshimbun-generate-nov group)
468 (nnshimbun-write-nov group)))
470 (defun nnshimbun-generate-nov-for-each-group (group)
471 (nnshimbun-possibly-change-group group)
473 (set-buffer (nnshimbun-open-nov group))
475 (goto-char (point-max))
477 (setq i (or (ignore-errors (read (current-buffer))) 0))
478 (dolist (header (save-excursion
479 (set-buffer nnshimbun-buffer)
481 (nnshimbun-retrieve-url (eval nnshimbun-index-url) t)
482 (goto-char (point-min))
483 (funcall nnshimbun-get-headers)))
484 (unless (nnshimbun-search-id group (mail-header-id header))
485 (mail-header-set-number header (setq i (1+ i)))
486 (goto-char (point-max))
487 (nnheader-insert-nov header)
488 (if nnshimbun-pre-fetch-article
489 (nnshimbun-request-article-1 i group nil nnshimbun-buffer)))))))
491 (defun nnshimbun-generate-nov-for-all-groups (&rest args)
492 (unless (and nnshimbun-nov-last-check
493 (< (nnshimbun-lapse-seconds nnshimbun-nov-last-check)
494 nnshimbun-check-interval))
496 (dolist (list (funcall nnshimbun-get-headers))
497 (let ((group (car list)))
498 (nnshimbun-possibly-change-group group)
500 (set-buffer (nnshimbun-open-nov group))
502 (goto-char (point-max))
504 (setq i (or (ignore-errors (read (current-buffer))) 0))
505 (dolist (header (cdr list))
506 (unless (nnshimbun-search-id group (mail-header-id header))
507 (mail-header-set-number header (setq i (1+ i)))
508 (goto-char (point-max))
509 (nnheader-insert-nov header)
510 (if nnshimbun-pre-fetch-article
511 (nnshimbun-request-article-1 i group nil nnshimbun-buffer))))))))
513 (setq nnshimbun-nov-last-check (current-time)))))
515 (defun nnshimbun-search-id (group id &optional nov)
517 (set-buffer (nnshimbun-open-nov group))
518 (goto-char (point-min))
520 (while (and (not found)
521 (search-forward id nil t)) ; We find the ID.
522 ;; And the id is in the fourth field.
523 (if (not (and (search-backward "\t" nil t 4)
524 (not (search-backward "\t" (gnus-point-at-bol) t))))
529 (goto-char (point-min))
530 (when (search-forward (concat "X-Nnshimbun-Original-Id: " id) nil t)
536 ;; We return the article number.
537 (ignore-errors (read (current-buffer))))))))
539 (defun nnshimbun-nov-fix-header (group header args)
541 (set-buffer (nnshimbun-open-nov group))
542 (when (nnheader-find-nov-line (mail-header-number header))
544 (if (eq (car arg) 'id)
545 (let ((extra (mail-header-extra header)) x)
546 (when (setq x (assq 'X-Nnshimbun-Original-Id extra))
547 (setq extra (delq x extra)))
548 (mail-header-set-extra
550 (cons (cons 'X-Nnshimbun-Original-Id (cdr arg)) extra)))
551 (let ((func (intern (concat "mail-header-set-" (symbol-name (car arg))))))
552 (if (cdr arg) (eval (list func header (cdr arg)))))))
553 (let ((xref (mail-header-xref header)))
554 (when (string-match "^Xref: " xref)
555 (mail-header-set-xref header (substring xref 6))))
556 (delete-region (point) (progn (forward-line 1) (point)))
557 (nnheader-insert-nov header))))
559 (defun nnshimbun-open-nov (group)
560 (let ((buffer (cdr (assoc group nnshimbun-nov-buffer-alist))))
561 (if (buffer-live-p buffer)
563 (setq buffer (gnus-get-buffer-create
564 (format " *nnshimbun overview %s %s*"
565 (nnoo-current-server 'nnshimbun) group)))
568 (set (make-local-variable 'nnshimbun-nov-buffer-file-name)
570 nnshimbun-nov-file-name
571 (nnmail-group-pathname group nnshimbun-server-directory)))
573 (when (file-exists-p nnshimbun-nov-buffer-file-name)
574 (nnheader-insert-file-contents nnshimbun-nov-buffer-file-name))
575 (set-buffer-modified-p nil))
576 (push (cons group buffer) nnshimbun-nov-buffer-alist)
579 (defun nnshimbun-write-nov (group)
580 (let ((buffer (cdr (assoc group nnshimbun-nov-buffer-alist))))
581 (when (buffer-live-p buffer)
585 (nnmail-write-region 1 (point-max) nnshimbun-nov-buffer-file-name
588 (defun nnshimbun-save-nov ()
590 (while nnshimbun-nov-buffer-alist
591 (when (buffer-name (cdar nnshimbun-nov-buffer-alist))
592 (set-buffer (cdar nnshimbun-nov-buffer-alist))
593 (when (buffer-modified-p)
594 (nnmail-write-region 1 (point-max) nnshimbun-nov-buffer-file-name
596 (set-buffer-modified-p nil)
597 (kill-buffer (current-buffer)))
598 (setq nnshimbun-nov-buffer-alist (cdr nnshimbun-nov-buffer-alist)))))
602 ;;; Server Initialize
603 (defun nnshimbun-possibly-change-group (group &optional server)
605 (unless (nnshimbun-server-opened server)
606 (nnshimbun-open-server server)))
607 (unless (gnus-buffer-live-p nnshimbun-buffer)
608 (setq nnshimbun-buffer
610 (nnheader-set-temp-buffer
611 (format " *nnshimbun %s*" (nnoo-current-server 'nnshimbun))))))
614 (let ((pathname (nnmail-group-pathname group nnshimbun-server-directory))
615 (pathname-coding-system 'binary))
616 (unless (equal pathname nnshimbun-current-directory)
617 (setq nnshimbun-current-directory pathname
618 nnshimbun-current-group group))
619 (unless (file-exists-p nnshimbun-current-directory)
620 (ignore-errors (make-directory nnshimbun-current-directory t)))
622 ((not (file-exists-p nnshimbun-current-directory))
623 (nnheader-report 'nnshimbun "Couldn't create directory: %s" nnshimbun-current-directory))
624 ((not (file-directory-p (file-truename nnshimbun-current-directory)))
625 (nnheader-report 'nnshimbun "Not a directory: %s" nnshimbun-current-directory))
633 (if (fboundp 'eword-encode-string)
635 (defun nnshimbun-mime-encode-string (string)
638 (split-string (eword-encode-string (nnweb-decode-entities-string string)) "\n")
641 (defun nnshimbun-mime-encode-string (string)
646 (insert (nnweb-decode-entities-string string))
647 (rfc2047-encode-region (point-min) (point-max))
648 (buffer-substring (point-min) (point-max)))
652 (defun nnshimbun-lapse-seconds (time)
653 (let ((now (current-time)))
654 (+ (* (- (car now) (car time)) 65536)
655 (- (nth 1 now) (nth 1 time)))))
657 (defun nnshimbun-make-date-string (year month day &optional time)
658 (format "%02d %s %04d %s +0900"
660 (aref [nil "Jan" "Feb" "Mar" "Apr" "May" "Jun"
661 "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"]
667 ((< year 1000) ; possible 3-digit years.
672 (if (fboundp 'regexp-opt)
673 (defalias 'nnshimbun-regexp-opt 'regexp-opt)
674 (defun nnshimbun-regexp-opt (strings &optional paren)
675 "Return a regexp to match a string in STRINGS.
676 Each string should be unique in STRINGS and should not contain any regexps,
677 quoted or not. If optional PAREN is non-nil, ensure that the returned regexp
678 is enclosed by at least one regexp grouping construct."
679 (let ((open-paren (if paren "\\(" "")) (close-paren (if paren "\\)" "")))
680 (concat open-paren (mapconcat 'regexp-quote strings "\\|") close-paren))))
683 ;; Fast fill-region function
685 (defvar nnshimbun-fill-column (min 80 (- (frame-width) 4)))
687 (defconst nnshimbun-kinsoku-bol-list
689 (if (fboundp 'string-to-char-list)
692 !)-_~}]:;',.?
\e$B!"!#!$!%!&!'!(!)!*!+!,!-!.!/!0!1!2!3!4!5!6!7!8!9!:!;!<!=!>!?!@!A
\e(B\
693 \e$B!B!C!D!E!G!I!K!M!O!Q!S!U!W!Y![!k!l!m!n$!$#$%$'$)$C$c$e$g$n%!%#%%%'%)%C%c%e%g%n%u%v
\e(B"))
695 (defconst nnshimbun-kinsoku-eol-list
697 (if (fboundp 'string-to-char-list)
700 "({[`
\e$B!F!H!J!L!N!P!R!T!V!X!Z!k!l!m!x
\e(B"))
702 (defun nnshimbun-fill-line ()
704 (let ((top (point)) chr)
705 (while (if (>= (move-to-column nnshimbun-fill-column)
706 nnshimbun-fill-column)
708 (if (memq (preceding-char) nnshimbun-kinsoku-eol-list)
711 (while (memq (preceding-char) nnshimbun-kinsoku-eol-list)
714 (while (memq (setq chr (following-char)) nnshimbun-kinsoku-bol-list)
716 (if (looking-at "\\s-+")
717 (or (eolp) (delete-region (point) (match-end 0)))
718 (or (> (char-width chr) 1)
719 (re-search-backward "\\<" top t)
721 (or (eolp) (insert "\n"))))))
726 (defsubst nnshimbun-shallow-rendering ()
727 (goto-char (point-min))
728 (while (search-forward "<p>" nil t)
730 (goto-char (point-min))
731 (while (search-forward "<br>" nil t)
733 (nnweb-remove-markup)
734 (nnweb-decode-entities)
735 (goto-char (point-min))
736 (while (nnshimbun-fill-line))
737 (goto-char (point-min))
738 (when (skip-chars-forward "\n")
739 (delete-region (point-min) (point)))
740 (while (search-forward "\n\n" nil t)
742 (when (skip-chars-forward "\n")
743 (delete-region p (point)))))
744 (goto-char (point-max))
745 (when (skip-chars-backward "\n")
746 (delete-region (point) (point-max)))
749 (defun nnshimbun-make-text-or-html-contents (header &optional x-face)
750 (let ((case-fold-search t) (html t) (start))
751 (when (and (re-search-forward nnshimbun-contents-start nil t)
753 (re-search-forward nnshimbun-contents-end nil t))
754 (delete-region (match-beginning 0) (point-max))
755 (delete-region (point-min) start)
756 (nnshimbun-shallow-rendering)
758 (goto-char (point-min))
759 (nnshimbun-insert-header header)
760 (insert "Content-Type: " (if html "text/html" "text/plain")
761 "; charset=ISO-2022-JP\nMIME-Version: 1.0\n")
767 (encode-coding-string (buffer-string)
768 (mime-charset-to-coding-system "ISO-2022-JP"))))
770 (defun nnshimbun-make-html-contents (header &optional x-face)
772 (when (and (re-search-forward nnshimbun-contents-start nil t)
774 (re-search-forward nnshimbun-contents-end nil t))
775 (delete-region (match-beginning 0) (point-max))
776 (delete-region (point-min) start))
777 (goto-char (point-min))
778 (nnshimbun-insert-header header)
779 (insert "Content-Type: text/html; charset=ISO-2022-JP\n"
780 "MIME-Version: 1.0\n")
786 (encode-coding-string (buffer-string)
787 (mime-charset-to-coding-system "ISO-2022-JP"))))
789 (defun nnshimbun-make-mhonarc-contents (header &rest args)
791 (if (search-forward "<!--X-Head-End-->" nil t)
794 ;; Processing headers.
796 (narrow-to-region (point-min) (point))
797 (nnweb-decode-entities)
798 (goto-char (point-min))
799 (while (search-forward "<!--X-" nil t)
801 (goto-char (point-min))
802 (while (search-forward " -->" nil t)
804 (goto-char (point-min))
809 (delete-region (point) (progn (forward-line 1) (point))))
810 ((looking-at "Message-Id: ")
811 (setq id (concat "<" (nnheader-header-value) ">"))
813 ((looking-at "Reference: ")
814 (push (concat "<" (nnheader-header-value) ">") refs)
815 (delete-region (point) (progn (forward-line 1) (point))))
816 ((looking-at "Content-Type: ")
817 (unless (search-forward "charset" (gnus-point-at-eol) t)
819 (insert "; charset=ISO-2022-JP"))
821 (t (forward-line 1))))
825 (setq ref (nnshimbun-search-id nnshimbun-current-group ref 'nov))
826 (push (mail-header-id ref) buf)))
828 (insert "References: "
829 (setq refs (mapconcat #'identity refs " "))
830 "\nMIME-Version: 1.0\n")
831 (nnshimbun-nov-fix-header nnshimbun-current-group
834 (references . ,refs))))
835 (goto-char (point-max)))
838 (narrow-to-region (point) (point-max))
842 (search-forward "\n<!--X-Body-of-Message-->\n" nil t)
844 (when (search-forward "\n<!--X-Body-of-Message-End-->\n" nil t)
846 (delete-region (point) (point-max)))
847 (nnweb-remove-markup)
848 (nnweb-decode-entities)))
849 (goto-char (point-min))
850 (nnshimbun-insert-header header)
851 (insert "Content-Type: text/html; charset=ISO-2022-JP\nMIME-Version: 1.0\n\n"))
852 (encode-coding-string (buffer-string)
853 (mime-charset-to-coding-system "ISO-2022-JP")))
858 (defun nnshimbun-asahi-get-headers ()
859 (when (search-forward "\n<!-- Start of past -->\n" nil t)
860 (delete-region (point-min) (point))
861 (when (search-forward "\n<!-- End of past -->\n" nil t)
863 (delete-region (point) (point-max))
864 (goto-char (point-min))
866 (while (re-search-forward
867 "^
\e$B"#
\e(B<a href=\"\\(\\([0-9][0-9][0-9][0-9]\\)/past/\\([A-z]*[0-9]*\\)\\.html\\)\"> *"
869 (let ((id (format "<%s%s%%%s>"
872 nnshimbun-current-group))
873 (url (match-string 1)))
874 (push (make-full-mail-header
876 (nnshimbun-mime-encode-string
881 (progn (search-forward "<br>" nil t) (point)))
882 "\\(<[^>]+>\\|\r\\)")
884 nnshimbun-from-address
885 "" id "" 0 0 (concat nnshimbun-url url))
887 (setq headers (nreverse headers))
889 (while (and (nth i headers)
891 "^\\[\\([0-9][0-9]\\)/\\([0-9][0-9]\\) \\([0-9][0-9]:[0-9][0-9]\\)\\]"
893 (let ((month (string-to-number (match-string 1)))
894 (date (decode-time (current-time))))
895 (mail-header-set-date
897 (nnshimbun-make-date-string
898 (if (and (eq 12 month) (eq 1 (nth 4 date)))
902 (string-to-number (match-string 2))
905 (nreverse headers)))))
909 ;;; www.sponichi.co.jp
911 (defun nnshimbun-sponichi-get-headers ()
912 (when (search-forward "
\e$B%K%e!<%9%$%s%G%C%/%9
\e(B" nil t)
913 (delete-region (point-min) (point))
914 (when (search-forward "
\e$B%"%I%?%0
\e(B" nil t)
916 (delete-region (point) (point-max))
917 (goto-char (point-min))
918 (let ((case-fold-search t) headers)
919 (while (re-search-forward
920 "^<a href=\"/\\(\\([A-z]*\\)/kiji/\\([0-9][0-9][0-9][0-9]\\)/\\([0-9][0-9]\\)/\\([0-9][0-9]\\)/\\([0-9][0-9]\\)\\.html\\)\">"
922 (let ((url (match-string 1))
923 (id (format "<%s%s%s%s%%%s>"
928 nnshimbun-current-group))
929 (date (nnshimbun-make-date-string
930 (string-to-number (match-string 3))
931 (string-to-number (match-string 4))
932 (string-to-number (match-string 5)))))
933 (push (make-full-mail-header
935 (nnshimbun-mime-encode-string
940 (progn (search-forward "<br>" nil t) (point)))
943 nnshimbun-from-address
944 date id "" 0 0 (concat nnshimbun-url url))
952 (defun nnshimbun-cnet-get-headers ()
953 (let ((case-fold-search t) headers)
954 (while (search-forward "\n<!--*****
\e$B8+=P$7
\e(B*****-->\n" nil t)
955 (let ((subject (buffer-substring (point) (gnus-point-at-eol)))
958 (when (looking-at "<a href=\"/\\(News/\\([0-9][0-9][0-9][0-9]\\)/Item/\\([0-9][0-9]\\([0-9][0-9]\\)\\([0-9][0-9]\\)-[0-9]+\\).html\\)\">")
959 (let ((url (match-string 1))
960 (id (format "<%s%s%%%s>"
963 nnshimbun-current-group))
964 (date (nnshimbun-make-date-string
965 (string-to-number (match-string 2))
966 (string-to-number (match-string 4))
967 (string-to-number (match-string 5)))))
968 (push (make-full-mail-header
970 (nnshimbun-mime-encode-string subject)
971 nnshimbun-from-address
972 date id "" 0 0 (concat nnshimbun-url url))
981 (defun nnshimbun-wired-get-all-headers ()
983 (set-buffer nnshimbun-buffer)
984 (let ((group-header-alist (mapcar (lambda (g) (cons g nil)) nnshimbun-groups))
987 "<a href=\"\\(%s\\|/\\)\\(news/news/\\(%s\\)/story/\\(\\([0-9][0-9][0-9][0-9]\\)\\([0-9][0-9]\\)\\([0-9][0-9]\\)[0-9]+\\)\\.html\\)\"><b>"
988 (regexp-quote nnshimbun-url)
989 (nnshimbun-regexp-opt nnshimbun-groups))))
990 (dolist (xover (list (concat nnshimbun-url "news/news/index.html")
991 (concat nnshimbun-url "news/news/last_seven.html")))
993 (nnshimbun-retrieve-url xover t)
994 (goto-char (point-min))
995 (while (re-search-forward regexp nil t)
996 (let* ((url (concat nnshimbun-url (match-string 2)))
997 (group (downcase (match-string 3)))
998 (id (format "<%s%%%s>" (match-string 4) group))
999 (date (nnshimbun-make-date-string
1000 (string-to-number (match-string 5))
1001 (string-to-number (match-string 6))
1002 (string-to-number (match-string 7))))
1003 (header (make-full-mail-header
1005 (nnshimbun-mime-encode-string
1006 (mapconcat 'identity
1010 (progn (search-forward "</b>" nil t) (point)))
1013 nnshimbun-from-address
1014 date id "" 0 0 url))
1015 (x (assoc group group-header-alist)))
1016 (setcdr x (cons header (cdr x))))))
1017 group-header-alist)))
1021 ;;; www.yomiuri.co.jp
1023 (defun nnshimbun-yomiuri-get-all-headers ()
1025 (set-buffer nnshimbun-buffer)
1027 (nnshimbun-retrieve-url (eval nnshimbun-index-url) t)
1028 (let ((case-fold-search t)
1029 (group-header-alist (mapcar (lambda (g) (cons g nil)) nnshimbun-groups)))
1030 (dolist (group nnshimbun-groups)
1032 (goto-char (point-min))
1033 (when (and (search-forward (format "\n<!-- /news/%s=start -->\n" group) nil t)
1034 (setq start (point))
1035 (search-forward (format "\n<!-- /news/%s=end -->\n" group) nil t))
1038 (narrow-to-region start (point))
1040 (while (re-search-forward
1041 "<a href=\"/\\([0-9]+\\)/\\(\\(\\([0-9][0-9][0-9][0-9]\\)\\([0-9][0-9]\\)\\([0-9][0-9]\\)[A-z0-9]+\\)\\.htm\\)\"[^>]*>"
1043 (let ((url (concat (match-string 1) "a/" (match-string 2)))
1044 (id (format "<%s%s%%%s>"
1048 (year (string-to-number (match-string 4)))
1049 (month (string-to-number (match-string 5)))
1050 (day (string-to-number (match-string 6)))
1056 (progn (search-forward "<br>" nil t) (point)))
1060 (when (string-match "^
\e$B"!
\e(B" subject)
1061 (setq subject (substring subject (match-end 0))))
1062 (if (string-match "(\\([0-9][0-9]:[0-9][0-9]\\))$" subject)
1063 (setq date (nnshimbun-make-date-string
1064 year month day (match-string 1 subject))
1065 subject (substring subject 0 (match-beginning 0)))
1066 (setq date (nnshimbun-make-date-string year month day)))
1067 (setcdr (setq x (assoc group group-header-alist))
1068 (cons (make-full-mail-header
1070 (nnshimbun-mime-encode-string subject)
1071 nnshimbun-from-address
1072 date id "" 0 0 (concat nnshimbun-url url))
1074 group-header-alist)))
1080 (defun nnshimbun-zdnet-get-headers ()
1081 (let ((case-fold-search t) headers)
1082 (goto-char (point-min))
1084 (while (and (search-forward "<!--" nil t)
1085 (setq start (- (point) 4))
1086 (search-forward "-->" nil t))
1087 (delete-region start (point))))
1088 (goto-char (point-min))
1089 (while (re-search-forward
1090 "<a href=\"\\(\\([0-9][0-9]\\)\\([0-9][0-9]\\)/\\([0-9][0-9]\\)/\\([^\\.]+\\).html\\)\"><font size=\"4\"><strong>"
1092 (let ((year (+ 2000 (string-to-number (match-string 2))))
1093 (month (string-to-number (match-string 3)))
1094 (day (string-to-number (match-string 4)))
1095 (id (format "<%s%s%s%s%%%s>"
1100 nnshimbun-current-group))
1101 (url (match-string 1)))
1102 (push (make-full-mail-header
1104 (nnshimbun-mime-encode-string
1105 (mapconcat 'identity
1109 (progn (search-forward "</a>" nil t) (point)))
1112 nnshimbun-from-address
1113 (nnshimbun-make-date-string year month day)
1114 id "" 0 0 (concat nnshimbun-url url))
1116 (nreverse headers)))
1118 ;;; MLs on www.mew.org
1120 (defmacro nnshimbun-mew-concat-url (url)
1121 `(concat nnshimbun-url
1122 (nth 1 (assoc nnshimbun-current-group nnshimbun-mew-groups))
1126 (defmacro nnshimbun-mew-reverse-order-p ()
1127 `(nth 2 (assoc nnshimbun-current-group nnshimbun-mew-groups)))
1129 (defmacro nnshimbun-mew-spew-p ()
1130 `(nth 3 (assoc nnshimbun-current-group nnshimbun-mew-groups)))
1132 (defsubst nnshimbun-mew-retrieve-xover (aux)
1134 (nnshimbun-retrieve-url
1135 (nnshimbun-mew-concat-url (if (= aux 1) "index.html" (format "mail%d.html" aux)))
1138 (defconst nnshimbun-mew-regexp "<A[^>]*HREF=\"\\(msg\\([0-9]+\\).html\\)\">\\([^<]+\\)<")
1140 (defmacro nnshimbun-mew-extract-header-values ()
1142 (setq url (nnshimbun-mew-concat-url (match-string 1))
1143 id (format "<%05d%%%s>"
1144 (1- (string-to-number (match-string 2)))
1145 nnshimbun-current-group)
1146 subject (match-string 3))
1148 (if (nnshimbun-search-id nnshimbun-current-group id)
1149 (throw 'stop headers)
1150 (push (make-full-mail-header
1152 (nnshimbun-mime-encode-string subject)
1153 (if (looking-at "<EM>\\([^<]+\\)<")
1154 (nnshimbun-mime-encode-string (match-string 1))
1160 (if (fboundp 'mime-entity-fetch-field)
1162 (defmacro nnshimbun-mew-mail-header-subject (header)
1163 `(mime-entity-fetch-field ,header 'Subject))
1165 (defalias 'nnshimbun-mew-mail-header-subject 'mail-header-subject)))
1167 (defun nnshimbun-mew-get-headers ()
1168 (if (nnshimbun-mew-spew-p)
1169 (let ((headers (nnshimbun-mew-get-headers-1)))
1171 (insert-buffer-substring (nnshimbun-open-nov nnshimbun-current-group))
1175 (goto-char (point-min))
1176 (let ((subject (nnshimbun-mew-mail-header-subject header))
1178 (while (and (not found)
1179 (search-forward subject nil t))
1180 (if (not (and (search-backward "\t" nil t)
1181 (not (search-backward "\t" (gnus-point-at-bol) t))))
1186 (goto-char (point-max))
1187 (nnheader-insert-nov header)
1190 (nnshimbun-mew-get-headers-1)))
1192 (defun nnshimbun-mew-get-headers-1 ()
1194 (when (re-search-forward
1195 "<A[^>]*HREF=\"mail\\([0-9]+\\)\\.html\">\\[?Last Page\\]?</A>" nil t)
1196 (let ((limit (string-to-number (match-string 1))))
1198 (if (nnshimbun-mew-reverse-order-p)
1200 (while (let (id url subject)
1201 (while (re-search-forward nnshimbun-mew-regexp nil t)
1202 (nnshimbun-mew-extract-header-values))
1204 (nnshimbun-mew-retrieve-xover (setq aux (1+ aux)))))
1206 (nnshimbun-mew-retrieve-xover limit)
1207 (setq limit (1- limit))
1208 (let (id url subject)
1209 (goto-char (point-max))
1210 (while (re-search-backward nnshimbun-mew-regexp nil t)
1211 (nnshimbun-mew-extract-header-values)
1212 (forward-line -2)))))
1215 ;;; MLs on www.xemacs.org
1217 (defmacro nnshimbun-xemacs-concat-url (url)
1218 `(concat nnshimbun-url nnshimbun-current-group "/" ,url))
1220 (defun nnshimbun-xemacs-get-headers ()
1221 (let (headers auxs aux)
1223 (while (re-search-forward
1224 (concat "<A HREF=\"/list-archives/" nnshimbun-current-group
1225 "/\\([12][0-9][0-9][0-9][0-1][0-9]\\)/\">\\[Index\\]")
1227 (setq auxs (append auxs (list (match-string 1)))))
1230 (nnshimbun-retrieve-url
1231 (nnshimbun-xemacs-concat-url (concat (setq aux (car auxs)) "/")))
1232 (let (id url subject)
1233 (goto-char (point-max))
1234 (while (re-search-backward
1235 "<A[^>]*HREF=\"\\(msg\\([0-9]+\\).html\\)\">\\([^<]+\\)<"
1237 (setq url (nnshimbun-xemacs-concat-url
1238 (concat aux "/" (match-string 1)))
1239 id (format "<%s%05d%%%s>"
1241 (string-to-number (match-string 2))
1242 nnshimbun-current-group)
1243 subject (match-string 3))
1245 (if (nnshimbun-search-id nnshimbun-current-group id)
1246 (throw 'stop headers)
1247 (push (make-full-mail-header
1249 (nnshimbun-mime-encode-string subject)
1250 (if (looking-at "<td><em>\\([^<]+\\)<")
1257 (setq auxs (cdr auxs))))
1261 (provide 'nnshimbun)
1262 ;;; nnshimbun.el ends here.