home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / elisp / patches / gnus-hier.el < prev    next >
Encoding:
Text File  |  1992-11-25  |  25.8 KB  |  788 lines

  1. ; Path: hal.com!olivea!uunet!cis.ohio-state.edu!east-wind.ORG!kingdon
  2. ; From: kingdon@east-wind.ORG (Jim Kingdon)
  3. ; Newsgroups: gnu.emacs.sources
  4. ; Subject: Hierarchical patches for GNUS 3.14.1
  5. ; Date: 23 Nov 92 06:33:59 GMT
  6. ; Organization: Source only  Discussion and requests in gnu.emacs.help.
  7. ; Enclosed are patches to make GNUS a hierarchical newsreader.  What I
  8. ; mean by that is that when you start up GNUS for the first time, rather
  9. ; than showing you all the newsgroups, it will just show one line for
  10. ; each top-level hierarchy ("soc", "rec", "alt", etc.).  You can then
  11. ; unsubscribe to the whole hierarchy with one keystroke, or you can type
  12. ; space to "explode" the hierarchy into all the second-level groups and
  13. ; hierarchies (e.g. "comp" will explode into "comp.sys", "comp.os",
  14. ; "comp.compilers", etc.).
  15. ; These patches should be considered experimental, both in the sense
  16. ; that some features probably should be added (but I didn't get around
  17. ; to it, or wasn't sure just how they should work), and that it has not
  18. ; been extensively tested for bugs.
  19.  
  20. ; LCD Archive Entry:
  21. ; gnus-hier|Jim Kingdon|kingdon@east-wind.ORG|
  22. ; Patches to make GNUS a hierarchical newsreader.|
  23. ; 92-11-21||~/patches/gnus-hier.el.Z|
  24.  
  25. ; *** gnus.el    Sat Nov 21 14:23:58 1992
  26. ; --- gnus-hier.el    Sat Nov 21 18:43:30 1992
  27. ; ***************
  28. ; *** 224,229 ****
  29. ; --- 224,250 ----
  30.   (defvar gnus-show-threads t
  31.     "*Show conversation threads in Subject Mode if non-nil.")
  32.   
  33. + ;;; Notes on hierarchy support:
  34. + ;;; * Right now, a hierarchy can be "exploded" into groups, but once
  35. + ;;; this is done, there is no way to "implode" a bunch of groups back
  36. + ;;; into a hierarchy.  One way to implement this would be to have gnus,
  37. + ;;; every time it starts up, look for a bunch of groups in .newsrc all of
  38. + ;;; which are in the same hierarchy, all of which have the same
  39. + ;;; subscribed/unsubscribed status, and all of which show no articles
  40. + ;;; that have been read (excluding articles no longer on the system),
  41. + ;;; and then automatically implode.
  42. + ;;; * The .newsrc written is not really compatible with non-hierarchical
  43. + ;;; newsreaders.  It might be better to rename it, or redesign things
  44. + ;;; somehow to make it easy for people to switch back and forth between
  45. + ;;; hierarchical and non-hierarchical newsreaders.  
  46. + ;;; Note that hierarchical GNUS can read a non-hierarchical .newsrc;
  47. + ;;; this feature should be preserved.
  48. + ;;; * Probably hierarchies should only be shown if some group in them
  49. + ;;; has unread news.  This is not currently the way it works.
  50. + (defvar gnus-show-hierarchies t
  51. +   "*Instead of subscribing users to new newsgroups, subscribe them to
  52. + the hierarchies which contain those groups.")
  53.   (defvar gnus-thread-hide-subject t
  54.     "*Non-nil means hide subjects for thread subtrees.")
  55.   
  56. ***************
  57. *** 330,335 ****
  58. --- 351,357 ----
  59.   (defvar gnus-subscribe-newsgroup-method
  60.     (function gnus-subscribe-alphabetically)
  61.     "*A function called with a newsgroup name when new newsgroup is found.
  62. + The argument is ('hierarchy name) or ('group name).
  63.   The function gnus-subscribe-randomly inserts a new newsgroup a the
  64.   beginning of newsgroups.  The function gnus-subscribe-alphabetically
  65.   inserts it in strict alphabetic order.  The function
  66. ***************
  67. *** 561,567 ****
  68.   
  69.   ;; Internal variables.
  70.   
  71. ! (defconst gnus-version "GNUS 3.14.1"
  72.     "Version numbers of this version of GNUS.")
  73.   
  74.   (defvar gnus-Info-nodes
  75. --- 583,589 ----
  76.   
  77.   ;; Internal variables.
  78.   
  79. ! (defconst gnus-version "GNUS 3.14.1 with hierarchy patches"
  80.     "Version numbers of this version of GNUS.")
  81.   
  82.   (defvar gnus-Info-nodes
  83. ***************
  84. *** 1133,1138 ****
  85. --- 1155,1161 ----
  86.     ;; Insert the message.
  87.     (insert "
  88.                      GNUS Version 3.14.1
  89. +                   with hierarchy patches
  90.   
  91.            NNTP-based News Reader for GNU Emacs
  92.   
  93. ***************
  94. *** 1193,1201 ****
  95.       (newsrc gnus-newsrc-assoc)
  96.       (group-info nil)
  97.       (group-name nil)
  98. !     (unread-count 0)
  99. !     ;; This specifies the format of Group buffer.
  100. !     (cntl "%s%s%5d: %s\n"))
  101.       (erase-buffer)
  102.       ;; List newsgroups.
  103.       (while newsrc
  104. --- 1216,1222 ----
  105.       (newsrc gnus-newsrc-assoc)
  106.       (group-info nil)
  107.       (group-name nil)
  108. !     (unread-count 0))
  109.       (erase-buffer)
  110.       ;; List newsgroups.
  111.       (while newsrc
  112. ***************
  113. *** 1204,1228 ****
  114.         (setq unread-count (nth 1 (gnus-gethash group-name gnus-unread-hashtb)))
  115.         (if (or all
  116.             (and (nth 1 group-info)    ;Subscribed.
  117. !            (> unread-count 0)))    ;There are unread articles.
  118.         ;; Yes, I can use gnus-Group-prepare-line, but this is faster.
  119. !       (insert
  120. !        (format cntl
  121. !            ;; Subscribed or not.
  122. !            (if (nth 1 group-info) " " "U")
  123. !            ;; Has new news?
  124. !            (if (and (> unread-count 0)
  125. !                 (>= 0
  126. !                 (- unread-count
  127. !                    (length
  128. !                     (cdr (assoc group-name
  129. !                         gnus-marked-assoc))))))
  130. !                "*" " ")
  131. !            ;; Number of unread articles.
  132. !            unread-count
  133. !            ;; Newsgroup name.
  134. !            group-name))
  135. !     )
  136.         (setq newsrc (cdr newsrc))
  137.         )
  138.       (setq gnus-have-all-newsgroups all)
  139. --- 1225,1257 ----
  140.         (setq unread-count (nth 1 (gnus-gethash group-name gnus-unread-hashtb)))
  141.         (if (or all
  142.             (and (nth 1 group-info)    ;Subscribed.
  143. !            (or (eq (nth 2 group-info) 'hierarchy)
  144. !                (> unread-count 0))))    ;There are unread articles.
  145.         ;; Yes, I can use gnus-Group-prepare-line, but this is faster.
  146. !       (progn
  147. !         ;; Subscribed or not.
  148. !         (insert (if (nth 1 group-info) " " "U"))
  149. !         ;; Has new news?
  150. !         (if (eq (nth 2 group-info) 'hierarchy)
  151. !         (insert "     H")
  152. !           (insert
  153. !            (if (and (> unread-count 0)
  154. !             (>= 0
  155. !                 (- unread-count
  156. !                    (length
  157. !                 (cdr (assoc group-name
  158. !                         gnus-marked-assoc))))))
  159. !            "*" " ")
  160. !            (format "%5d" unread-count))
  161. !           )
  162. !         (insert
  163. !          ": "
  164. !          ;; Newsgroup name.
  165. !          group-name
  166. !          "\n")))
  167.         (setq newsrc (cdr newsrc))
  168.         )
  169.       (setq gnus-have-all-newsgroups all)
  170. ***************
  171. *** 1235,1262 ****
  172.   INFO is an element of gnus-newsrc-assoc or gnus-killed-assoc."
  173.     (let* ((group-name (car info))
  174.        (unread-count
  175. !       (or (nth 1 (gnus-gethash group-name gnus-unread-hashtb))
  176. !           ;; Not in hash table, so compute it now.
  177. !           (gnus-number-of-articles
  178. !            (gnus-difference-of-range
  179. !         (nth 2 (gnus-gethash group-name gnus-active-hashtb))
  180. !         (nthcdr 2 info)))))
  181. !      ;; This specifies the format of Group buffer.
  182. !      (cntl "%s%s%5d: %s\n"))
  183. !     (format cntl
  184. !         ;; Subscribed or not.
  185. !         (if (nth 1 info) " " "U")
  186. !         ;; Has new news?
  187. !         (if (and (> unread-count 0)
  188. !              (>= 0
  189. !              (- unread-count
  190. !                 (length
  191. !                  (cdr (assoc group-name gnus-marked-assoc))))))
  192. !         "*" " ")
  193. !         ;; Number of unread articles.
  194. !         unread-count
  195.           ;; Newsgroup name.
  196.           group-name
  197.           )))
  198.   
  199.   (defun gnus-Group-update-group (group &optional visible-only)
  200. --- 1264,1298 ----
  201.   INFO is an element of gnus-newsrc-assoc or gnus-killed-assoc."
  202.     (let* ((group-name (car info))
  203.        (unread-count
  204. !       (if (eq (nth 2 info) 'hierarchy)
  205. !           0
  206. !         (or (nth 1 (gnus-gethash group-name gnus-unread-hashtb))
  207. !         ;; Not in hash table, so compute it now.
  208. !         (gnus-number-of-articles
  209. !          (gnus-difference-of-range
  210. !           (nth 2 (gnus-gethash group-name gnus-active-hashtb))
  211. !           (nthcdr 2 info)))))))
  212. !     (concat
  213. !      ;; Subscribed or not.
  214. !      (if (nth 1 info) " " "U")
  215. !      ;; Has new news?
  216. !      (if (and (> unread-count 0)
  217. !           (>= 0
  218. !           (- unread-count
  219. !              (length
  220. !               (cdr (assoc group-name gnus-marked-assoc))))))
  221. !      "*" " ")
  222. !         ;; Number of unread articles or H for hierarchy
  223. !         (if (eq (nth 2 info) 'hierarchy)
  224. !                "    H"
  225. !           (format "%5d" unread-count))
  226. !         ": "
  227.           ;; Newsgroup name.
  228.           group-name
  229. +         "\n"
  230.           )))
  231.   
  232.   (defun gnus-Group-update-group (group &optional visible-only)
  233. ***************
  234. *** 1289,1295 ****
  235.     "Get newsgroup name around point."
  236.     (save-excursion
  237.       (beginning-of-line)
  238. !     (if (looking-at ".[* \t]*[0-9]+:[ \t]+\\([^ \t\n]+\\)$")
  239.       (buffer-substring (match-beginning 1) (match-end 1))
  240.         )))
  241.   
  242. --- 1325,1331 ----
  243.     "Get newsgroup name around point."
  244.     (save-excursion
  245.       (beginning-of-line)
  246. !     (if (looking-at ".[* \t]*[0-9H]+:[ \t]+\\([^ \t\n]+\\)$")
  247.       (buffer-substring (match-beginning 1) (match-end 1))
  248.         )))
  249.   
  250. ***************
  251. *** 1298,1313 ****
  252.   If argument ALL is non-nil, already read articles become readable.
  253.   If optional argument NO-ARTICLE is non-nil, no article body is displayed."
  254.     (interactive "P")
  255. !   (let ((group (gnus-Group-group-name))) ;Newsgroup name to read.
  256.       (if group
  257. !     (gnus-Subject-read-group
  258. !      group
  259. !      (or all
  260. !          ;;(not (nth 1 (assoc group gnus-newsrc-assoc)))    ;Unsubscribed
  261. !          (zerop
  262. !           (nth 1 (gnus-gethash group gnus-unread-hashtb))))    ;No unread
  263. !      no-article
  264. !      ))
  265.       ))
  266.   
  267.   (defun gnus-Group-select-group (all)
  268. --- 1334,1383 ----
  269.   If argument ALL is non-nil, already read articles become readable.
  270.   If optional argument NO-ARTICLE is non-nil, no article body is displayed."
  271.     (interactive "P")
  272. !   (let ((group (gnus-Group-group-name)) ;Newsgroup name to read.
  273. !     (tail gnus-newsrc-assoc)
  274. !     (prev nil))
  275.       (if group
  276. !     (progn
  277. !       ;; First, find the group in gnus-newsrc-assoc so we know
  278. !       ;; whether it is a hierarchy.
  279. !       (catch 'exit-loop
  280. !         (while tail
  281. !           (if (string= (car (car tail)) group)
  282. !           (throw 'exit-loop nil))
  283. !           (setq prev tail)
  284. !           (setq tail (cdr tail))))
  285. !       (if (eq (nth 2 (car tail)) 'hierarchy)
  286. !           (progn
  287. !         (message "Exploding hierarchy %s..." group)
  288. !         ;; Delete group from gnus-newsrc-assoc
  289. !         (if prev
  290. !             (setcdr prev (cdr tail))
  291. !           (setq gnus-newsrc-assoc (cdr tail)))
  292. !         (gnus-update-newsrc-buffer group 'delete)
  293. !         ;; Subscribe to groups and hierarchies
  294. !         ;; one level down from group
  295. !         (let ((new-newsgroups (gnus-find-new-newsgroups group)))
  296. !           (while new-newsgroups
  297. !             (funcall gnus-subscribe-newsgroup-method
  298. !                  (car new-newsgroups))
  299. !             (setq new-newsgroups (cdr new-newsgroups))
  300. !             ))
  301. !         (message "Exploding hierarchy %s...done" group)
  302. !         ;; This is not necessarily the cleanest way to show them;
  303. !         ;; i.e. does it cause unpleasant changes to the display?
  304. !         (gnus-Group-list-groups gnus-have-all-newsgroups)
  305. !         )
  306. !         ;; It's a group.  Read it.
  307. !         (gnus-Subject-read-group
  308. !          group
  309. !          (or all
  310. !          ;;(not (nth 1 (assoc group gnus-newsrc-assoc)))    ;Unsubscribed
  311. !          (zerop
  312. !           (nth 1 (gnus-gethash group gnus-unread-hashtb))))    ;No unread
  313. !          no-article
  314. !          )
  315. !         )))
  316.       ))
  317.   
  318.   (defun gnus-Group-select-group (all)
  319. ***************
  320. *** 1339,1345 ****
  321.       (regexp 
  322.        (format "^%s[ \t]*\\(%s\\):"
  323.            (if any-group ".." " [ \t]")
  324. !          (if any-group "[0-9]+" "[1-9][0-9]*")))
  325.       (found nil))
  326.       (if backward
  327.       (beginning-of-line)
  328. --- 1409,1415 ----
  329.       (regexp 
  330.        (format "^%s[ \t]*\\(%s\\):"
  331.            (if any-group ".." " [ \t]")
  332. !          (if any-group "[0-9H]+" "[1-9][0-9]*")))
  333.       (found nil))
  334.       (if backward
  335.       (beginning-of-line)
  336. ***************
  337. *** 4575,4581 ****
  338.     (let ((groups gnus-newsrc-assoc)
  339.       (before nil))
  340.       (while (and (not before) groups)
  341. !       (if (string< newgroup (car (car groups)))
  342.         (setq before (car (car groups)))
  343.       (setq groups (cdr groups))))
  344.       (gnus-subscribe-newsgroup newgroup before)
  345. --- 4645,4651 ----
  346.     (let ((groups gnus-newsrc-assoc)
  347.       (before nil))
  348.       (while (and (not before) groups)
  349. !       (if (string< (car (cdr newgroup)) (car (car groups)))
  350.         (setq before (car (car groups)))
  351.       (setq groups (cdr groups))))
  352.       (gnus-subscribe-newsgroup newgroup before)
  353. ***************
  354. *** 4586,4592 ****
  355.     ;; Basic ideas by mike-w@cs.aukuni.ac.nz (Mike Williams)
  356.     (save-excursion
  357.       (set-buffer (find-file-noselect gnus-current-startup-file))
  358. !     (let ((groupkey newgroup)
  359.         (before nil))
  360.         (while (and (not before) groupkey)
  361.       (goto-char (point-min))
  362. --- 4656,4662 ----
  363.     ;; Basic ideas by mike-w@cs.aukuni.ac.nz (Mike Williams)
  364.     (save-excursion
  365.       (set-buffer (find-file-noselect gnus-current-startup-file))
  366. !     (let ((groupkey (car (cdr newgroup)))
  367.         (before nil))
  368.         (while (and (not before) groupkey)
  369.       (goto-char (point-min))
  370. ***************
  371. *** 4596,4602 ****
  372.                 (progn
  373.               (setq before (buffer-substring
  374.                         (match-beginning 1) (match-end 1)))
  375. !             (string< before newgroup)))
  376.           ))
  377.       ;; Remove tail of newsgroup name (eg. a.b.c -> a.b)
  378.       (setq groupkey
  379. --- 4666,4672 ----
  380.                 (progn
  381.               (setq before (buffer-substring
  382.                         (match-beginning 1) (match-end 1)))
  383. !             (string< before (car (cdr newgroup)))))
  384.           ))
  385.       ;; Remove tail of newsgroup name (eg. a.b.c -> a.b)
  386.       (setq groupkey
  387. ***************
  388. *** 4606,4615 ****
  389.         )))
  390.   
  391.   (defun gnus-subscribe-newsgroup (newsgroup &optional next)
  392. !   "Subscribe new NEWSGROUP.
  393.   If optional argument NEXT is non-nil, it is inserted before NEXT."
  394. !   (gnus-insert-newsgroup (list newsgroup t) next)
  395. !   (message "Newsgroup %s is subscribed" newsgroup))
  396.   
  397.   ;; For directories
  398.   
  399. --- 4676,4691 ----
  400.         )))
  401.   
  402.   (defun gnus-subscribe-newsgroup (newsgroup &optional next)
  403. !   "Subscribe new NEWSGROUP, which is a list ('group name) or ('hierarchy name).
  404.   If optional argument NEXT is non-nil, it is inserted before NEXT."
  405. !   (gnus-insert-newsgroup
  406. !    (if (eq (car newsgroup) 'hierarchy)
  407. !        (list (car (cdr newsgroup)) t 'hierarchy)
  408. !      (list (car (cdr newsgroup)) t))
  409. !    next)
  410. !   (message "%s %s is subscribed"
  411. !        (if (eq (car newsgroup) 'hierarchy) "Hierarchy" "Newsgroup")
  412. !        (car (cdr newsgroup))))
  413.   
  414.   ;; For directories
  415.   
  416. ***************
  417. *** 5671,5678 ****
  418.   
  419.   ;; GNUS internal format of gnus-newsrc-assoc and gnus-killed-assoc:
  420.   ;; (("general" t (1 . 1))
  421. ! ;;  ("misc"    t (1 . 10) (12 . 15))
  422. ! ;;  ("test"  nil (1 . 99)) ...)
  423.   ;; GNUS internal format of gnus-marked-assoc:
  424.   ;; (("general" 1 2 3)
  425.   ;;  ("misc" 2) ...)
  426. --- 5747,5755 ----
  427.   
  428.   ;; GNUS internal format of gnus-newsrc-assoc and gnus-killed-assoc:
  429.   ;; (("general" t (1 . 1))
  430. ! ;;  ("misc.foo"    t (1 . 10) (12 . 15))
  431. ! ;;  ("test"  nil (1 . 99))
  432. ! ;;  ("talk"  nil 'hierarchy) ...)
  433.   ;; GNUS internal format of gnus-marked-assoc:
  434.   ;; (("general" 1 2 3)
  435.   ;;  ("misc" 2) ...)
  436. ***************
  437. *** 5720,5744 ****
  438.                     (list newsgroup t))
  439.                     (car (car gnus-newsrc-assoc)))))
  440.   
  441. ! (defun gnus-find-new-newsgroups ()
  442. !   "Looking for new newsgroups and return names.
  443. ! `-n' option of options line in .newsrc file is recognized."
  444.     (let ((group nil)
  445.       (new-newsgroups nil))
  446.       (mapatoms
  447.        (function
  448.         (lambda (sym)
  449.       (setq group (symbol-name sym))
  450.       ;; Taking account of `-n' option.
  451.       (and (or (null gnus-newsrc-options-n-no)
  452.            (not (string-match gnus-newsrc-options-n-no group))
  453.            (and gnus-newsrc-options-n-yes
  454.                 (string-match gnus-newsrc-options-n-yes group)))
  455. !          (null (assoc group gnus-killed-assoc)) ;Ignore killed.
  456. !          (null (assoc group gnus-newsrc-assoc)) ;Really new.
  457. !          ;; Find new newsgroup.
  458. !          (setq new-newsgroups
  459. !            (cons group new-newsgroups)))
  460.       ))
  461.        gnus-active-hashtb)
  462.       ;; Return new newsgroups.
  463. --- 5797,5960 ----
  464.                     (list newsgroup t))
  465.                     (car (car gnus-newsrc-assoc)))))
  466.   
  467. ! (defun gnus-group-is-new (group)
  468. !   "Return true if GROUP is not found in gnus-newsrc-assoc or
  469. ! gnus-killed-assoc, either as itself or as a hierarchy which includes
  470. ! GROUP."
  471. !   (catch 'return
  472. !     (let ((group-okay t) ;if nil, need a hierarchy
  473. !       (group-found nil))
  474. !       (while t
  475. !     (setq group-found (or (assoc group gnus-killed-assoc)
  476. !                   (assoc group gnus-newsrc-assoc)))
  477. !     (if (and group-found
  478. !          (or group-okay (eq (nth 2 group-found) 'hierarchy))
  479. !          )
  480. !         (throw 'return nil))
  481. !     (if (not (string-match "\\.[^.]*$" group)) (throw 'return t))
  482. !     (setq group (substring group 0 (match-beginning 0)))
  483. !     (setq group-okay nil)
  484. !     ))))
  485. ! (defun gnus-find-new-newsgroups (&optional prefix)
  486. !   "Look for new newsgroups and return names.
  487. ! `-n' option of options line in .newsrc file is recognized.
  488. ! Return value is a list each element of which is 
  489. !   ('group name) or ('hierarchy name).
  490. ! If PREFIX is specified, it is the name of a hierarchy.  Don't return
  491. ! that hierarchy, instead return an entry for each group or hierarchy
  492. ! underneath that hierarchy.  New groups not under the PREFIX hierarchy
  493. ! are not included in the return value."
  494. !   (if (null prefix)
  495. !       (setq prefix ""))
  496.     (let ((group nil)
  497. +     (group-length 0)
  498.       (new-newsgroups nil))
  499.       (mapatoms
  500.        (function
  501.         (lambda (sym)
  502.       (setq group (symbol-name sym))
  503. +     (setq group-length (length group))
  504.       ;; Taking account of `-n' option.
  505.       (and (or (null gnus-newsrc-options-n-no)
  506.            (not (string-match gnus-newsrc-options-n-no group))
  507.            (and gnus-newsrc-options-n-yes
  508.                 (string-match gnus-newsrc-options-n-yes group)))
  509. !          (>= group-length (length prefix))
  510. !          (string= (substring group 0 (length prefix)) prefix)
  511. !          (gnus-group-is-new group)
  512. !          ;; OK, we got a group which is new.
  513. !          ;; Find what newsgroup or hierarchy we want to add.
  514. !          (let ((new-entry
  515. !             (if (not gnus-show-hierarchies)
  516. !             (list 'group group)
  517. !               ;; Find the most general hierarchy which applies
  518. !               (let ((newsrc gnus-newsrc-assoc)
  519. !                 (spare-assoc gnus-killed-assoc)
  520. !                 (longest-match prefix)
  521. !                 (longest-match-length 0)
  522. !                 (test-string nil)
  523. !                 (test-string-length 0))
  524. !             (setq longest-match-length (length longest-match))
  525. !             (while newsrc
  526. !               (setq test-string (car (car newsrc)))
  527. !               (setq test-string-length (length test-string))
  528. !               (while (> test-string-length longest-match-length)
  529. !                 ;; Would the following be faster? Does it matter?
  530. !                 ;; (and (< test-string-length group-length)
  531. !                 ;;      (string= 
  532. !                 ;;       test-string
  533. !                 ;;       (substring group test-string-length)))
  534. !                 (if (string-match
  535. !                  (concat "^" (regexp-quote test-string))
  536. !                  group)
  537. !                 (progn
  538. !                   (setq longest-match test-string)
  539. !                   (setq longest-match-length
  540. !                     test-string-length))
  541. !                   (if (not (string-match "\\.[^.]*$" test-string))
  542. !                   ;; OK, we've tested everything
  543. !                   (setq test-string-length 0)
  544. !                 (progn
  545. !                   (setq test-string-length (match-beginning 0))
  546. !                   (setq test-string
  547. !                     (substring
  548. !                      test-string 0 test-string-length))
  549. !                   )
  550. !                   )))
  551. !               (setq newsrc (cdr newsrc))
  552. !               (if (null newsrc)
  553. !                   (progn
  554. !                 (setq newsrc spare-assoc)
  555. !                 (setq spare-assoc nil)))
  556. !               )
  557. !             ;; OK, take one more level beyond longest-match.
  558. !             ;; skip the period
  559. !             (if (< longest-match-length group-length)
  560. !                 (setq longest-match-length
  561. !                   (1+ longest-match-length))
  562. !               )
  563. !             (while (and
  564. !                 (< longest-match-length group-length)
  565. !                 (not (string-equal
  566. !                       (substring
  567. !                        group
  568. !                        longest-match-length
  569. !                        (1+ longest-match-length))
  570. !                       ".")))
  571. !               (setq longest-match-length
  572. !                 (1+ longest-match-length)))
  573. !             (if (= longest-match-length group-length)
  574. !                 (list 'group group)
  575. !               (list 'hierarchy
  576. !                 (substring group 0 longest-match-length))
  577. !               )))
  578. !             ))
  579. !            (catch 'got-it
  580. !          (let ((tail new-newsgroups)
  581. !                (group-name (car (cdr new-entry))))
  582. !            (while tail
  583. !              (if (string= (car (cdr (car tail))) group-name)
  584. !              (if (and (eq (car (car tail)) 'group)
  585. !                   (eq (car new-entry) 'hierarchy))
  586. !                  ;; If we are trying to add, for
  587. !                  ;; example, ('hierarchy "misc.consumers")
  588. !                  ;; and ('group "misc.consumers") is
  589. !                  ;; already in there, nuke the 'group
  590. !                  ;; entry and put in the hierarchy.  This
  591. !                  ;; is because logically speaking the
  592. !                  ;; group "misc.consumers" is part of the
  593. !                  ;; "misc.consumers" hierarchy, yet we
  594. !                  ;; don't know there is such a hierarchy
  595. !                  ;; until we find the group
  596. !                  ;; "misc.consumers.house".
  597. !                  (progn
  598. !                    (setcar (car tail) 'hierarchy)
  599. !                    (throw 'got-it nil)
  600. !                    )
  601. !                ;; They are both the same hierarchy, which we see
  602. !                ;; twice, for example, if newgroups are sent out
  603. !                ;; for comp.sys.mac.lawsuits and
  604. !                ;; comp.sys.mac.user-interface
  605. !                ;; at the same time.  Since it's already there,
  606. !                ;; don't need to do anything else
  607. !                ;; The one from new-entry being a hierarchy
  608. !                ;; and the other a group we already checked for.
  609. !                ;; The one from new-entry being a group and
  610. !                ;; the other a hierarchy, or both being groups,
  611. !                ;; "can't happen".
  612. !                (throw 'got-it nil)
  613. !                )
  614. !                (setq tail (cdr tail))
  615. !                ))
  616. !            ;; It is genuinely new.  Add it.
  617. !            (setq new-newsgroups
  618. !              (cons new-entry
  619. !                    new-newsgroups))
  620. !            ))
  621. !          )
  622. !          )
  623.       ))
  624.        gnus-active-hashtb)
  625.       ;; Return new newsgroups.
  626. ***************
  627. *** 5772,5779 ****
  628.         (error "Invalid argument: %s" info))
  629.     (let* ((group (car info))        ;Newsgroup name.
  630.        (range
  631. !       (gnus-difference-of-range
  632. !        (nth 2 (gnus-gethash group gnus-active-hashtb)) (nthcdr 2 info))))
  633.       ;; Check duplication.
  634.       (if (assoc group gnus-newsrc-assoc)
  635.       (error "Duplicated: %s" group))
  636. --- 5988,5997 ----
  637.         (error "Invalid argument: %s" info))
  638.     (let* ((group (car info))        ;Newsgroup name.
  639.        (range
  640. !       (or (eq 'hierarchy (nth 2 info))
  641. !           (gnus-difference-of-range
  642. !            (nth 2 (gnus-gethash group gnus-active-hashtb))
  643. !            (nthcdr 2 info)))))
  644.       ;; Check duplication.
  645.       (if (assoc group gnus-newsrc-assoc)
  646.       (error "Duplicated: %s" group))
  647. ***************
  648. *** 5802,5812 ****
  649.       ;; Then insert to .newsrc.
  650.       (gnus-update-newsrc-buffer group nil next)
  651.       ;; Add to gnus-unread-hashtb.
  652. !     (gnus-sethash group
  653. !           (cons group        ;Newsgroup name.
  654. !             (cons (gnus-number-of-articles range) range))
  655. !           gnus-unread-hashtb)
  656. !     ))
  657.   
  658.   (defun gnus-check-killed-newsgroups ()
  659.     "Check consistency between gnus-newsrc-assoc and gnus-killed-assoc."
  660. --- 6020,6032 ----
  661.       ;; Then insert to .newsrc.
  662.       (gnus-update-newsrc-buffer group nil next)
  663.       ;; Add to gnus-unread-hashtb.
  664. !     (or (eq 'hierarchy (nth 2 info))
  665. !     (gnus-sethash group
  666. !               (cons group        ;Newsgroup name.
  667. !                 (cons (gnus-number-of-articles range) range))
  668. !               gnus-unread-hashtb)
  669. !     ))
  670. !     )
  671.   
  672.   (defun gnus-check-killed-newsgroups ()
  673.     "Check consistency between gnus-newsrc-assoc and gnus-killed-assoc."
  674. ***************
  675. *** 5893,5914 ****
  676.       (while read
  677.         (setq group-info (car read))    ;About one newsgroup
  678.         (setq group-name (car group-info))
  679. !       (setq active (nth 2 (gnus-gethash group-name gnus-active-hashtb)))
  680. !       (if (and gnus-octive-hashtb
  681. !            ;; Is nothing changed?
  682. !            (equal active
  683. !               (nth 2 (gnus-gethash group-name gnus-octive-hashtb)))
  684. !            ;; Is this newsgroup in the unread hash table?
  685. !            (gnus-gethash group-name gnus-unread-hashtb)
  686. !            )
  687. !       nil                ;Nothing to do.
  688. !     (setq range (gnus-difference-of-range active (nthcdr 2 group-info)))
  689. !     (gnus-sethash group-name
  690. !               (cons group-name    ;Group name
  691. !                 (cons (gnus-number-of-articles range)
  692. !                   range)) ;Range of unread articles
  693. !               gnus-unread-hashtb)
  694. !     )
  695.         (setq read (cdr read))
  696.         )
  697.       (message "Checking new news... done")
  698. --- 6113,6140 ----
  699.       (while read
  700.         (setq group-info (car read))    ;About one newsgroup
  701.         (setq group-name (car group-info))
  702. !       (if (not (eq (nth 2 group-info) 'hierarchy))
  703. !       (progn
  704. !         (setq active
  705. !           (nth 2 (gnus-gethash group-name gnus-active-hashtb)))
  706. !         (if (and gnus-octive-hashtb
  707. !              ;; Is nothing changed?
  708. !              (equal active
  709. !                 (nth 2
  710. !                  (gnus-gethash group-name gnus-octive-hashtb)))
  711. !              ;; Is this newsgroup in the unread hash table?
  712. !              (gnus-gethash group-name gnus-unread-hashtb)
  713. !              )
  714. !         nil                ;Nothing to do.
  715. !           (setq range
  716. !             (gnus-difference-of-range active (nthcdr 2 group-info)))
  717. !           (gnus-sethash group-name
  718. !                 (cons group-name    ;Group name
  719. !                   (cons (gnus-number-of-articles range)
  720. !                     range)) ;Range of unread articles
  721. !                 gnus-unread-hashtb)
  722. !           )
  723. !         ))
  724.         (setq read (cdr read))
  725.         )
  726.       (message "Checking new news... done")
  727. ***************
  728. *** 6211,6217 ****
  729.              ":" (buffer-substring (match-beginning 2) (match-end 2))))
  730.       (setq ranges (buffer-substring (match-beginning 3) (match-end 3)))
  731.       (setq read-list nil)
  732. !     (while (string-match "^[, \t]*\\([0-9-]+\\)" ranges)
  733.         (setq subrange (substring ranges (match-beginning 1) (match-end 1)))
  734.         (setq ranges (substring ranges (match-end 1)))
  735.         (cond ((string-match "^\\([0-9]+\\)-\\([0-9]+\\)$" subrange)
  736. --- 6437,6443 ----
  737.              ":" (buffer-substring (match-beginning 2) (match-end 2))))
  738.       (setq ranges (buffer-substring (match-beginning 3) (match-end 3)))
  739.       (setq read-list nil)
  740. !     (while (string-match "^[, \t]*\\([0-9-h]+\\)" ranges)
  741.         (setq subrange (substring ranges (match-beginning 1) (match-end 1)))
  742.         (setq ranges (substring ranges (match-end 1)))
  743.         (cond ((string-match "^\\([0-9]+\\)-\\([0-9]+\\)$" subrange)
  744. ***************
  745. *** 6229,6234 ****
  746. --- 6455,6462 ----
  747.                  (cons (cons (string-to-int subrange)
  748.                      (string-to-int subrange))
  749.                    read-list)))
  750. +         ((string-match "^h$" subrange)
  751. +          (setq read-list (cons 'hierarchy nil)))
  752.           (t
  753.            (ding) (message "Ignoring bogus lines of %s" newsgroup)
  754.            (sit-for 0))
  755. ***************
  756. *** 6404,6409 ****
  757. --- 6632,6639 ----
  758.   
  759.   (defun gnus-ranges-to-newsrc-format (ranges)
  760.     "Insert ranges of read articles."
  761. +   (if (eq (car ranges) 'hierarchy)
  762. +       (insert "h")
  763.     (let ((range nil))            ;Range is a pair of BEGIN and END.
  764.       (while ranges
  765.         (setq range (car ranges))
  766. ***************
  767. *** 6421,6426 ****
  768. --- 6651,6657 ----
  769.            (if ranges (insert ","))
  770.            ))
  771.         )))
  772. +   )
  773.   
  774.   (defun gnus-compress-sequence (numbers)
  775.     "Convert list of sorted numbers to ranges."
  776.