| 1 | ;;; gnus-edit.el --- Gnus SCORE file editing |
| 2 | ;; Copyright (C) 1995,96 Free Software Foundation, Inc. |
| 3 | ;; |
| 4 | ;; Author: Per Abrahamsen <abraham@iesd.auc.dk> |
| 5 | ;; Keywords: news, help |
| 6 | ;; Version: 0.2 |
| 7 | |
| 8 | ;;; Commentary: |
| 9 | ;; |
| 10 | ;; Type `M-x gnus-score-customize RET' to invoke. |
| 11 | |
| 12 | ;;; Code: |
| 13 | |
| 14 | (require 'custom) |
| 15 | (require 'gnus-score) |
| 16 | (eval-when-compile (require 'cl)) |
| 17 | |
| 18 | (defconst gnus-score-custom-data |
| 19 | '((tag . "Score") |
| 20 | (doc . "Customization of Gnus SCORE files. |
| 21 | |
| 22 | SCORE files allow you to assign a score to each article when you enter |
| 23 | a group, and automatically mark the articles as read or delete them |
| 24 | based on the score. In the summary buffer you can use the score to |
| 25 | sort the articles by score (`C-c C-s C-s') or to jump to the unread |
| 26 | article with the highest score (`,').") |
| 27 | (type . group) |
| 28 | (data "\n" |
| 29 | ((header . nil) |
| 30 | (doc . "Name of SCORE file to customize. |
| 31 | |
| 32 | Enter the name in the `File' field, then push the [Load] button to |
| 33 | load it. When done editing, push the [Save] button to save the file. |
| 34 | |
| 35 | Several score files may apply to each group, and several groups may |
| 36 | use the same score file. This is controlled implicitly by the name of |
| 37 | the score file and the value of the global variable |
| 38 | `gnus-score-find-score-files-function', and explicitly by the the |
| 39 | `Files' and `Exclude Files' entries.") |
| 40 | (compact . t) |
| 41 | (type . group) |
| 42 | (data ((tag . "Load") |
| 43 | (type . button) |
| 44 | (query . gnus-score-custom-load)) |
| 45 | ((tag . "Save") |
| 46 | (type . button) |
| 47 | (query . gnus-score-custom-save)) |
| 48 | ((name . file) |
| 49 | (tag . "File") |
| 50 | (directory . gnus-kill-files-directory) |
| 51 | (default-file . "SCORE") |
| 52 | (type . file)))) |
| 53 | ((name . files) |
| 54 | (tag . "Files") |
| 55 | (doc . "\ |
| 56 | List of score files to load when the the current score file is loaded. |
| 57 | You can use this to share score entries between multiple score files. |
| 58 | |
| 59 | Push the `[INS]' button add a score file to the list, or `[DEL]' to |
| 60 | delete a score file from the list.") |
| 61 | (type . list) |
| 62 | (data ((type . repeat) |
| 63 | (header . nil) |
| 64 | (data (type . file) |
| 65 | (directory . gnus-kill-files-directory))))) |
| 66 | ((name . exclude-files) |
| 67 | (tag . "Exclude Files") |
| 68 | (doc . "\ |
| 69 | List of score files to exclude when the the current score file is loaded. |
| 70 | You can use this if you have a score file you want to share between a |
| 71 | number of newsgroups, except for the newsgroup this score file |
| 72 | matches. [ Did anyone get that? ] |
| 73 | |
| 74 | Push the `[INS]' button add a score file to the list, or `[DEL]' to |
| 75 | delete a score file from the list.") |
| 76 | (type . list) |
| 77 | (data ((type . repeat) |
| 78 | (header . nil) |
| 79 | (data (type . file) |
| 80 | (directory . gnus-kill-files-directory))))) |
| 81 | ((name . mark) |
| 82 | (tag . "Mark") |
| 83 | (doc . "\ |
| 84 | Articles below this score will be automatically marked as read. |
| 85 | |
| 86 | This means that when you enter the summary buffer, the articles will |
| 87 | be shown but will already be marked as read. You can then press `x' |
| 88 | to get rid of them entirely. |
| 89 | |
| 90 | By default articles with a negative score will be marked as read. To |
| 91 | change this, push the `Mark' button, and choose `Integer'. You can |
| 92 | then enter a value in the `Mark' field.") |
| 93 | (type . gnus-score-custom-maybe-type)) |
| 94 | ((name . expunge) |
| 95 | (tag . "Expunge") |
| 96 | (doc . "\ |
| 97 | Articles below this score will not be shown in the summary buffer.") |
| 98 | (type . gnus-score-custom-maybe-type)) |
| 99 | ((name . mark-and-expunge) |
| 100 | (tag . "Mark and Expunge") |
| 101 | (doc . "\ |
| 102 | Articles below this score will be marked as read, but not shown. |
| 103 | |
| 104 | Someone should explain me the difference between this and `expunge' |
| 105 | alone or combined with `mark'.") |
| 106 | (type . gnus-score-custom-maybe-type)) |
| 107 | ((name . eval) |
| 108 | (tag . "Eval") |
| 109 | (doc . "\ |
| 110 | Evaluate this lisp expression when the entering summary buffer.") |
| 111 | (type . sexp)) |
| 112 | ((name . read-only) |
| 113 | (tag . "Read Only") |
| 114 | (doc . "Read-only score files will not be updated or saved. |
| 115 | Except from this buffer, of course!") |
| 116 | (type . toggle)) |
| 117 | ((type . doc) |
| 118 | (doc . "\ |
| 119 | Each news header has an associated list of score entries. |
| 120 | You can use the [INS] buttons to add new score entries anywhere in the |
| 121 | list, or the [DEL] buttons to delete specific score entries. |
| 122 | |
| 123 | Each score entry should specify a string that should be matched with |
| 124 | the content actual header in order to determine whether the entry |
| 125 | applies to that header. Enter that string in the `Match' field. |
| 126 | |
| 127 | If the score entry matches, the articles score will be adjusted with |
| 128 | some amount. Enter that amount in the in the `Score' field. You |
| 129 | should specify a positive amount for score entries that matches |
| 130 | articles you find interesting, and a negative amount for score entries |
| 131 | matching articles you would rather avoid. The final score for the |
| 132 | article will be the sum of the score of all score entries that match |
| 133 | the article. |
| 134 | |
| 135 | The score entry can be either permanent or expirable. To make the |
| 136 | entry permanent, push the `Date' button and choose the `Permanent' |
| 137 | entry. To make the entry expirable, choose instead the `Integer' |
| 138 | entry. After choosing the you can enter the date the score entry was |
| 139 | last matched in the `Date' field. The date will be automatically |
| 140 | updated each time the score entry matches an article. When the date |
| 141 | become too old, the the score entry will be removed. |
| 142 | |
| 143 | For your convenience, the date is specified as the number of days |
| 144 | elapsed since the (imaginary) Gregorian date Sunday, December 31, 1 |
| 145 | BC. |
| 146 | |
| 147 | Finally, you can choose what kind of match you want to perform by |
| 148 | pushing the `Type' button. For most entries you can choose between |
| 149 | `Exact' which mean the header content must be exactly identical to the |
| 150 | match string, or `Substring' meaning the match string should be |
| 151 | somewhere in the header content, or even `Regexp' to use Emacs regular |
| 152 | expression matching. The last choice is `Fuzzy' which is like `Exact' |
| 153 | except that whitespace derivations, a beginning `Re:' or a terminating |
| 154 | parenthetical remark are all ignored. Each of the four types have a |
| 155 | variant which will ignore case in the comparison. That variant is |
| 156 | indicated with a `(fold)' after its name.")) |
| 157 | ((name . from) |
| 158 | (tag . "From") |
| 159 | (doc . "Scoring based on the authors email address.") |
| 160 | (type . gnus-score-custom-string-type)) |
| 161 | ((name . subject) |
| 162 | (tag . "Subject") |
| 163 | (doc . "Scoring based on the articles subject.") |
| 164 | (type . gnus-score-custom-string-type)) |
| 165 | ((name . followup) |
| 166 | (tag . "Followup") |
| 167 | (doc . "Scoring based on who the article is a followup to. |
| 168 | |
| 169 | If you want to see all followups to your own articles, add an entry |
| 170 | with a positive score matching your email address here. You can also |
| 171 | put an entry with a negative score matching someone who is so annoying |
| 172 | that you don't even want to see him quoted in followups.") |
| 173 | (type . gnus-score-custom-string-type)) |
| 174 | ((name . xref) |
| 175 | (tag . "Xref") |
| 176 | (doc . "Scoring based on article crossposting. |
| 177 | |
| 178 | If you want to score based on which newsgroups an article is posted |
| 179 | to, this is the header to use. The syntax is a little different from |
| 180 | the `Newsgroups' header, but scoring in `Xref' is much faster. As an |
| 181 | example, to match all crossposted articles match on `:.*:' using the |
| 182 | `Regexp' type.") |
| 183 | (type . gnus-score-custom-string-type)) |
| 184 | ((name . references) |
| 185 | (tag . "References") |
| 186 | (doc . "Scoring based on article references. |
| 187 | |
| 188 | The `References' header gives you an alternative way to score on |
| 189 | followups. If you for example want to see follow all discussions |
| 190 | where people from `iesd.auc.dk' school participate, you can add a |
| 191 | substring match on `iesd.auc.dk>' on this header.") |
| 192 | (type . gnus-score-custom-string-type)) |
| 193 | ((name . message-id) |
| 194 | (tag . "Message-ID") |
| 195 | (doc . "Scoring based on the articles message-id. |
| 196 | |
| 197 | This isn't very useful, but Lars like completeness. You can use it to |
| 198 | match all messaged generated by recent Gnus version with a `Substring' |
| 199 | match on `.fsf@'.") |
| 200 | (type . gnus-score-custom-string-type)) |
| 201 | ((type . doc) |
| 202 | (doc . "\ |
| 203 | WARNING: Scoring on the following three pseudo headers is very slow! |
| 204 | Scoring on any of the real headers use a technique that avoids |
| 205 | scanning the entire article, only the actual headers you score on are |
| 206 | scanned, and this scanning has been heavily optimized. Using just a |
| 207 | single entry for one the three pseudo-headers `Head', `Body', and |
| 208 | `All' will require GNUS to retrieve and scan the entire article, which |
| 209 | can be very slow on large groups. However, if you add one entry for |
| 210 | any of these headers, you can just as well add several. Each |
| 211 | subsequent entry cost relatively little extra time.")) |
| 212 | ((name . head) |
| 213 | (tag . "Head") |
| 214 | (doc . "Scoring based on the article header. |
| 215 | |
| 216 | Instead of matching the content of a single header, the entire header |
| 217 | section of the article is matched. You can use this to match on |
| 218 | arbitrary headers, foe example to single out TIN lusers, use a substring |
| 219 | match on `Newsreader: TIN'. That should get 'em!") |
| 220 | (type . gnus-score-custom-string-type)) |
| 221 | ((name . body) |
| 222 | (tag . "Body") |
| 223 | (doc . "Scoring based on the article body. |
| 224 | |
| 225 | If you think any article that mentions `Kibo' is inherently |
| 226 | interesting, do a substring match on His name. You Are Allowed.") |
| 227 | (type . gnus-score-custom-string-type)) |
| 228 | ((name . all) |
| 229 | (tag . "All") |
| 230 | (doc . "Scoring based on the whole article.") |
| 231 | (type . gnus-score-custom-string-type)) |
| 232 | ((name . date) |
| 233 | (tag . "Date") |
| 234 | (doc . "Scoring based on article date. |
| 235 | |
| 236 | You can change the score of articles that have been posted before, |
| 237 | after, or at a specific date. You should add the date in the `Match' |
| 238 | field, and then select `before', `after', or `at' by pushing the |
| 239 | `Type' button. Imagine you want to lower the score of very old |
| 240 | articles, or want to raise the score of articles from the future (such |
| 241 | things happen!). Then you can't use date scoring for that. In fact, |
| 242 | I can't imagine anything you would want to use this for. |
| 243 | |
| 244 | For your convenience, the date is specified in Usenet date format.") |
| 245 | (type . gnus-score-custom-date-type)) |
| 246 | ((type . doc) |
| 247 | (doc . "\ |
| 248 | The Lines and Chars headers use integer based scoring. |
| 249 | |
| 250 | This means that you should write an integer in the `Match' field, and |
| 251 | the push the `Type' field to if the `Chars' or `Lines' header should |
| 252 | be larger, equal, or smaller than the number you wrote in the match |
| 253 | field.")) |
| 254 | ((name . chars) |
| 255 | (tag . "Characters") |
| 256 | (doc . "Scoring based on the number of characters in the article.") |
| 257 | (type . gnus-score-custom-integer-type)) |
| 258 | ((name . lines) |
| 259 | (tag . "Lines") |
| 260 | (doc . "Scoring based on the number of lines in the article.") |
| 261 | (type . gnus-score-custom-integer-type)) |
| 262 | ((name . orphan) |
| 263 | (tag . "Orphan") |
| 264 | (doc . "Score to add to articles with no parents.") |
| 265 | (type . gnus-score-custom-maybe-type)) |
| 266 | ((name . adapt) |
| 267 | (tag . "Adapt") |
| 268 | (doc . "Adapting the score files to your newsreading habits. |
| 269 | |
| 270 | When you have finished reading a group GNUS can automatically create |
| 271 | new score entries based on which articles you read and which you |
| 272 | skipped. This is normally controlled by the two global variables |
| 273 | `gnus-use-adaptive-scoring' and `gnus-default-adaptive-score-alist', |
| 274 | The first determines whether adaptive scoring should be enabled or |
| 275 | not, while the second determines what score entries should be created. |
| 276 | |
| 277 | You can overwrite the setting of `gnus-use-adaptive-scoring' by |
| 278 | selecting `Enable' or `Disable' by pressing the `Adapt' button. |
| 279 | Selecting `Custom' will allow you to specify the exact adaptation |
| 280 | rules (overwriting `gnus-default-adaptive-score-alist').") |
| 281 | (type . choice) |
| 282 | (data ((tag . "Default") |
| 283 | (default . nil) |
| 284 | (type . const)) |
| 285 | ((tag . "Enable") |
| 286 | (default . t) |
| 287 | (type . const)) |
| 288 | ((tag . "Disable") |
| 289 | (default . ignore) |
| 290 | (type . const)) |
| 291 | ((tag . "Custom") |
| 292 | (doc . "Customization of adaptive scoring. |
| 293 | |
| 294 | Each time you read an article it will be marked as read. Likewise, if |
| 295 | you delete it it will be marked as deleted, and if you tick it it will |
| 296 | be marked as ticked. When you leave a group, GNUS can automatically |
| 297 | create score file entries based on these marks, so next time you enter |
| 298 | the group articles with subjects that you read last time have higher |
| 299 | score and articles with subjects that deleted will have lower score. |
| 300 | |
| 301 | Below is a list of such marks. You can insert new marks to the list |
| 302 | by pushing on one of the `[INS]' buttons in the left margin to create |
| 303 | a new entry and then pushing the `Mark' button to select the mark. |
| 304 | For each mark there is another list, this time of article headers, |
| 305 | which determine how the mark should affect that header. The `[INS]' |
| 306 | buttons of this list are indented to indicate that the belong to the |
| 307 | mark above. Push the `Header' button to choose a header, and then |
| 308 | enter a score value in the `Score' field. |
| 309 | |
| 310 | For each article that are marked with `Mark' when you leave the |
| 311 | group, a temporary score entry for the articles `Header' with the |
| 312 | value of `Score' will be added the adapt file. If the score entry |
| 313 | already exists, `Score' will be added to its value. If you understood |
| 314 | that, you are smart. |
| 315 | |
| 316 | You can select the special value `Other' when pressing the `Mark' or |
| 317 | `Header' buttons. This is because Lars might add more useful values |
| 318 | there. If he does, it is up to you to figure out what they are named.") |
| 319 | (type . list) |
| 320 | (default . ((__uninitialized__))) |
| 321 | (data ((type . repeat) |
| 322 | (header . nil) |
| 323 | (data . ((type . list) |
| 324 | (header . nil) |
| 325 | (compact . t) |
| 326 | (data ((type . choice) |
| 327 | (tag . "Mark") |
| 328 | (data ((tag . "Unread") |
| 329 | (default . gnus-unread-mark) |
| 330 | (type . const)) |
| 331 | ((tag . "Ticked") |
| 332 | (default . gnus-ticked-mark) |
| 333 | (type . const)) |
| 334 | ((tag . "Dormant") |
| 335 | (default . gnus-dormant-mark) |
| 336 | (type . const)) |
| 337 | ((tag . "Deleted") |
| 338 | (default . gnus-del-mark) |
| 339 | (type . const)) |
| 340 | ((tag . "Read") |
| 341 | (default . gnus-read-mark) |
| 342 | (type . const)) |
| 343 | ((tag . "Expirable") |
| 344 | (default . gnus-expirable-mark) |
| 345 | (type . const)) |
| 346 | ((tag . "Killed") |
| 347 | (default . gnus-killed-mark) |
| 348 | (type . const)) |
| 349 | ((tag . "Kill-file") |
| 350 | (default . gnus-kill-file-mark) |
| 351 | (type . const)) |
| 352 | ((tag . "Low-score") |
| 353 | (default . gnus-low-score-mark) |
| 354 | (type . const)) |
| 355 | ((tag . "Catchup") |
| 356 | (default . gnus-catchup-mark) |
| 357 | (type . const)) |
| 358 | ((tag . "Ancient") |
| 359 | (default . gnus-ancient-mark) |
| 360 | (type . const)) |
| 361 | ((tag . "Canceled") |
| 362 | (default . gnus-canceled-mark) |
| 363 | (type . const)) |
| 364 | ((prompt . "Other") |
| 365 | (default . ??) |
| 366 | (type . sexp)))) |
| 367 | ((type . repeat) |
| 368 | (prefix . " ") |
| 369 | (data . ((type . list) |
| 370 | (compact . t) |
| 371 | (data ((tag . "Header") |
| 372 | (type . choice) |
| 373 | (data ((tag . "Subject") |
| 374 | (default . subject) |
| 375 | (type . const)) |
| 376 | ((prompt . "From") |
| 377 | (tag . "From ") |
| 378 | (default . from) |
| 379 | (type . const)) |
| 380 | ((prompt . "Other") |
| 381 | (width . 7) |
| 382 | (default . nil) |
| 383 | (type . symbol)))) |
| 384 | ((tag . "Score") |
| 385 | (type . integer)))))))))))))) |
| 386 | ((name . local) |
| 387 | (tag . "Local") |
| 388 | (doc . "\ |
| 389 | List of local variables to set when this score file is loaded. |
| 390 | |
| 391 | Using this entry can provide a convenient way to set variables that |
| 392 | will affect the summary mode for only some specific groups, i.e. those |
| 393 | groups matched by the current score file.") |
| 394 | (type . list) |
| 395 | (data ((type . repeat) |
| 396 | (header . nil) |
| 397 | (data . ((type . list) |
| 398 | (compact . t) |
| 399 | (data ((tag . "Name") |
| 400 | (width . 26) |
| 401 | (type . symbol)) |
| 402 | ((tag . "Value") |
| 403 | (width . 26) |
| 404 | (type . sexp))))))))))) |
| 405 | |
| 406 | (defconst gnus-score-custom-type-properties |
| 407 | '((gnus-score-custom-maybe-type |
| 408 | (type . choice) |
| 409 | (data ((type . integer) |
| 410 | (default . 0)) |
| 411 | ((tag . "Default") |
| 412 | (type . const) |
| 413 | (default . nil)))) |
| 414 | (gnus-score-custom-string-type |
| 415 | (type . list) |
| 416 | (data ((type . repeat) |
| 417 | (header . nil) |
| 418 | (data . ((type . list) |
| 419 | (compact . t) |
| 420 | (data ((tag . "Match") |
| 421 | (width . 59) |
| 422 | (type . string)) |
| 423 | "\n " |
| 424 | ((tag . "Score") |
| 425 | (type . integer)) |
| 426 | ((tag . "Date") |
| 427 | (type . choice) |
| 428 | (data ((type . integer) |
| 429 | (default . 0) |
| 430 | (width . 9)) |
| 431 | ((tag . "Permanent") |
| 432 | (type . const) |
| 433 | (default . nil)))) |
| 434 | ((tag . "Type") |
| 435 | (type . choice) |
| 436 | (data ((tag . "Exact") |
| 437 | (default . E) |
| 438 | (type . const)) |
| 439 | ((tag . "Substring") |
| 440 | (default . S) |
| 441 | (type . const)) |
| 442 | ((tag . "Regexp") |
| 443 | (default . R) |
| 444 | (type . const)) |
| 445 | ((tag . "Fuzzy") |
| 446 | (default . F) |
| 447 | (type . const)) |
| 448 | ((tag . "Exact (fold)") |
| 449 | (default . e) |
| 450 | (type . const)) |
| 451 | ((tag . "Substring (fold)") |
| 452 | (default . s) |
| 453 | (type . const)) |
| 454 | ((tag . "Regexp (fold)") |
| 455 | (default . r) |
| 456 | (type . const)) |
| 457 | ((tag . "Fuzzy (fold)") |
| 458 | (default . f) |
| 459 | (type . const)))))))))) |
| 460 | (gnus-score-custom-integer-type |
| 461 | (type . list) |
| 462 | (data ((type . repeat) |
| 463 | (header . nil) |
| 464 | (data . ((type . list) |
| 465 | (compact . t) |
| 466 | (data ((tag . "Match") |
| 467 | (type . integer)) |
| 468 | ((tag . "Score") |
| 469 | (type . integer)) |
| 470 | ((tag . "Date") |
| 471 | (type . choice) |
| 472 | (data ((type . integer) |
| 473 | (default . 0) |
| 474 | (width . 9)) |
| 475 | ((tag . "Permanent") |
| 476 | (type . const) |
| 477 | (default . nil)))) |
| 478 | ((tag . "Type") |
| 479 | (type . choice) |
| 480 | (data ((tag . "<") |
| 481 | (default . <) |
| 482 | (type . const)) |
| 483 | ((tag . ">") |
| 484 | (default . >) |
| 485 | (type . const)) |
| 486 | ((tag . "=") |
| 487 | (default . =) |
| 488 | (type . const)) |
| 489 | ((tag . ">=") |
| 490 | (default . >=) |
| 491 | (type . const)) |
| 492 | ((tag . "<=") |
| 493 | (default . <=) |
| 494 | (type . const)))))))))) |
| 495 | (gnus-score-custom-date-type |
| 496 | (type . list) |
| 497 | (data ((type . repeat) |
| 498 | (header . nil) |
| 499 | (data . ((type . list) |
| 500 | (compact . t) |
| 501 | (data ((tag . "Match") |
| 502 | (width . 59) |
| 503 | (type . string)) |
| 504 | "\n " |
| 505 | ((tag . "Score") |
| 506 | (type . integer)) |
| 507 | ((tag . "Date") |
| 508 | (type . choice) |
| 509 | (data ((type . integer) |
| 510 | (default . 0) |
| 511 | (width . 9)) |
| 512 | ((tag . "Permanent") |
| 513 | (type . const) |
| 514 | (default . nil)))) |
| 515 | ((tag . "Type") |
| 516 | (type . choice) |
| 517 | (data ((tag . "Before") |
| 518 | (default . before) |
| 519 | (type . const)) |
| 520 | ((tag . "After") |
| 521 | (default . after) |
| 522 | (type . const)) |
| 523 | ((tag . "At") |
| 524 | (default . at) |
| 525 | (type . const)))))))))))) |
| 526 | |
| 527 | (defvar gnus-score-custom-file nil |
| 528 | "Name of SCORE file being customized.") |
| 529 | |
| 530 | (defun gnus-score-customize () |
| 531 | "Create a buffer for editing gnus SCORE files." |
| 532 | (interactive) |
| 533 | (let (gnus-score-alist) |
| 534 | (custom-buffer-create "*Score Edit*" gnus-score-custom-data |
| 535 | gnus-score-custom-type-properties |
| 536 | 'gnus-score-custom-set |
| 537 | 'gnus-score-custom-get |
| 538 | 'gnus-score-custom-save)) |
| 539 | (make-local-variable 'gnus-score-custom-file) |
| 540 | (setq gnus-score-custom-file |
| 541 | (expand-file-name "SCORE" gnus-kill-files-directory)) |
| 542 | (make-local-variable 'gnus-score-alist) |
| 543 | (setq gnus-score-alist nil) |
| 544 | (custom-reset-all)) |
| 545 | |
| 546 | (defun gnus-score-custom-get (name) |
| 547 | (if (eq name 'file) |
| 548 | gnus-score-custom-file |
| 549 | (let ((entry (assoc (symbol-name name) gnus-score-alist))) |
| 550 | (if entry |
| 551 | (mapcar 'gnus-score-custom-sanify (cdr entry)) |
| 552 | (setq entry (assoc name gnus-score-alist)) |
| 553 | (if (or (memq name '(files exclude-files local)) |
| 554 | (and (eq name 'adapt) |
| 555 | (not (symbolp (car (cdr entry)))))) |
| 556 | (cdr entry) |
| 557 | (car (cdr entry))))))) |
| 558 | |
| 559 | (defun gnus-score-custom-set (name value) |
| 560 | (cond ((eq name 'file) |
| 561 | (setq gnus-score-custom-file value)) |
| 562 | ((assoc (symbol-name name) gnus-score-alist) |
| 563 | (if value |
| 564 | (setcdr (assoc (symbol-name name) gnus-score-alist) value) |
| 565 | (setq gnus-score-alist (delq (assoc (symbol-name name) |
| 566 | gnus-score-alist) |
| 567 | gnus-score-alist)))) |
| 568 | ((assoc (symbol-name name) gnus-header-index) |
| 569 | (if value |
| 570 | (setq gnus-score-alist |
| 571 | (cons (cons (symbol-name name) value) gnus-score-alist)))) |
| 572 | ((assoc name gnus-score-alist) |
| 573 | (cond ((null value) |
| 574 | (setq gnus-score-alist (delq (assoc name gnus-score-alist) |
| 575 | gnus-score-alist))) |
| 576 | ((and (listp value) (not (eq name 'eval))) |
| 577 | (setcdr (assoc name gnus-score-alist) value)) |
| 578 | (t |
| 579 | (setcdr (assoc name gnus-score-alist) (list value))))) |
| 580 | ((null value)) |
| 581 | ((and (listp value) (not (eq name 'eval))) |
| 582 | (setq gnus-score-alist (cons (cons name value) gnus-score-alist))) |
| 583 | (t |
| 584 | (setq gnus-score-alist |
| 585 | (cons (cons name (list value)) gnus-score-alist))))) |
| 586 | |
| 587 | (defun gnus-score-custom-sanify (entry) |
| 588 | (list (nth 0 entry) |
| 589 | (or (nth 1 entry) gnus-score-interactive-default-score) |
| 590 | (nth 2 entry) |
| 591 | (cond ((null (nth 3 entry)) |
| 592 | 's) |
| 593 | ((memq (nth 3 entry) '(before after at >= <=)) |
| 594 | (nth 3 entry)) |
| 595 | (t |
| 596 | (intern (substring (symbol-name (nth 3 entry)) 0 1)))))) |
| 597 | |
| 598 | (defvar gnus-score-cache nil) |
| 599 | |
| 600 | (defun gnus-score-custom-load () |
| 601 | (interactive) |
| 602 | (let ((file (custom-name-value 'file))) |
| 603 | (if (eq file custom-nil) |
| 604 | (error "You must specify a file name")) |
| 605 | (setq file (expand-file-name file gnus-kill-files-directory)) |
| 606 | (gnus-score-load file) |
| 607 | (setq gnus-score-custom-file file) |
| 608 | (custom-reset-all) |
| 609 | (gnus-message 4 "Loaded"))) |
| 610 | |
| 611 | (defun gnus-score-custom-save () |
| 612 | (interactive) |
| 613 | (custom-apply-all) |
| 614 | (gnus-score-remove-from-cache gnus-score-custom-file) |
| 615 | (let ((file gnus-score-custom-file) |
| 616 | (score gnus-score-alist) |
| 617 | emacs-lisp-mode-hook) |
| 618 | (save-excursion |
| 619 | (set-buffer (get-buffer-create "*Score*")) |
| 620 | (buffer-disable-undo (current-buffer)) |
| 621 | (erase-buffer) |
| 622 | (pp score (current-buffer)) |
| 623 | (gnus-make-directory (file-name-directory file)) |
| 624 | (write-region (point-min) (point-max) file nil 'silent) |
| 625 | (kill-buffer (current-buffer)))) |
| 626 | (gnus-message 4 "Saved")) |
| 627 | |
| 628 | (provide 'gnus-edit) |
| 629 | |
| 630 | ;;; gnus-edit.el end here |