Revert the accidental commit of unrelated patches in the previous commit.
[bpt/emacs.git] / lisp / gnus / nnweb.el
CommitLineData
eec82323 1;;; nnweb.el --- retrieving articles via web search engines
e84b4b86
TTN
2
3;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
d7a0267c 4;; 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
eec82323 5
6748645f 6;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
eec82323
LMI
7;; Keywords: news
8
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs 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;; GNU Emacs 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 GNU Emacs; see the file COPYING. If not, write to the
3a35cf56
LK
23;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24;; Boston, MA 02110-1301, USA.
eec82323
LMI
25
26;;; Commentary:
27
23f87bed 28;; Note: You need to have `w3' installed for some functions to work.
eec82323
LMI
29
30;;; Code:
31
5ab7173c
RS
32(eval-when-compile (require 'cl))
33
eec82323
LMI
34(require 'nnoo)
35(require 'message)
36(require 'gnus-util)
37(require 'gnus)
eec82323 38(require 'nnmail)
16409b0b 39(require 'mm-util)
23f87bed
MB
40(require 'mm-url)
41(eval-and-compile
0d5dc4a5 42 (ignore-errors
23f87bed
MB
43 (require 'url)))
44(autoload 'w3-parse-buffer "w3-parse")
eec82323
LMI
45
46(nnoo-declare nnweb)
47
48(defvoo nnweb-directory (nnheader-concat gnus-directory "nnweb/")
49 "Where nnweb will save its files.")
50
95fa1ff7 51(defvoo nnweb-type 'google
6748645f 52 "What search engine type is being used.
23f87bed 53Valid types include `google', `dejanews', and `gmane'.")
eec82323 54
16409b0b 55(defvar nnweb-type-definition
23f87bed 56 '((google
4a2358e9 57 (id . "http://www.google.com/groups?as_umsgid=%s&hl=en&dmode=source")
46e8fe3d 58 (result . "http://groups.google.com/group/%s/msg/%s?dmode=source")
4a2358e9 59 (article . nnweb-google-wash-article)
95fa1ff7
SZ
60 (reference . identity)
61 (map . nnweb-google-create-mapping)
62 (search . nnweb-google-search)
4a2358e9
MB
63 (address . "http://groups.google.com/groups")
64 (base . "http://groups.google.com")
95fa1ff7
SZ
65 (identifier . nnweb-google-identity))
66 (dejanews ;; alias of google
46e8fe3d
MB
67 (id . "http://www.google.com/groups?as_umsgid=%s&hl=en&dmode=source")
68 (result . "http://groups.google.com/group/%s/msg/%s?dmode=source")
69 (article . nnweb-google-wash-article)
95fa1ff7
SZ
70 (reference . identity)
71 (map . nnweb-google-create-mapping)
72 (search . nnweb-google-search)
73 (address . "http://groups.google.com/groups")
5f5475ac 74 (base . "http://groups.google.com")
95fa1ff7 75 (identifier . nnweb-google-identity))
23f87bed
MB
76 (gmane
77 (article . nnweb-gmane-wash-article)
78 (id . "http://gmane.org/view.php?group=%s")
79 (reference . identity)
80 (map . nnweb-gmane-create-mapping)
81 (search . nnweb-gmane-search)
719120ef 82 (address . "http://search.gmane.org/nov.php")
23f87bed 83 (identifier . nnweb-gmane-identity)))
eec82323
LMI
84 "Type-definition alist.")
85
86(defvoo nnweb-search nil
23f87bed 87 "Search string to feed to Google.")
eec82323 88
6748645f 89(defvoo nnweb-max-hits 999
eec82323
LMI
90 "Maximum number of hits to display.")
91
92(defvoo nnweb-ephemeral-p nil
93 "Whether this nnweb server is ephemeral.")
94
95;;; Internal variables
96
97(defvoo nnweb-articles nil)
98(defvoo nnweb-buffer nil)
719120ef 99(defvoo nnweb-group-alist nil)
eec82323
LMI
100(defvoo nnweb-group nil)
101(defvoo nnweb-hashtb nil)
102
103;;; Interface functions
104
105(nnoo-define-basics nnweb)
106
107(deffoo nnweb-retrieve-headers (articles &optional group server fetch-old)
108 (nnweb-possibly-change-server group server)
109 (save-excursion
110 (set-buffer nntp-server-buffer)
111 (erase-buffer)
112 (let (article header)
16409b0b
GM
113 (mm-with-unibyte-current-buffer
114 (while (setq article (pop articles))
115 (when (setq header (cadr (assq article nnweb-articles)))
116 (nnheader-insert-nov header))))
eec82323
LMI
117 'nov)))
118
119(deffoo nnweb-request-scan (&optional group server)
120 (nnweb-possibly-change-server group server)
95fa1ff7 121 (if nnweb-ephemeral-p
46e8fe3d
MB
122 (setq nnweb-hashtb (gnus-make-hashtable 4095))
123 (unless nnweb-articles
124 (nnweb-read-overview group)))
eec82323
LMI
125 (funcall (nnweb-definition 'map))
126 (unless nnweb-ephemeral-p
127 (nnweb-write-active)
128 (nnweb-write-overview group)))
129
130(deffoo nnweb-request-group (group &optional server dont-check)
46e8fe3d
MB
131 (nnweb-possibly-change-server group server)
132 (unless (or nnweb-ephemeral-p
6203370b
MB
133 dont-check
134 nnweb-articles)
46e8fe3d 135 (nnweb-read-overview group))
eec82323
LMI
136 (cond
137 ((not nnweb-articles)
138 (nnheader-report 'nnweb "No matching articles"))
139 (t
140 (let ((active (if nnweb-ephemeral-p
141 (cons (caar nnweb-articles)
142 (caar (last nnweb-articles)))
143 (cadr (assoc group nnweb-group-alist)))))
144 (nnheader-report 'nnweb "Opened group %s" group)
145 (nnheader-insert
146 "211 %d %d %d %s\n" (length nnweb-articles)
147 (car active) (cdr active) group)))))
148
149(deffoo nnweb-close-group (group &optional server)
150 (nnweb-possibly-change-server group server)
151 (when (gnus-buffer-live-p nnweb-buffer)
152 (save-excursion
153 (set-buffer nnweb-buffer)
154 (set-buffer-modified-p nil)
155 (kill-buffer nnweb-buffer)))
156 t)
157
158(deffoo nnweb-request-article (article &optional group server buffer)
159 (nnweb-possibly-change-server group server)
160 (save-excursion
161 (set-buffer (or buffer nntp-server-buffer))
162 (let* ((header (cadr (assq article nnweb-articles)))
163 (url (and header (mail-header-xref header))))
164 (when (or (and url
16409b0b 165 (mm-with-unibyte-current-buffer
23f87bed 166 (mm-url-insert url)))
eec82323
LMI
167 (and (stringp article)
168 (nnweb-definition 'id t)
169 (let ((fetch (nnweb-definition 'id))
95fa1ff7 170 art active)
eec82323
LMI
171 (when (string-match "^<\\(.*\\)>$" article)
172 (setq art (match-string 1 article)))
95fa1ff7 173 (when (and fetch art)
7ce31649
MB
174 (setq url (format fetch
175 (mm-url-form-encode-xwfu art)))
95fa1ff7 176 (mm-with-unibyte-current-buffer
23f87bed 177 (mm-url-insert url))
95fa1ff7
SZ
178 (if (nnweb-definition 'reference t)
179 (setq article
180 (funcall (nnweb-definition
181 'reference) article)))))))
eec82323 182 (unless nnheader-callback-function
95fa1ff7 183 (funcall (nnweb-definition 'article)))
eec82323 184 (nnheader-report 'nnweb "Fetched article %s" article)
16409b0b 185 (cons group (and (numberp article) article))))))
eec82323
LMI
186
187(deffoo nnweb-close-server (&optional server)
188 (when (and (nnweb-server-opened server)
189 (gnus-buffer-live-p nnweb-buffer))
190 (save-excursion
191 (set-buffer nnweb-buffer)
192 (set-buffer-modified-p nil)
193 (kill-buffer nnweb-buffer)))
194 (nnoo-close-server 'nnweb server))
195
196(deffoo nnweb-request-list (&optional server)
197 (nnweb-possibly-change-server nil server)
198 (save-excursion
199 (set-buffer nntp-server-buffer)
46e8fe3d 200 (nnmail-generate-active (list (assoc server nnweb-group-alist)))
eec82323
LMI
201 t))
202
203(deffoo nnweb-request-update-info (group info &optional server)
16409b0b 204 (nnweb-possibly-change-server group server))
eec82323
LMI
205
206(deffoo nnweb-asynchronous-p ()
23f87bed 207 nil)
eec82323
LMI
208
209(deffoo nnweb-request-create-group (group &optional server args)
210 (nnweb-possibly-change-server nil server)
211 (nnweb-request-delete-group group)
46e8fe3d 212 (push `(,group ,(cons 1 0)) nnweb-group-alist)
eec82323
LMI
213 (nnweb-write-active)
214 t)
215
216(deffoo nnweb-request-delete-group (group &optional force server)
217 (nnweb-possibly-change-server group server)
16409b0b
GM
218 (gnus-pull group nnweb-group-alist t)
219 (nnweb-write-active)
eec82323
LMI
220 (gnus-delete-file (nnweb-overview-file group))
221 t)
222
223(nnoo-define-skeleton nnweb)
224
225;;; Internal functions
226
227(defun nnweb-read-overview (group)
228 "Read the overview of GROUP and build the map."
229 (when (file-exists-p (nnweb-overview-file group))
16409b0b 230 (mm-with-unibyte-buffer
eec82323
LMI
231 (nnheader-insert-file-contents (nnweb-overview-file group))
232 (goto-char (point-min))
233 (let (header)
234 (while (not (eobp))
235 (setq header (nnheader-parse-nov))
236 (forward-line 1)
237 (push (list (mail-header-number header)
238 header (mail-header-xref header))
239 nnweb-articles)
240 (nnweb-set-hashtb header (car nnweb-articles)))))))
241
242(defun nnweb-write-overview (group)
243 "Write the overview file for GROUP."
16409b0b 244 (with-temp-file (nnweb-overview-file group)
eec82323
LMI
245 (let ((articles nnweb-articles))
246 (while articles
247 (nnheader-insert-nov (cadr (pop articles)))))))
248
249(defun nnweb-set-hashtb (header data)
250 (gnus-sethash (nnweb-identifier (mail-header-xref header))
251 data nnweb-hashtb))
252
253(defun nnweb-get-hashtb (url)
254 (gnus-gethash (nnweb-identifier url) nnweb-hashtb))
255
256(defun nnweb-identifier (ident)
257 (funcall (nnweb-definition 'identifier) ident))
258
259(defun nnweb-overview-file (group)
260 "Return the name of the overview file of GROUP."
261 (nnheader-concat nnweb-directory group ".overview"))
262
263(defun nnweb-write-active ()
264 "Save the active file."
16409b0b
GM
265 (gnus-make-directory nnweb-directory)
266 (with-temp-file (nnheader-concat nnweb-directory "active")
eec82323
LMI
267 (prin1 `(setq nnweb-group-alist ',nnweb-group-alist) (current-buffer))))
268
269(defun nnweb-read-active ()
270 "Read the active file."
271 (load (nnheader-concat nnweb-directory "active") t t t))
272
273(defun nnweb-definition (type &optional noerror)
274 "Return the definition of TYPE."
275 (let ((def (cdr (assq type (assq nnweb-type nnweb-type-definition)))))
276 (when (and (not def)
277 (not noerror))
278 (error "Undefined definition %s" type))
279 def))
280
281(defun nnweb-possibly-change-server (&optional group server)
eec82323
LMI
282 (when server
283 (unless (nnweb-server-opened server)
46e8fe3d
MB
284 (nnweb-open-server server))
285 (nnweb-init server))
eec82323
LMI
286 (unless nnweb-group-alist
287 (nnweb-read-active))
95fa1ff7
SZ
288 (unless nnweb-hashtb
289 (setq nnweb-hashtb (gnus-make-hashtable 4095)))
eec82323 290 (when group
46e8fe3d 291 (setq nnweb-group group)))
eec82323
LMI
292
293(defun nnweb-init (server)
294 "Initialize buffers and such."
295 (unless (gnus-buffer-live-p nnweb-buffer)
296 (setq nnweb-buffer
297 (save-excursion
16409b0b
GM
298 (mm-with-unibyte
299 (nnheader-set-temp-buffer
300 (format " *nnweb %s %s %s*"
301 nnweb-type nnweb-search server))
302 (current-buffer))))))
eec82323 303
95fa1ff7 304;;;
4a2358e9 305;;; groups.google.com
95fa1ff7
SZ
306;;;
307
308(defun nnweb-google-wash-article ()
4a2358e9 309 ;; We have Google's masked e-mail addresses here. :-/
719120ef 310 (let ((case-fold-search t)
0565caeb
MB
311 (start-re "<pre>[\r\n ]*")
312 (end-re "[\r\n ]*</pre>"))
95fa1ff7 313 (goto-char (point-min))
d752cf53
MB
314 (if (save-excursion
315 (or (re-search-forward "The requested message.*could not be found."
316 nil t)
719120ef
MB
317 (not (and (re-search-forward start-re nil t)
318 (re-search-forward end-re nil t)))))
d752cf53
MB
319 ;; FIXME: Don't know how to indicate "not found".
320 ;; Should this function throw an error? --rsteib
321 (progn
322 (gnus-message 3 "Requested article not found")
323 (erase-buffer))
324 (delete-region (point-min)
719120ef 325 (re-search-forward start-re))
d752cf53 326 (goto-char (point-min))
719120ef
MB
327 (delete-region (progn
328 (re-search-forward end-re)
329 (match-beginning 0))
d752cf53
MB
330 (point-max))
331 (mm-url-decode-entities))))
95fa1ff7
SZ
332
333(defun nnweb-google-parse-1 (&optional Message-ID)
46e8fe3d 334 "Parse search result in current buffer."
95fa1ff7
SZ
335 (let ((i 0)
336 (case-fold-search t)
337 (active (cadr (assoc nnweb-group nnweb-group-alist)))
338 Subject Score Date Newsgroups From
339 map url mid)
340 (unless active
46e8fe3d 341 (push (list nnweb-group (setq active (cons 1 0)))
95fa1ff7
SZ
342 nnweb-group-alist))
343 ;; Go through all the article hits on this page.
344 (goto-char (point-min))
46e8fe3d
MB
345 (while
346 (re-search-forward
347 "a +href=\"/group/\\([^>\"]+\\)/browse_thread/[^>]+#\\([0-9a-f]+\\)"
348 nil t)
349 (setq Newsgroups (match-string-no-properties 1)
350 ;; Note: Starting with Google Groups 2, `mid' is a Google-internal
351 ;; ID, not a proper Message-ID.
352 mid (match-string-no-properties 2)
debad045 353 url (format
46e8fe3d 354 (nnweb-definition 'result) Newsgroups mid))
95fa1ff7
SZ
355 (narrow-to-region (search-forward ">" nil t)
356 (search-forward "</a>" nil t))
23f87bed
MB
357 (mm-url-remove-markup)
358 (mm-url-decode-entities)
95fa1ff7
SZ
359 (setq Subject (buffer-string))
360 (goto-char (point-max))
361 (widen)
46e8fe3d 362 (narrow-to-region (point)
c91f4b83 363 (search-forward "</table" nil t))
46e8fe3d
MB
364
365 (mm-url-remove-markup)
366 (mm-url-decode-entities)
c91f4b83
MB
367 (goto-char (point-max))
368 (when
369 (re-search-backward
370 "^\\(\\w+\\) \\([0-9]+\\)\\(?: \\([0-9]\\{4\\}\\)\\)? by \\(.*\\)"
371 nil t)
5ebdc299 372 (setq Date (format "%s %s 00:00:00 %s"
46e8fe3d
MB
373 (match-string 1)
374 (match-string 2)
375 (or (match-string 3)
5ebdc299 376 (substring (current-time-string) -4))))
c91f4b83 377 (setq From (match-string 4)))
46e8fe3d 378 (widen)
95fa1ff7
SZ
379 (incf i)
380 (unless (nnweb-get-hashtb url)
381 (push
382 (list
383 (incf (cdr active))
384 (make-full-mail-header
385 (cdr active) (if Newsgroups
386 (concat "(" Newsgroups ") " Subject)
387 Subject)
388 From Date (or Message-ID mid)
389 nil 0 0 url))
390 map)
391 (nnweb-set-hashtb (cadar map) (car map))))
392 map))
393
394(defun nnweb-google-reference (id)
395 (let ((map (nnweb-google-parse-1 id)) header)
396 (setq nnweb-articles
397 (nconc nnweb-articles map))
398 (when (setq header (cadar map))
399 (mm-with-unibyte-current-buffer
23f87bed 400 (mm-url-insert (mail-header-xref header)))
95fa1ff7
SZ
401 (caar map))))
402
403(defun nnweb-google-create-mapping ()
debad045 404 "Perform the search and create a number-to-url alist."
95fa1ff7
SZ
405 (save-excursion
406 (set-buffer nnweb-buffer)
407 (erase-buffer)
719120ef 408 (nnheader-message 7 "Searching google...")
95fa1ff7 409 (when (funcall (nnweb-definition 'search) nnweb-search)
23f87bed
MB
410 (let ((more t)
411 (i 0))
95fa1ff7
SZ
412 (while more
413 (setq nnweb-articles
414 (nconc nnweb-articles (nnweb-google-parse-1)))
23f87bed
MB
415 ;; Check if there are more articles to fetch
416 (goto-char (point-min))
417 (incf i 100)
418 (if (or (not (re-search-forward
bd876f90 419 "<a [^>]+href=\"\n?\\([^>\" \n\t]+\\)[^<]*<img[^>]+src=[^>]+next"
719120ef 420 nil t))
23f87bed
MB
421 (>= i nnweb-max-hits))
422 (setq more nil)
423 ;; Yup, there are more articles
5f5475ac 424 (setq more (concat (nnweb-definition 'base) (match-string 1)))
23f87bed
MB
425 (when more
426 (erase-buffer)
719120ef 427 (nnheader-message 7 "Searching google...(%d)" i)
23f87bed 428 (mm-url-insert more))))
95fa1ff7 429 ;; Return the articles in the right order.
719120ef 430 (nnheader-message 7 "Searching google...done")
95fa1ff7
SZ
431 (setq nnweb-articles
432 (sort nnweb-articles 'car-less-than-car))))))
433
434(defun nnweb-google-search (search)
23f87bed 435 (mm-url-insert
95fa1ff7
SZ
436 (concat
437 (nnweb-definition 'address)
438 "?"
23f87bed 439 (mm-url-encode-www-form-urlencoded
95fa1ff7 440 `(("q" . ,search)
7ce31649
MB
441 ("num" . ,(number-to-string
442 (min 100 nnweb-max-hits)))
95fa1ff7 443 ("hq" . "")
5f5475ac 444 ("hl" . "en")
95fa1ff7
SZ
445 ("lr" . "")
446 ("safe" . "off")
46e8fe3d
MB
447 ("sites" . "groups")
448 ("filter" . "0")))))
95fa1ff7
SZ
449 t)
450
451(defun nnweb-google-identity (url)
452 "Return an unique identifier based on URL."
453 (if (string-match "selm=\\([^ &>]+\\)" url)
454 (match-string 1 url)
455 url))
456
23f87bed
MB
457;;;
458;;; gmane.org
459;;;
460(defun nnweb-gmane-create-mapping ()
461 "Perform the search and create a number-to-url alist."
462 (save-excursion
463 (set-buffer nnweb-buffer)
719120ef
MB
464 (let ((case-fold-search t)
465 (active (or (cadr (assoc nnweb-group nnweb-group-alist))
466 (cons 1 0)))
467 map)
468 (erase-buffer)
469 (nnheader-message 7 "Searching Gmane..." )
470 (when (funcall (nnweb-definition 'search) nnweb-search)
23f87bed 471 (goto-char (point-min))
719120ef
MB
472 ;; Skip the status line
473 (forward-line 1)
474 ;; Thanks to Olly Betts we now have NOV lines in our buffer!
475 (while (not (eobp))
476 (unless (or (eolp) (looking-at "\x0d"))
477 (let ((header (nnheader-parse-nov)))
478 (let ((xref (mail-header-xref header))
479 (from (mail-header-from header))
480 (subject (mail-header-subject header))
481 (rfc2047-encoding-type 'mime))
ba361211 482 (when (string-match " \\([^:]+\\)[:/]\\([0-9]+\\)" xref)
719120ef
MB
483 (mail-header-set-xref
484 header
485 (format "http://article.gmane.org/%s/%s/raw"
486 (match-string 1 xref)
487 (match-string 2 xref))))
488
489 ;; Add host part to gmane-encrypted addresses
490 (when (string-match "@$" from)
491 (mail-header-set-from header
492 (concat from "public.gmane.org")))
493
494 (mail-header-set-subject header
495 (rfc2047-encode-string subject))
496
497 (unless (nnweb-get-hashtb (mail-header-xref header))
ba361211
MB
498 (mail-header-set-number header (incf (cdr active)))
499 (push (list (mail-header-number header) header) map)
719120ef
MB
500 (nnweb-set-hashtb (cadar map) (car map))))))
501 (forward-line 1)))
502 (nnheader-message 7 "Searching Gmane...done")
503 (setq nnweb-articles
504 (sort (nconc nnweb-articles map) 'car-less-than-car)))))
23f87bed
MB
505
506(defun nnweb-gmane-wash-article ()
507 (let ((case-fold-search t))
508 (goto-char (point-min))
719120ef
MB
509 (when (search-forward "<!--X-Head-of-Message-->" nil t)
510 (delete-region (point-min) (point))
511 (goto-char (point-min))
512 (while (looking-at "^<li><em>\\([^ ]+\\)</em>.*</li>")
513 (replace-match "\\1\\2" t)
514 (forward-line 1))
515 (mm-url-remove-markup))))
23f87bed
MB
516
517(defun nnweb-gmane-search (search)
518 (mm-url-insert
519 (concat
520 (nnweb-definition 'address)
521 "?"
522 (mm-url-encode-www-form-urlencoded
719120ef
MB
523 `(("query" . ,search)
524 ("HITSPERPAGE" . ,(number-to-string nnweb-max-hits))))))
23f87bed 525 (setq buffer-file-name nil)
719120ef
MB
526 (set-buffer-multibyte t)
527 (mm-decode-coding-region (point-min) (point-max) 'utf-8)
23f87bed
MB
528 t)
529
23f87bed
MB
530(defun nnweb-gmane-identity (url)
531 "Return a unique identifier based on URL."
532 (if (string-match "group=\\(.+\\)" url)
533 (match-string 1 url)
534 url))
535
16409b0b
GM
536;;;
537;;; General web/w3 interface utility functions
538;;;
539
540(defun nnweb-insert-html (parse)
541 "Insert HTML based on a w3 parse tree."
542 (if (stringp parse)
95fa1ff7 543 (insert (nnheader-string-as-multibyte parse))
16409b0b
GM
544 (insert "<" (symbol-name (car parse)) " ")
545 (insert (mapconcat
546 (lambda (param)
547 (concat (symbol-name (car param)) "="
548 (prin1-to-string
549 (if (consp (cdr param))
550 (cadr param)
551 (cdr param)))))
552 (nth 1 parse)
553 " "))
554 (insert ">\n")
555 (mapcar 'nnweb-insert-html (nth 2 parse))
556 (insert "</" (symbol-name (car parse)) ">\n")))
557
16409b0b
GM
558(defun nnweb-parse-find (type parse &optional maxdepth)
559 "Find the element of TYPE in PARSE."
560 (catch 'found
561 (nnweb-parse-find-1 type parse maxdepth)))
562
563(defun nnweb-parse-find-1 (type contents maxdepth)
564 (when (or (null maxdepth)
565 (not (zerop maxdepth)))
566 (when (consp contents)
567 (when (eq (car contents) type)
568 (throw 'found contents))
569 (when (listp (cdr contents))
570 (dolist (element contents)
571 (when (consp element)
572 (nnweb-parse-find-1 type element
573 (and maxdepth (1- maxdepth)))))))))
574
575(defun nnweb-parse-find-all (type parse)
576 "Find all elements of TYPE in PARSE."
577 (catch 'found
578 (nnweb-parse-find-all-1 type parse)))
579
580(defun nnweb-parse-find-all-1 (type contents)
581 (let (result)
582 (when (consp contents)
583 (if (eq (car contents) type)
584 (push contents result)
585 (when (listp (cdr contents))
586 (dolist (element contents)
587 (when (consp element)
588 (setq result
589 (nconc result (nnweb-parse-find-all-1 type element))))))))
590 result))
591
592(defvar nnweb-text)
593(defun nnweb-text (parse)
594 "Return a list of text contents in PARSE."
595 (let ((nnweb-text nil))
596 (nnweb-text-1 parse)
597 (nreverse nnweb-text)))
598
599(defun nnweb-text-1 (contents)
600 (dolist (element contents)
601 (if (stringp element)
602 (push element nnweb-text)
603 (when (and (consp element)
604 (listp (cdr element)))
605 (nnweb-text-1 element)))))
606
eec82323
LMI
607(provide 'nnweb)
608
ab5796a9 609;;; arch-tag: f59307eb-c90f-479f-b7d2-dbd8bf51b697
eec82323 610;;; nnweb.el ends here