* Makefile.in (install-package-ja): Compile and install lisp files first.
[elisp/gnus.git-] / lisp / nnshimbun.el
1 ;;; nnshimbun.el --- interfacing with web newspapers -*- coding: junet; -*-
2
3 ;; Authors: TSUCHIYA Masatoshi <tsuchiya@pine.kuee.kyoto-u.ac.jp>
4 ;;          Akihiro Arisawa    <ari@atesoft.advantest.co.jp>
5 ;; Keywords: news
6
7 ;;; Copyright:
8
9 ;; This file is a part of Semi-Gnus.
10
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)
14 ;; any later version.
15
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.
20
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.
25
26 ;;; Commentary:
27
28 ;; Gnus backend to read newspapers on WEB.
29
30
31 ;;; Defintinos:
32
33 (gnus-declare-backend "nnshimbun" 'address)
34
35 (eval-when-compile (require 'cl))
36
37 (require 'nnheader)
38 (require 'nnmail)
39 (require 'nnoo)
40 (require 'gnus-bcklg)
41 (eval-when-compile
42   (ignore-errors
43     (require 'nnweb)))
44 ;; Report failure to find w3 at load time if appropriate.
45 (eval '(require 'nnweb))
46
47
48 (nnoo-declare nnshimbun)
49
50 (defvar nnshimbun-check-interval 300)
51
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)))
59
60 (defvar nnshimbun-type-definition
61   `(("asahi"
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"))
72     ("sponichi"
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"))
83     ("cnet"
84      (url . "http://cnet.sphere.ne.jp/")
85      (groups "comp")
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"))
94     ("wired"
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)
100      (index-url)
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"))
105     ("yomiuri"
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"))
116     ("zdnet"
117      (url . "http://www.zdnet.co.jp/news/")
118      (groups "comp")
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-->\\|<!--DATE-->\\)")
126      (contents-end   . "\\(<!--BODYEND-->\\|<!--BYLINEEND-->\\)"))
127     ("mew"
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))
135     ("xemacs"
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))
145     ("netbsd"
146      (url . "http://www.jp.netbsd.org/ja/JP/ml/")
147      (groups "announce-ja" "junk-ja" "tech-misc-ja" "tech-pkg-ja"
148              "port-arm32-ja" "port-hpcmips-ja" "port-mac68k-ja"
149              "port-mips-ja" "port-powerpc-ja" "hpcmips-changes-ja"
150              "members-ja" "admin-ja" "www-changes-ja")
151      (coding-system  . ,(if (boundp 'MULE) '*iso-2022-jp* 'iso-2022-jp))
152      (generate-nov   . nnshimbun-generate-nov-for-each-group)
153      (get-headers    . nnshimbun-netbsd-get-headers)
154      (index-url      . (format "%s%s/index.html" nnshimbun-url nnshimbun-current-group))
155      (make-contents  . nnshimbun-make-mhonarc-contents))
156     ))
157
158 (defvar nnshimbun-x-face-alist
159   '(("default" .
160      (("default" .
161        "X-Face: Ygq$6P.,%Xt$U)DS)cRY@k$VkW!7(X'X'?U{{osjjFG\"E]hND;SPJ-J?O?R|a?L
162         g2$0rVng=O3Lt}?~IId8Jj&vP^3*o=LKUyk(`t%0c!;t6REk=JbpsEn9MrN7gZ%"))))
163   "Alist of server vs. alist of group vs. X-Face field.  It looks like:
164
165 \((\"asahi\" . ((\"national\" . \"X-face: ***\")
166              (\"business\" . \"X-Face: ***\")
167                 ;;
168                 ;;
169              (\"default\" . \"X-face: ***\")))
170  (\"sponichi\" . ((\"baseball\" . \"X-face: ***\")
171                 (\"soccer\" . \"X-Face: ***\")
172                 ;;
173                 ;;
174                 (\"default\" . \"X-face: ***\")))
175                 ;;
176  (\"default\" . ((\"default\" . \"X-face: ***\")))")
177
178 (defvoo nnshimbun-directory (nnheader-concat gnus-directory "shimbun/")
179   "Where nnshimbun will save its files.")
180
181 (defvoo nnshimbun-nov-is-evil nil
182   "*Non-nil means that nnshimbun will never retrieve NOV headers.")
183
184 (defvoo nnshimbun-nov-file-name ".overview")
185
186 (defvoo nnshimbun-pre-fetch-article nil
187   "*Non nil means that nnshimbun fetch unread articles when scanning groups.")
188
189 ;; set by nnshimbun-possibly-change-group
190 (defvoo nnshimbun-buffer nil)
191 (defvoo nnshimbun-current-directory nil)
192 (defvoo nnshimbun-current-group nil)
193
194 ;; set by nnshimbun-open-server
195 (defvoo nnshimbun-url nil)
196 (defvoo nnshimbun-coding-system nil)
197 (defvoo nnshimbun-groups nil)
198 (defvoo nnshimbun-generate-nov nil)
199 (defvoo nnshimbun-get-headers nil)
200 (defvoo nnshimbun-index-url nil)
201 (defvoo nnshimbun-from-address nil)
202 (defvoo nnshimbun-make-contents nil)
203 (defvoo nnshimbun-contents-start nil)
204 (defvoo nnshimbun-contents-end nil)
205 (defvoo nnshimbun-server-directory nil)
206
207 (defvoo nnshimbun-status-string "")
208 (defvoo nnshimbun-nov-last-check nil)
209 (defvoo nnshimbun-nov-buffer-alist nil)
210 (defvoo nnshimbun-nov-buffer-file-name nil)
211
212 (defvoo nnshimbun-keep-backlog 300)
213 (defvoo nnshimbun-backlog-articles nil)
214 (defvoo nnshimbun-backlog-hashtb nil)
215
216
217
218 ;;; backlog
219 (defmacro nnshimbun-backlog (&rest form)
220   `(let ((gnus-keep-backlog nnshimbun-keep-backlog)
221          (gnus-backlog-buffer (format " *nnshimbun backlog %s*" (nnoo-current-server 'nnshimbun)))
222          (gnus-backlog-articles nnshimbun-backlog-articles)
223          (gnus-backlog-hashtb nnshimbun-backlog-hashtb))
224      (unwind-protect
225          (progn ,@form)
226        (setq nnshimbun-backlog-articles gnus-backlog-articles
227              nnshimbun-backlog-hashtb gnus-backlog-hashtb))))
228 (put 'nnshimbun-backlog 'lisp-indent-function 0)
229 (put 'nnshimbun-backlog 'edebug-form-spec '(form body))
230
231
232
233 ;;; Interface Functions
234 (nnoo-define-basics nnshimbun)
235
236 (deffoo nnshimbun-open-server (server &optional defs)
237   ;; Set default values.
238   (dolist (default (cdr (assoc server nnshimbun-type-definition)))
239     (let ((symbol (intern (concat "nnshimbun-" (symbol-name (car default))))))
240       (unless (assq symbol defs)
241         (push (list symbol (cdr default)) defs))))
242   ;; Set directory for server working files.
243   (push (list 'nnshimbun-server-directory
244               (file-name-as-directory
245                (expand-file-name server nnshimbun-directory)))
246         defs)
247   (nnoo-change-server 'nnshimbun server defs)
248   (nnshimbun-possibly-change-group nil server)
249   ;; Make directories.
250   (unless (file-exists-p nnshimbun-directory)
251     (ignore-errors (make-directory nnshimbun-directory t)))
252   (cond
253    ((not (file-exists-p nnshimbun-directory))
254     (nnshimbun-close-server)
255     (nnheader-report 'nnshimbun "Couldn't create directory: %s" nnshimbun-directory))
256    ((not (file-directory-p (file-truename nnshimbun-directory)))
257     (nnshimbun-close-server)
258     (nnheader-report 'nnshimbun "Not a directory: %s" nnshimbun-directory))
259    (t
260     (unless (file-exists-p nnshimbun-server-directory)
261       (ignore-errors (make-directory nnshimbun-server-directory t)))
262     (cond
263      ((not (file-exists-p nnshimbun-server-directory))
264       (nnshimbun-close-server)
265       (nnheader-report 'nnshimbun "Couldn't create directory: %s" nnshimbun-server-directory))
266      ((not (file-directory-p (file-truename nnshimbun-server-directory)))
267       (nnshimbun-close-server)
268       (nnheader-report 'nnshimbun "Not a directory: %s" nnshimbun-server-directory))
269      (t
270       (nnheader-report 'nnshimbun "Opened server %s using directory %s"
271                        server nnshimbun-server-directory)
272       t)))))
273
274 (deffoo nnshimbun-close-server (&optional server)
275   (and (nnshimbun-server-opened server)
276        (gnus-buffer-live-p nnshimbun-buffer)
277        (kill-buffer nnshimbun-buffer))
278   (nnshimbun-backlog (gnus-backlog-shutdown))
279   (nnshimbun-save-nov)
280   (nnoo-close-server 'nnshimbun server)
281   t)
282
283 (defun nnshimbun-retrieve-url (url &optional no-cache)
284   "Rertrieve URL contents and insert to current buffer."
285   (let ((coding-system-for-read 'binary)
286         (coding-system-for-write 'binary))
287     (set-buffer-multibyte nil)
288     ;; Following code is imported from `url-insert-file-contents'.
289     (save-excursion
290       (let ((old-asynch (default-value 'url-be-asynchronous))
291             (old-caching (default-value 'url-automatic-caching))
292             (old-mode (default-value 'url-standalone-mode)))
293         (unwind-protect
294             (progn
295               (setq-default url-be-asynchronous nil)
296               (when no-cache
297                 (setq-default url-automatic-caching nil)
298                 (setq-default url-standalone-mode nil))
299               (let ((buf (current-buffer))
300                     (url-working-buffer (cdr (url-retrieve url no-cache))))
301                 (set-buffer url-working-buffer)
302                 (url-uncompress)
303                 (set-buffer buf)
304                 (insert-buffer url-working-buffer)
305                 (save-excursion
306                   (set-buffer url-working-buffer)
307                   (set-buffer-modified-p nil))
308                 (kill-buffer url-working-buffer)))
309           (setq-default url-be-asynchronous old-asynch)
310           (setq-default url-automatic-caching old-caching)
311           (setq-default url-standalone-mode old-mode))))
312     ;; Modify buffer coding system.
313     (decode-coding-region (point-min) (point-max) nnshimbun-coding-system)
314     (set-buffer-multibyte t)))
315
316 (deffoo nnshimbun-request-article (article &optional group server to-buffer)
317   (when (nnshimbun-possibly-change-group group server)
318     (if (stringp article)
319         (setq article (nnshimbun-search-id group article)))
320     (if (integerp article)
321         (nnshimbun-request-article-1 article group server to-buffer)
322       (nnheader-report 'nnml "Couldn't retrieve article: %s" (prin1-to-string article))
323       nil)))
324
325 (defsubst nnshimbun-header-xref (x)
326   (if (and (setq x (mail-header-xref x))
327            (string-match "^Xref: " x))
328       (substring x 6)
329     x))
330
331 (defun nnshimbun-request-article-1 (article &optional group server to-buffer)
332   (if (nnshimbun-backlog
333         (gnus-backlog-request-article
334          group article (or to-buffer nntp-server-buffer)))
335       (cons group article)
336     (let (header contents)
337       (when (setq header (save-excursion
338                            (set-buffer (nnshimbun-open-nov group))
339                            (and (nnheader-find-nov-line article)
340                                 (nnheader-parse-nov))))
341         (let* ((xref (nnshimbun-header-xref header))
342                (x-faces (cdr (or (assoc (or server
343                                             (nnoo-current-server 'nnshimbun))
344                                         nnshimbun-x-face-alist)
345                                  (assoc "default" nnshimbun-x-face-alist))))
346                (x-face (cdr (or (assoc group x-faces)
347                                 (assoc "default" x-faces)))))
348           (save-excursion
349             (set-buffer nnshimbun-buffer)
350             (erase-buffer)
351             (nnshimbun-retrieve-url xref)
352             (nnheader-message 6 "nnshimbun: Make contents...")
353             (goto-char (point-min))
354             (setq contents (funcall nnshimbun-make-contents header x-face))
355             (nnheader-message 6 "nnshimbun: Make contents...done"))))
356       (when contents
357         (save-excursion
358           (set-buffer (or to-buffer nntp-server-buffer))
359           (erase-buffer)
360           (insert contents)
361           (nnshimbun-backlog
362             (gnus-backlog-enter-article group article (current-buffer)))
363           (nnheader-report 'nnshimbun "Article %s retrieved" (mail-header-id header))
364           (cons group (mail-header-number header)))))))
365
366 (deffoo nnshimbun-request-group (group &optional server dont-check)
367   (let ((pathname-coding-system 'binary))
368     (cond
369      ((not (nnshimbun-possibly-change-group group server))
370       (nnheader-report 'nnshimbun "Invalid group (no such directory)"))
371      ((not (file-exists-p nnshimbun-current-directory))
372       (nnheader-report 'nnshimbun "Directory %s does not exist"
373                        nnshimbun-current-directory))
374      ((not (file-directory-p nnshimbun-current-directory))
375       (nnheader-report 'nnshimbun "%s is not a directory" nnshimbun-current-directory))
376      (dont-check
377       (nnheader-report 'nnshimbun "Group %s selected" group)
378       t)
379      (t
380       (let (beg end lines)
381         (save-excursion
382           (set-buffer (nnshimbun-open-nov group))
383           (goto-char (point-min))
384           (setq beg (ignore-errors (read (current-buffer))))
385           (goto-char (point-max))
386           (forward-line -1)
387           (setq end (ignore-errors (read (current-buffer)))
388                 lines (count-lines (point-min) (point-max))))
389         (nnheader-report 'nnshimbunw "Selected group %s" group)
390         (nnheader-insert "211 %d %d %d %s\n"
391                          lines (or beg 0) (or end 0) group))))))
392
393 (deffoo nnshimbun-request-scan (&optional group server)
394   (nnshimbun-possibly-change-group group server)
395   (nnshimbun-generate-nov-database group))
396
397 (deffoo nnshimbun-close-group (group &optional server)
398   (nnshimbun-write-nov group)
399   t)
400
401 (deffoo nnshimbun-request-list (&optional server)
402   (save-excursion
403     (set-buffer nntp-server-buffer)
404     (erase-buffer)
405     (dolist (group nnshimbun-groups)
406       (when (nnshimbun-possibly-change-group group server)
407         (let (beg end)
408           (save-excursion
409             (set-buffer (nnshimbun-open-nov group))
410             (goto-char (point-min))
411             (setq beg (ignore-errors (read (current-buffer))))
412             (goto-char (point-max))
413             (forward-line -1)
414             (setq end (ignore-errors (read (current-buffer)))))
415           (insert (format "%s %d %d n\n" group (or end 0) (or beg 0)))))))
416   t) ; return value
417
418 (eval-and-compile
419   (if (fboundp 'mime-entity-fetch-field)
420       ;; For Semi-Gnus.
421       (defun nnshimbun-insert-header (header)
422         (insert "Subject: " (or (mime-entity-fetch-field header 'Subject) "(none)") "\n"
423                 "From: " (or (mime-entity-fetch-field header 'From) "(nobody)") "\n"
424                 "Date: " (or (mail-header-date header) "") "\n"
425                 "Message-ID: " (or (mail-header-id header) (nnmail-message-id)) "\n")
426         (let ((refs (mail-header-references header)))
427           (and refs
428                (string< "" refs)
429                (insert "References: " refs "\n")))
430         (insert "Lines: " (number-to-string (or (mail-header-lines header) 0)) "\n"
431                 "Xref: " (nnshimbun-header-xref header) "\n"))
432     ;; For pure Gnus.
433     (defun nnshimbun-insert-header (header)
434       (nnheader-insert-header header)
435       (delete-char -1)
436       (insert "Xref: " (nnshimbun-header-xref header) "\n"))))
437
438 (deffoo nnshimbun-retrieve-headers (articles &optional group server fetch-old)
439   (when (nnshimbun-possibly-change-group group server)
440     (if (nnshimbun-retrieve-headers-with-nov articles fetch-old)
441         'nov
442       (save-excursion
443         (set-buffer nntp-server-buffer)
444         (erase-buffer)
445         (let (header)
446           (dolist (art articles)
447             (if (stringp art)
448                 (setq art (nnshimbun-search-id group art)))
449             (if (integerp art)
450                 (when (setq header
451                             (save-excursion
452                               (set-buffer (nnshimbun-open-nov group))
453                               (and (nnheader-find-nov-line art)
454                                    (nnheader-parse-nov))))
455                   (insert (format "220 %d Article retrieved.\n" art))
456                   (nnshimbun-insert-header header)
457                   (insert ".\n")
458                   (delete-region (point) (point-max))))))
459         'header))))
460
461 (defun nnshimbun-retrieve-headers-with-nov (articles &optional fetch-old)
462   (if (or gnus-nov-is-evil nnshimbun-nov-is-evil)
463       nil
464     (let ((nov (expand-file-name nnshimbun-nov-file-name nnshimbun-current-directory)))
465       (when (file-exists-p nov)
466         (save-excursion
467           (set-buffer nntp-server-buffer)
468           (erase-buffer)
469           (nnheader-insert-file-contents nov)
470           (if (and fetch-old (not (numberp fetch-old)))
471               t                         ; Don't remove anything.
472             (nnheader-nov-delete-outside-range
473              (if fetch-old (max 1 (- (car articles) fetch-old))
474                (car articles))
475              (car (last articles)))
476             t))))))
477
478
479
480 ;;; Nov Database Operations
481
482 (defun nnshimbun-generate-nov-database (group)
483   (prog1 (funcall nnshimbun-generate-nov group)
484     (nnshimbun-write-nov group)))
485
486 (defun nnshimbun-generate-nov-for-each-group (group)
487   (nnshimbun-possibly-change-group group)
488   (save-excursion
489     (set-buffer (nnshimbun-open-nov group))
490     (let (i)
491       (goto-char (point-max))
492       (forward-line -1)
493       (setq i (or (ignore-errors (read (current-buffer))) 0))
494       (dolist (header (save-excursion
495                         (set-buffer nnshimbun-buffer)
496                         (erase-buffer)
497                         (nnshimbun-retrieve-url (eval nnshimbun-index-url) t)
498                         (goto-char (point-min))
499                         (funcall nnshimbun-get-headers)))
500         (unless (nnshimbun-search-id group (mail-header-id header))
501           (mail-header-set-number header (setq i (1+ i)))
502           (goto-char (point-max))
503           (nnheader-insert-nov header)
504           (if nnshimbun-pre-fetch-article
505               (nnshimbun-request-article-1 i group nil nnshimbun-buffer)))))))
506
507 (defun nnshimbun-generate-nov-for-all-groups (&rest args)
508   (unless (and nnshimbun-nov-last-check
509                (< (nnshimbun-lapse-seconds nnshimbun-nov-last-check)
510                   nnshimbun-check-interval))
511     (save-excursion
512       (dolist (list (funcall nnshimbun-get-headers))
513         (let ((group (car list)))
514           (nnshimbun-possibly-change-group group)
515           (when (cdr list)
516             (set-buffer (nnshimbun-open-nov group))
517             (let (i)
518               (goto-char (point-max))
519               (forward-line -1)
520               (setq i (or (ignore-errors (read (current-buffer))) 0))
521               (dolist (header (cdr list))
522                 (unless (nnshimbun-search-id group (mail-header-id header))
523                   (mail-header-set-number header (setq i (1+ i)))
524                   (goto-char (point-max))
525                   (nnheader-insert-nov header)
526                   (if nnshimbun-pre-fetch-article
527                       (nnshimbun-request-article-1 i group nil nnshimbun-buffer))))))))
528       (nnshimbun-save-nov)
529       (setq nnshimbun-nov-last-check (current-time)))))
530
531 (defun nnshimbun-search-id (group id &optional nov)
532   (save-excursion
533     (set-buffer (nnshimbun-open-nov group))
534     (goto-char (point-min))
535     (let (found)
536       (while (and (not found)
537                   (search-forward id nil t)) ; We find the ID.
538         ;; And the id is in the fourth field.
539         (if (not (and (search-backward "\t" nil t 4)
540                       (not (search-backward "\t" (gnus-point-at-bol) t))))
541             (forward-line 1)
542           (forward-line 0)
543           (setq found t)))
544       (unless found
545         (goto-char (point-min))
546         (when (search-forward (concat "X-Nnshimbun-Id: " id) nil t)
547           (forward-line 0)
548           (setq found t)))
549       (if found
550           (if nov
551               (nnheader-parse-nov)
552             ;; We return the article number.
553             (ignore-errors (read (current-buffer))))))))
554
555 (defun nnshimbun-nov-fix-header (group header args)
556   (save-excursion
557     (set-buffer (nnshimbun-open-nov group))
558     (when (nnheader-find-nov-line (mail-header-number header))
559       (dolist (arg args)
560         (if (eq (car arg) 'id)
561             (let ((extra (mail-header-extra header)))
562               (unless (assq 'X-Nnshimbun-Id extra)
563                 (mail-header-set-extra
564                  header
565                  (cons (cons 'X-Nnshimbun-Id (mail-header-id header))
566                        extra)))
567               (mail-header-set-id header (cdr arg)))
568           (let ((func (intern (concat "mail-header-set-" (symbol-name (car arg))))))
569             (if (cdr arg) (eval (list func header (cdr arg)))))))
570       (mail-header-set-xref header (nnshimbun-header-xref header))
571       (delete-region (point) (progn (forward-line 1) (point)))
572       (nnheader-insert-nov header))))
573
574 (defun nnshimbun-open-nov (group)
575   (let ((buffer (cdr (assoc group nnshimbun-nov-buffer-alist))))
576     (if (buffer-live-p buffer)
577         buffer
578       (setq buffer (gnus-get-buffer-create
579                     (format " *nnshimbun overview %s %s*"
580                             (nnoo-current-server 'nnshimbun) group)))
581       (save-excursion
582         (set-buffer buffer)
583         (set (make-local-variable 'nnshimbun-nov-buffer-file-name)
584              (expand-file-name
585               nnshimbun-nov-file-name
586               (nnmail-group-pathname group nnshimbun-server-directory)))
587         (erase-buffer)
588         (when (file-exists-p nnshimbun-nov-buffer-file-name)
589           (nnheader-insert-file-contents nnshimbun-nov-buffer-file-name))
590         (set-buffer-modified-p nil))
591       (push (cons group buffer) nnshimbun-nov-buffer-alist)
592       buffer)))
593
594 (defun nnshimbun-write-nov (group)
595   (let ((buffer (cdr (assoc group nnshimbun-nov-buffer-alist))))
596     (when (buffer-live-p buffer)
597       (save-excursion
598         (set-buffer buffer)
599         (buffer-modified-p)
600         (nnmail-write-region 1 (point-max) nnshimbun-nov-buffer-file-name
601                              nil 'nomesg)))))
602
603 (defun nnshimbun-save-nov ()
604   (save-excursion
605     (while nnshimbun-nov-buffer-alist
606       (when (buffer-name (cdar nnshimbun-nov-buffer-alist))
607         (set-buffer (cdar nnshimbun-nov-buffer-alist))
608         (when (buffer-modified-p)
609           (nnmail-write-region 1 (point-max) nnshimbun-nov-buffer-file-name
610                                nil 'nomesg))
611         (set-buffer-modified-p nil)
612         (kill-buffer (current-buffer)))
613       (setq nnshimbun-nov-buffer-alist (cdr nnshimbun-nov-buffer-alist)))))
614
615
616
617 ;;; Server Initialize
618 (defun nnshimbun-possibly-change-group (group &optional server)
619   (when server
620     (unless (nnshimbun-server-opened server)
621       (nnshimbun-open-server server)))
622   (unless (gnus-buffer-live-p nnshimbun-buffer)
623     (setq nnshimbun-buffer
624           (save-excursion
625             (nnheader-set-temp-buffer
626              (format " *nnshimbun %s*" (nnoo-current-server 'nnshimbun))))))
627   (if (not group)
628       t
629     (let ((pathname (nnmail-group-pathname group nnshimbun-server-directory))
630           (pathname-coding-system 'binary))
631       (unless (equal pathname nnshimbun-current-directory)
632         (setq nnshimbun-current-directory pathname
633               nnshimbun-current-group group))
634       (unless (file-exists-p nnshimbun-current-directory)
635         (ignore-errors (make-directory nnshimbun-current-directory t)))
636       (cond
637        ((not (file-exists-p nnshimbun-current-directory))
638         (nnheader-report 'nnshimbun "Couldn't create directory: %s" nnshimbun-current-directory))
639        ((not (file-directory-p (file-truename nnshimbun-current-directory)))
640         (nnheader-report 'nnshimbun "Not a directory: %s" nnshimbun-current-directory))
641        (t t)))))
642
643
644
645 ;;; Misc Functions
646
647 (eval-and-compile
648   (if (fboundp 'eword-encode-string)
649       ;; For Semi-Gnus.
650       (defun nnshimbun-mime-encode-string (string)
651         (mapconcat
652          #'identity
653          (split-string (eword-encode-string (nnweb-decode-entities-string string)) "\n")
654          ""))
655     ;; For pure Gnus.
656     (defun nnshimbun-mime-encode-string (string)
657       (mapconcat
658        #'identity
659        (split-string
660         (with-temp-buffer
661           (insert (nnweb-decode-entities-string string))
662           (rfc2047-encode-region (point-min) (point-max))
663           (buffer-substring (point-min) (point-max)))
664         "\n")
665        ""))))
666
667 (defun nnshimbun-lapse-seconds (time)
668   (let ((now (current-time)))
669     (+ (* (- (car now) (car time)) 65536)
670        (- (nth 1 now) (nth 1 time)))))
671
672 (defun nnshimbun-make-date-string (year month day &optional time)
673   (format "%02d %s %04d %s +0900"
674           day
675           (aref [nil "Jan" "Feb" "Mar" "Apr" "May" "Jun"
676                      "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"]
677                 month)
678           (cond ((< year 69)
679                  (+ year 2000))
680                 ((< year 100)
681                  (+ year 1900))
682                 ((< year 1000)  ; possible 3-digit years.
683                  (+ year 1900))
684                 (t year))
685           (or time "00:00")))
686
687 (if (fboundp 'regexp-opt)
688     (defalias 'nnshimbun-regexp-opt 'regexp-opt)
689   (defun nnshimbun-regexp-opt (strings &optional paren)
690     "Return a regexp to match a string in STRINGS.
691 Each string should be unique in STRINGS and should not contain any regexps,
692 quoted or not.  If optional PAREN is non-nil, ensure that the returned regexp
693 is enclosed by at least one regexp grouping construct."
694     (let ((open-paren (if paren "\\(" "")) (close-paren (if paren "\\)" "")))
695       (concat open-paren (mapconcat 'regexp-quote strings "\\|") close-paren))))
696
697
698 ;; Fast fill-region function
699
700 (defvar nnshimbun-fill-column (min 80 (- (frame-width) 4)))
701
702 (defconst nnshimbun-kinsoku-bol-list
703   (funcall
704    (if (fboundp 'string-to-char-list)
705        'string-to-char-list
706      'string-to-list) "\
707 !)-_~}]:;',.?\e$B!"!#!$!%!&!'!(!)!*!+!,!-!.!/!0!1!2!3!4!5!6!7!8!9!:!;!<!=!>!?!@!A\e(B\
708 \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"))
709
710 (defconst nnshimbun-kinsoku-eol-list
711   (funcall
712    (if (fboundp 'string-to-char-list)
713        'string-to-char-list
714      'string-to-list)
715    "({[`\e$B!F!H!J!L!N!P!R!T!V!X!Z!k!l!m!x\e(B"))
716
717 (defun nnshimbun-fill-line ()
718   (forward-line 0)
719   (let ((top (point)) chr)
720     (while (if (>= (move-to-column nnshimbun-fill-column)
721                    nnshimbun-fill-column)
722                (not (progn
723                       (if (memq (preceding-char) nnshimbun-kinsoku-eol-list)
724                           (progn
725                             (backward-char)
726                             (while (memq (preceding-char) nnshimbun-kinsoku-eol-list)
727                               (backward-char))
728                             (insert "\n"))
729                         (while (memq (setq chr (following-char)) nnshimbun-kinsoku-bol-list)
730                           (forward-char))
731                         (if (looking-at "\\s-+")
732                             (or (eolp) (delete-region (point) (match-end 0)))
733                           (or (> (char-width chr) 1)
734                               (re-search-backward "\\<" top t)
735                               (end-of-line)))
736                         (or (eolp) (insert "\n"))))))
737       (setq top (point))))
738   (forward-line 1)
739   (not (eobp)))
740
741 (defsubst nnshimbun-shallow-rendering ()
742   (goto-char (point-min))
743   (while (search-forward "<p>" nil t)
744     (insert "\n\n"))
745   (goto-char (point-min))
746   (while (search-forward "<br>" nil t)
747     (insert "\n"))
748   (nnweb-remove-markup)
749   (nnweb-decode-entities)
750   (goto-char (point-min))
751   (while (nnshimbun-fill-line))
752   (goto-char (point-min))
753   (when (skip-chars-forward "\n")
754     (delete-region (point-min) (point)))
755   (while (search-forward "\n\n" nil t)
756     (let ((p (point)))
757       (when (skip-chars-forward "\n")
758         (delete-region p (point)))))
759   (goto-char (point-max))
760   (when (skip-chars-backward "\n")
761     (delete-region (point) (point-max)))
762   (insert "\n"))
763
764 (defun nnshimbun-make-text-or-html-contents (header &optional x-face)
765   (let ((case-fold-search t) (html t) (start))
766     (when (and (re-search-forward nnshimbun-contents-start nil t)
767                (setq start (point))
768                (re-search-forward nnshimbun-contents-end nil t))
769       (delete-region (match-beginning 0) (point-max))
770       (delete-region (point-min) start)
771       (nnshimbun-shallow-rendering)
772       (setq html nil))
773     (goto-char (point-min))
774     (nnshimbun-insert-header header)
775     (insert "Content-Type: " (if html "text/html" "text/plain")
776             "; charset=ISO-2022-JP\nMIME-Version: 1.0\n")
777     (when x-face
778       (insert x-face)
779       (unless (bolp)
780         (insert "\n")))
781     (insert "\n")
782     (encode-coding-string (buffer-string)
783                           (mime-charset-to-coding-system "ISO-2022-JP"))))
784
785 (defun nnshimbun-make-html-contents (header &optional x-face)
786   (let (start)
787     (when (and (re-search-forward nnshimbun-contents-start nil t)
788                (setq start (point))
789                (re-search-forward nnshimbun-contents-end nil t))
790       (delete-region (match-beginning 0) (point-max))
791       (delete-region (point-min) start))
792     (goto-char (point-min))
793     (nnshimbun-insert-header header)
794     (insert "Content-Type: text/html; charset=ISO-2022-JP\n"
795             "MIME-Version: 1.0\n")
796     (when x-face
797       (insert x-face)
798       (unless (bolp)
799         (insert "\n")))
800     (insert "\n")
801     (encode-coding-string (buffer-string)
802                           (mime-charset-to-coding-system "ISO-2022-JP"))))
803
804 (defun nnshimbun-make-mhonarc-contents (header &rest args)
805   (require 'mml)
806   (if (search-forward "<!--X-Head-End-->" nil t)
807       (progn
808         (forward-line 0)
809         ;; Processing headers.
810         (save-restriction
811           (narrow-to-region (point-min) (point))
812           (nnweb-decode-entities)
813           (goto-char (point-min))
814           (while (search-forward "\n<!--X-" nil t)
815             (replace-match "\n"))
816           (goto-char (point-min))
817           (while (search-forward " -->\n" nil t)
818             (replace-match "\n"))
819           (goto-char (point-min))
820           (while (search-forward "\t" nil t)
821             (replace-match " "))
822           (goto-char (point-min))
823           (let (buf refs)
824             (while (not (eobp))
825               (cond
826                ((looking-at "<!--")
827                 (delete-region (point) (progn (forward-line 1) (point))))
828                ((looking-at "Subject: +")
829                 (push (cons 'subject (nnheader-header-value)) buf)
830                 (delete-region (point) (progn (forward-line 1) (point))))
831                ((looking-at "From: +")
832                 (push (cons 'from (nnheader-header-value)) buf)
833                 (delete-region (point) (progn (forward-line 1) (point))))
834                ((looking-at "Date: +")
835                 (push (cons 'date (nnheader-header-value)) buf)
836                 (delete-region (point) (progn (forward-line 1) (point))))
837                ((looking-at "Message-Id: +")
838                 (push (cons 'id (concat "<" (nnheader-header-value) ">")) buf)
839                 (delete-region (point) (progn (forward-line 1) (point))))
840                ((looking-at "Reference: +")
841                 (push (concat "<" (nnheader-header-value) ">") refs)
842                 (delete-region (point) (progn (forward-line 1) (point))))
843                ((looking-at "Content-Type: ")
844                 (unless (search-forward "charset" (gnus-point-at-eol) t)
845                   (end-of-line)
846                   (insert "; charset=ISO-2022-JP"))
847                 (forward-line 1))
848                (t (forward-line 1))))
849             (insert "MIME-Version: 1.0\n")
850             (if refs (push (cons 'references (mapconcat 'identity refs " ")) buf))
851             (nnshimbun-nov-fix-header nnshimbun-current-group header buf)
852             (goto-char (point-min))
853             (nnshimbun-insert-header header))
854           (goto-char (point-max)))
855         ;; Processing body.
856         (save-restriction
857           (narrow-to-region (point) (point-max))
858           (delete-region
859            (point)
860            (progn
861              (search-forward "\n<!--X-Body-of-Message-->\n" nil t)
862              (point)))
863           (when (search-forward "\n<!--X-Body-of-Message-End-->\n" nil t)
864             (forward-line -1)
865             (delete-region (point) (point-max)))
866           (nnweb-remove-markup)
867           (nnweb-decode-entities)))
868     (goto-char (point-min))
869     (nnshimbun-insert-header header)
870     (insert "Content-Type: text/html; charset=ISO-2022-JP\nMIME-Version: 1.0\n\n"))
871   (encode-coding-string (buffer-string)
872                         (mime-charset-to-coding-system "ISO-2022-JP")))
873
874
875 ;;; www.asahi.com
876
877 (defun nnshimbun-asahi-get-headers ()
878   (when (search-forward "\n<!-- Start of past -->\n" nil t)
879     (delete-region (point-min) (point))
880     (when (search-forward "\n<!-- End of past -->\n" nil t)
881       (forward-line -1)
882       (delete-region (point) (point-max))
883       (goto-char (point-min))
884       (let (headers)
885         (while (re-search-forward
886                 "^\e$B"#\e(B<a href=\"\\(\\([0-9][0-9][0-9][0-9]\\)/past/\\([A-z]*[0-9]*\\)\\.html\\)\"> *"
887                 nil t)
888           (let ((id (format "<%s%s%%%s>"
889                             (match-string 2)
890                             (match-string 3)
891                             nnshimbun-current-group))
892                 (url (match-string 1)))
893             (push (make-full-mail-header
894                    0
895                    (nnshimbun-mime-encode-string
896                     (mapconcat 'identity
897                                (split-string
898                                 (buffer-substring
899                                  (match-end 0)
900                                  (progn (search-forward "<br>" nil t) (point)))
901                                 "\\(<[^>]+>\\|\r\\)")
902                                ""))
903                    nnshimbun-from-address
904                    "" id "" 0 0 (concat nnshimbun-url url))
905                   headers)))
906         (setq headers (nreverse headers))
907         (let ((i 0))
908           (while (and (nth i headers)
909                       (re-search-forward
910                        "^\\[\\([0-9][0-9]\\)/\\([0-9][0-9]\\) \\([0-9][0-9]:[0-9][0-9]\\)\\]"
911                        nil t))
912             (let ((month (string-to-number (match-string 1)))
913                   (date (decode-time (current-time))))
914               (mail-header-set-date
915                (nth i headers)
916                (nnshimbun-make-date-string
917                 (if (and (eq 12 month) (eq 1 (nth 4 date)))
918                     (1- (nth 5 date))
919                   (nth 5 date))
920                 month
921                 (string-to-number (match-string 2))
922                 (match-string 3))))
923             (setq i (1+ i))))
924         (nreverse headers)))))
925
926
927
928 ;;; www.sponichi.co.jp
929
930 (defun nnshimbun-sponichi-get-headers ()
931   (when (search-forward "\e$B%K%e!<%9%$%s%G%C%/%9\e(B" nil t)
932     (delete-region (point-min) (point))
933     (when (search-forward "\e$B%"%I%?%0\e(B" nil t)
934       (forward-line 2)
935       (delete-region (point) (point-max))
936       (goto-char (point-min))
937       (let ((case-fold-search t) headers)
938         (while (re-search-forward
939                 "^<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\\)\">"
940                 nil t)
941           (let ((url (match-string 1))
942                 (id (format "<%s%s%s%s%%%s>"
943                             (match-string 3)
944                             (match-string 4)
945                             (match-string 5)
946                             (match-string 6)
947                             nnshimbun-current-group))
948                 (date (nnshimbun-make-date-string
949                        (string-to-number (match-string 3))
950                        (string-to-number (match-string 4))
951                        (string-to-number (match-string 5)))))
952             (push (make-full-mail-header
953                    0
954                    (nnshimbun-mime-encode-string
955                     (mapconcat 'identity
956                                (split-string
957                                 (buffer-substring
958                                  (match-end 0)
959                                  (progn (search-forward "<br>" nil t) (point)))
960                                 "<[^>]+>")
961                                ""))
962                    nnshimbun-from-address
963                    date id "" 0 0 (concat nnshimbun-url url))
964                   headers)))
965         headers))))
966
967
968
969 ;;; CNET Japan
970
971 (defun nnshimbun-cnet-get-headers ()
972   (let ((case-fold-search t) headers)
973     (while (search-forward "\n<!--*****\e$B8+=P$7\e(B*****-->\n" nil t)
974       (let ((subject (buffer-substring (point) (gnus-point-at-eol)))
975             (point (point)))
976         (forward-line -2)
977         (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\\)\">")
978           (let ((url (match-string 1))
979                 (id  (format "<%s%s%%%s>"
980                              (match-string 2)
981                              (match-string 3)
982                              nnshimbun-current-group))
983                 (date (nnshimbun-make-date-string
984                        (string-to-number (match-string 2))
985                        (string-to-number (match-string 4))
986                        (string-to-number (match-string 5)))))
987             (push (make-full-mail-header
988                    0
989                    (nnshimbun-mime-encode-string subject)
990                    nnshimbun-from-address
991                    date id "" 0 0 (concat nnshimbun-url url))
992                   headers)))
993         (goto-char point)))
994     headers))
995
996
997
998 ;;; Wired
999
1000 (defun nnshimbun-wired-get-all-headers ()
1001   (save-excursion
1002     (set-buffer nnshimbun-buffer)
1003     (let ((group-header-alist (mapcar (lambda (g) (cons g nil)) nnshimbun-groups))
1004           (case-fold-search t)
1005           (regexp (format
1006                    "<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>"
1007                    (regexp-quote nnshimbun-url)
1008                    (nnshimbun-regexp-opt nnshimbun-groups))))
1009       (dolist (xover (list (concat nnshimbun-url "news/news/index.html")
1010                            (concat nnshimbun-url "news/news/last_seven.html")))
1011         (erase-buffer)
1012         (nnshimbun-retrieve-url xover t)
1013         (goto-char (point-min))
1014         (while (re-search-forward regexp nil t)
1015           (let* ((url   (concat nnshimbun-url (match-string 2)))
1016                  (group (downcase (match-string 3)))
1017                  (id    (format "<%s%%%s>" (match-string 4) group))
1018                  (date  (nnshimbun-make-date-string
1019                          (string-to-number (match-string 5))
1020                          (string-to-number (match-string 6))
1021                          (string-to-number (match-string 7))))
1022                  (header (make-full-mail-header
1023                           0
1024                           (nnshimbun-mime-encode-string
1025                            (mapconcat 'identity
1026                                       (split-string
1027                                        (buffer-substring
1028                                         (match-end 0)
1029                                         (progn (search-forward "</b>" nil t) (point)))
1030                                        "<[^>]+>")
1031                                       ""))
1032                           nnshimbun-from-address
1033                           date id "" 0 0 url))
1034                  (x (assoc group group-header-alist)))
1035             (setcdr x (cons header (cdr x))))))
1036       group-header-alist)))
1037
1038
1039
1040 ;;; www.yomiuri.co.jp
1041
1042 (defun nnshimbun-yomiuri-get-all-headers ()
1043   (save-excursion
1044     (set-buffer nnshimbun-buffer)
1045     (erase-buffer)
1046     (nnshimbun-retrieve-url (eval nnshimbun-index-url) t)
1047     (let ((case-fold-search t)
1048           (group-header-alist (mapcar (lambda (g) (cons g nil)) nnshimbun-groups)))
1049       (dolist (group nnshimbun-groups)
1050         (let (start)
1051           (goto-char (point-min))
1052           (when (and (search-forward (format "\n<!-- /news/%s=start -->\n" group) nil t)
1053                      (setq start (point))
1054                      (search-forward (format "\n<!-- /news/%s=end -->\n" group) nil t))
1055             (forward-line -1)
1056             (save-restriction
1057               (narrow-to-region start (point))
1058               (goto-char start)
1059               (while (re-search-forward
1060                       "<a href=\"/\\([0-9]+\\)/\\(\\(\\([0-9][0-9][0-9][0-9]\\)\\([0-9][0-9]\\)\\([0-9][0-9]\\)[A-z0-9]+\\)\\.htm\\)\"[^>]*>"
1061                       nil t)
1062                 (let ((url   (concat (match-string 1) "a/" (match-string 2)))
1063                       (id    (format "<%s%s%%%s>"
1064                                      (match-string 1)
1065                                      (match-string 3)
1066                                      group))
1067                       (year  (string-to-number (match-string 4)))
1068                       (month (string-to-number (match-string 5)))
1069                       (day   (string-to-number (match-string 6)))
1070                       (subject (mapconcat
1071                                 'identity
1072                                 (split-string
1073                                  (buffer-substring
1074                                   (match-end 0)
1075                                   (progn (search-forward "<br>" nil t) (point)))
1076                                  "<[^>]+>")
1077                                 ""))
1078                       date x)
1079                   (when (string-match "^\e$B"!\e(B" subject)
1080                     (setq subject (substring subject (match-end 0))))
1081                   (if (string-match "(\\([0-9][0-9]:[0-9][0-9]\\))$" subject)
1082                       (setq date (nnshimbun-make-date-string
1083                                   year month day (match-string 1 subject))
1084                             subject (substring subject 0 (match-beginning 0)))
1085                     (setq date (nnshimbun-make-date-string year month day)))
1086                   (setcdr (setq x (assoc group group-header-alist))
1087                           (cons (make-full-mail-header
1088                                  0
1089                                  (nnshimbun-mime-encode-string subject)
1090                                  nnshimbun-from-address
1091                                  date id "" 0 0 (concat nnshimbun-url url))
1092                                 (cdr x)))))))))
1093       group-header-alist)))
1094
1095
1096
1097 ;;; Zdnet Japan
1098
1099 (defun nnshimbun-zdnet-get-headers ()
1100   (let ((case-fold-search t) headers)
1101     (goto-char (point-min))
1102     (let (start)
1103       (while (and (search-forward "<!--" nil t)
1104                   (setq start (- (point) 4))
1105                   (search-forward "-->" nil t))
1106         (delete-region start (point))))
1107     (goto-char (point-min))
1108     (while (re-search-forward
1109             "<a href=\"\\(/news/\\)?\\(\\([0-9][0-9]\\)\\([0-9][0-9]\\)/\\([0-9][0-9]\\)/\\([^\\.]+\\).html\\)\"><font size=\"4\"><strong>"
1110             nil t)
1111       (let ((year  (+ 2000 (string-to-number (match-string 3))))
1112             (month (string-to-number (match-string 4)))
1113             (day   (string-to-number (match-string 5)))
1114             (id    (format "<%s%s%s%s%%%s>"
1115                            (match-string 3)
1116                            (match-string 4)
1117                            (match-string 5)
1118                            (match-string 6)
1119                            nnshimbun-current-group))
1120             (url (match-string 2)))
1121         (push (make-full-mail-header
1122                0
1123                (nnshimbun-mime-encode-string
1124                 (mapconcat 'identity
1125                            (split-string
1126                             (buffer-substring
1127                              (match-end 0)
1128                              (progn (search-forward "</a>" nil t) (point)))
1129                             "<[^>]+>")
1130                            ""))
1131                nnshimbun-from-address
1132                (nnshimbun-make-date-string year month day)
1133                id  "" 0 0 (concat nnshimbun-url url))
1134               headers)))
1135     (nreverse headers)))
1136
1137
1138
1139 ;;; MLs on www.mew.org
1140
1141 (defmacro nnshimbun-mew-concat-url (url)
1142   `(concat nnshimbun-url
1143            (nth 1 (assoc nnshimbun-current-group nnshimbun-mew-groups))
1144            "/"
1145            ,url))
1146
1147 (defmacro nnshimbun-mew-reverse-order-p ()
1148   `(nth 2 (assoc nnshimbun-current-group nnshimbun-mew-groups)))
1149
1150 (defmacro nnshimbun-mew-spew-p ()
1151   `(nth 3 (assoc nnshimbun-current-group nnshimbun-mew-groups)))
1152
1153 (defsubst nnshimbun-mew-retrieve-xover (aux)
1154   (erase-buffer)
1155   (nnshimbun-retrieve-url
1156    (nnshimbun-mew-concat-url (if (= aux 1) "index.html" (format "mail%d.html" aux)))
1157    t))
1158
1159 (defconst nnshimbun-mew-regexp "<A[^>]*HREF=\"\\(msg\\([0-9]+\\).html\\)\">\\([^<]+\\)<")
1160
1161 (defmacro nnshimbun-mew-extract-header-values ()
1162   `(progn
1163      (setq url (nnshimbun-mew-concat-url (match-string 1))
1164            id (format "<%05d%%%s>"
1165                       (1- (string-to-number (match-string 2)))
1166                       nnshimbun-current-group)
1167            subject (match-string 3))
1168      (forward-line 1)
1169      (if (nnshimbun-search-id nnshimbun-current-group id)
1170          (throw 'stop headers)
1171        (push (make-full-mail-header
1172               0
1173               (nnshimbun-mime-encode-string subject)
1174               (if (looking-at "<EM>\\([^<]+\\)<")
1175                   (nnshimbun-mime-encode-string (match-string 1))
1176                 "")
1177               "" id "" 0 0 url)
1178              headers))))
1179
1180 (eval-and-compile
1181   (if (fboundp 'mime-entity-fetch-field)
1182       ;; For Semi-Gnus.
1183       (defmacro nnshimbun-mew-mail-header-subject (header)
1184         `(mime-entity-fetch-field ,header 'Subject))
1185     ;; For pure Gnus.
1186     (defalias 'nnshimbun-mew-mail-header-subject 'mail-header-subject)))
1187
1188 (defun nnshimbun-mew-get-headers ()
1189   (if (nnshimbun-mew-spew-p)
1190       (let ((headers (nnshimbun-mew-get-headers-1)))
1191         (erase-buffer)
1192         (insert-buffer-substring (nnshimbun-open-nov nnshimbun-current-group))
1193         (delq nil
1194               (mapcar
1195                (lambda (header)
1196                  (goto-char (point-min))
1197                  (let ((subject (nnshimbun-mew-mail-header-subject header))
1198                        (found))
1199                    (while (and (not found)
1200                                (search-forward subject nil t))
1201                      (if (not (and (search-backward "\t" nil t)
1202                                    (not (search-backward "\t" (gnus-point-at-bol) t))))
1203                          (forward-line 1)
1204                        (setq found t)))
1205                    (if found
1206                        nil
1207                      (goto-char (point-max))
1208                      (nnheader-insert-nov header)
1209                      header)))
1210                headers)))
1211     (nnshimbun-mew-get-headers-1)))
1212
1213 (defun nnshimbun-mew-get-headers-1 ()
1214   (let (headers)
1215     (when (re-search-forward
1216            "<A[^>]*HREF=\"mail\\([0-9]+\\)\\.html\">\\[?Last Page\\]?</A>" nil t)
1217       (let ((limit (string-to-number (match-string 1))))
1218         (catch 'stop
1219           (if (nnshimbun-mew-reverse-order-p)
1220               (let ((aux 1))
1221                 (while (let (id url subject)
1222                          (while (re-search-forward nnshimbun-mew-regexp nil t)
1223                            (nnshimbun-mew-extract-header-values))
1224                          (< aux limit))
1225                   (nnshimbun-mew-retrieve-xover (setq aux (1+ aux)))))
1226             (while (> limit 0)
1227               (nnshimbun-mew-retrieve-xover limit)
1228               (setq limit (1- limit))
1229               (let (id url subject)
1230                 (goto-char (point-max))
1231                 (while (re-search-backward nnshimbun-mew-regexp nil t)
1232                   (nnshimbun-mew-extract-header-values)
1233                   (forward-line -2)))))
1234           headers)))))
1235
1236
1237
1238 ;;; MLs on www.xemacs.org
1239
1240 (defmacro nnshimbun-xemacs-concat-url (url)
1241   `(concat nnshimbun-url nnshimbun-current-group "/" ,url))
1242
1243 (defun nnshimbun-xemacs-get-headers ()
1244   (let (headers auxs aux)
1245     (catch 'stop
1246       (while (re-search-forward
1247               (concat "<A HREF=\"/list-archives/" nnshimbun-current-group
1248                       "/\\([12][0-9][0-9][0-9][0-1][0-9]\\)/\">\\[Index\\]")
1249               nil t)
1250         (setq auxs (append auxs (list (match-string 1)))))
1251       (while auxs
1252         (erase-buffer)
1253         (nnshimbun-retrieve-url
1254          (nnshimbun-xemacs-concat-url (concat (setq aux (car auxs)) "/")))
1255         (let (id url subject)
1256           (goto-char (point-max))
1257           (while (re-search-backward
1258                   "<A[^>]*HREF=\"\\(msg\\([0-9]+\\).html\\)\">\\([^<]+\\)<"
1259                   nil t)
1260             (setq url (nnshimbun-xemacs-concat-url
1261                        (concat aux "/" (match-string 1)))
1262                   id (format "<%s%05d%%%s>"
1263                              aux
1264                              (string-to-number (match-string 2))
1265                              nnshimbun-current-group)
1266                   subject (match-string 3))
1267             (forward-line 1)
1268             (if (nnshimbun-search-id nnshimbun-current-group id)
1269                 (throw 'stop headers)
1270               (push (make-full-mail-header
1271                      0
1272                      (nnshimbun-mime-encode-string subject)
1273                      (if (looking-at "<td><em>\\([^<]+\\)<")
1274                          (match-string 1)
1275                        "")
1276                      "" id "" 0 0 url)
1277                     headers))
1278             (message "%s" id)
1279             (forward-line -2)))
1280         (setq auxs (cdr auxs))))
1281     headers))
1282
1283 ;;; MLs on www.jp.netbsd.org
1284
1285 (defun nnshimbun-netbsd-get-headers ()
1286   (let ((case-fold-search t) headers months)
1287     (goto-char (point-min))
1288     (while (re-search-forward "<A HREF=\"\\([0-9]+\\)/\\(threads.html\\)?\">" nil t)
1289       (push (match-string 1) months))
1290     (setq months (nreverse months))
1291     (catch 'exit
1292       (dolist (month months)
1293         (erase-buffer)
1294         (nnshimbun-retrieve-url
1295          (format "%s%s/%s/maillist.html" nnshimbun-url nnshimbun-current-group month)
1296          t)
1297         (let (id url subject)
1298           (while (re-search-forward
1299                   "<A[^>]*HREF=\"\\(msg\\([0-9]+\\)\\.html\\)\">\\([^<]+\\)</A>"
1300                   nil t)
1301             (setq url (format "%s%s/%s/%s"
1302                               nnshimbun-url
1303                               nnshimbun-current-group
1304                               month
1305                               (match-string 1))
1306                   id (format "<%s%05d%%%s>"
1307                              month
1308                              (string-to-number (match-string 2))
1309                              nnshimbun-current-group)
1310                   subject (match-string 3))
1311             (if (nnshimbun-search-id nnshimbun-current-group id)
1312                 (throw 'exit headers)
1313               (push (make-full-mail-header
1314                      0
1315                      (nnshimbun-mime-encode-string subject)
1316                      (if (looking-at "</STRONG> *<EM>\\([^<]+\\)<")
1317                          (nnshimbun-mime-encode-string (match-string 1))
1318                        "")
1319                      "" id "" 0 0 url)
1320                     headers)))))
1321       headers)))
1322
1323 (provide 'nnshimbun)
1324 ;;; nnshimbun.el ends here.