1;;; gnus-spec.el --- format spec functions for Gnus 2 3;; Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 4;; 2005, 2006, 2007 Free Software Foundation, Inc. 5 6;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org> 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 23;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 24;; Boston, MA 02110-1301, USA. 25 26;;; Commentary: 27 28;;; Code: 29 30(eval-when-compile (require 'cl)) 31(defvar gnus-newsrc-file-version) 32 33(require 'gnus) 34 35(defcustom gnus-use-correct-string-widths (featurep 'xemacs) 36 "*If non-nil, use correct functions for dealing with wide characters." 37 :version "22.1" 38 :group 'gnus-format 39 :type 'boolean) 40 41(defcustom gnus-make-format-preserve-properties (featurep 'xemacs) 42 "*If non-nil, use a replacement `format' function which preserves 43text properties. This is only needed on XEmacs, as FSF Emacs does this anyway." 44 :version "22.1" 45 :group 'gnus-format 46 :type 'boolean) 47 48;;; Internal variables. 49 50(defvar gnus-summary-mark-positions nil) 51(defvar gnus-group-mark-positions nil) 52(defvar gnus-group-indentation "") 53 54;; Format specs. The chunks below are the machine-generated forms 55;; that are to be evaled as the result of the default format strings. 56;; We write them in here to get them byte-compiled. That way the 57;; default actions will be quite fast, while still retaining the full 58;; flexibility of the user-defined format specs. 59 60;; First we have lots of dummy defvars to let the compiler know these 61;; are really dynamic variables. 62 63(defvar gnus-tmp-unread) 64(defvar gnus-tmp-replied) 65(defvar gnus-tmp-score-char) 66(defvar gnus-tmp-indentation) 67(defvar gnus-tmp-opening-bracket) 68(defvar gnus-tmp-lines) 69(defvar gnus-tmp-name) 70(defvar gnus-tmp-closing-bracket) 71(defvar gnus-tmp-subject-or-nil) 72(defvar gnus-tmp-subject) 73(defvar gnus-tmp-marked) 74(defvar gnus-tmp-marked-mark) 75(defvar gnus-tmp-subscribed) 76(defvar gnus-tmp-process-marked) 77(defvar gnus-tmp-number-of-unread) 78(defvar gnus-tmp-group-name) 79(defvar gnus-tmp-group) 80(defvar gnus-tmp-article-number) 81(defvar gnus-tmp-unread-and-unselected) 82(defvar gnus-tmp-news-method) 83(defvar gnus-tmp-news-server) 84(defvar gnus-tmp-article-number) 85(defvar gnus-mouse-face) 86(defvar gnus-mouse-face-prop) 87(defvar gnus-tmp-header) 88(defvar gnus-tmp-from) 89 90(defun gnus-summary-line-format-spec () 91 (insert gnus-tmp-unread gnus-tmp-replied 92 gnus-tmp-score-char gnus-tmp-indentation) 93 (gnus-put-text-property 94 (point) 95 (progn 96 (insert 97 (format "%c%4s: %-23s%c" gnus-tmp-opening-bracket gnus-tmp-lines 98 (let ((val 99 (inline 100 (gnus-summary-from-or-to-or-newsgroups 101 gnus-tmp-header gnus-tmp-from)))) 102 (if (> (length val) 23) 103 (substring val 0 23) 104 val)) 105 gnus-tmp-closing-bracket)) 106 (point)) 107 gnus-mouse-face-prop gnus-mouse-face) 108 (insert " " gnus-tmp-subject-or-nil "\n")) 109 110(defvar gnus-summary-line-format-spec 111 (gnus-byte-code 'gnus-summary-line-format-spec)) 112 113(defun gnus-summary-dummy-line-format-spec () 114 (insert "* ") 115 (gnus-put-text-property 116 (point) 117 (progn 118 (insert ": :") 119 (point)) 120 gnus-mouse-face-prop gnus-mouse-face) 121 (insert " " gnus-tmp-subject "\n")) 122 123(defvar gnus-summary-dummy-line-format-spec 124 (gnus-byte-code 'gnus-summary-dummy-line-format-spec)) 125 126(defun gnus-group-line-format-spec () 127 (insert gnus-tmp-marked-mark gnus-tmp-subscribed 128 gnus-tmp-process-marked 129 gnus-group-indentation 130 (format "%5s: " gnus-tmp-number-of-unread)) 131 (gnus-put-text-property 132 (point) 133 (progn 134 (insert gnus-tmp-group "\n") 135 (1- (point))) 136 gnus-mouse-face-prop gnus-mouse-face)) 137(defvar gnus-group-line-format-spec 138 (gnus-byte-code 'gnus-group-line-format-spec)) 139 140(defvar gnus-format-specs 141 `((version . ,emacs-version) 142 (gnus-version . ,(gnus-continuum-version)) 143 (group "%M\%S\%p\%P\%5y: %(%g%)%l\n" ,gnus-group-line-format-spec) 144 (summary-dummy "* %(: :%) %S\n" 145 ,gnus-summary-dummy-line-format-spec) 146 (summary "%U%R%z%I%(%[%4L: %-23,23f%]%) %s\n" 147 ,gnus-summary-line-format-spec)) 148 "Alist of format specs.") 149 150(defvar gnus-default-format-specs gnus-format-specs) 151 152(defvar gnus-article-mode-line-format-spec nil) 153(defvar gnus-summary-mode-line-format-spec nil) 154(defvar gnus-group-mode-line-format-spec nil) 155 156;;; Phew. All that gruft is over with, fortunately. 157 158;;;###autoload 159(defun gnus-update-format (var) 160 "Update the format specification near point." 161 (interactive 162 (list 163 (save-excursion 164 (eval-defun nil) 165 ;; Find the end of the current word. 166 (re-search-forward "[ \t\n]" nil t) 167 ;; Search backward. 168 (when (re-search-backward "\\(gnus-[-a-z]+-line-format\\)" nil t) 169 (match-string 1))))) 170 (let* ((type (intern (progn (string-match "gnus-\\([-a-z]+\\)-line" var) 171 (match-string 1 var)))) 172 (entry (assq type gnus-format-specs)) 173 value spec) 174 (when entry 175 (setq gnus-format-specs (delq entry gnus-format-specs))) 176 (set 177 (intern (format "%s-spec" var)) 178 (gnus-parse-format (setq value (symbol-value (intern var))) 179 (symbol-value (intern (format "%s-alist" var))) 180 (not (string-match "mode" var)))) 181 (setq spec (symbol-value (intern (format "%s-spec" var)))) 182 (push (list type value spec) gnus-format-specs) 183 184 (pop-to-buffer "*Gnus Format*") 185 (erase-buffer) 186 (lisp-interaction-mode) 187 (insert (gnus-pp-to-string spec)))) 188 189(defun gnus-update-format-specifications (&optional force &rest types) 190 "Update all (necessary) format specifications. 191Return a list of updated types." 192 ;; Make the indentation array. 193 ;; See whether all the stored info needs to be flushed. 194 (when (or force 195 (not gnus-newsrc-file-version) 196 (not (equal (gnus-continuum-version) 197 (gnus-continuum-version gnus-newsrc-file-version))) 198 (not (equal emacs-version 199 (cdr (assq 'version gnus-format-specs))))) 200 (setq gnus-format-specs nil)) 201 ;; Flush the group format spec cache if it doesn't support decoded 202 ;; group names. 203 (when (memq 'group types) 204 (let ((spec (assq 'group gnus-format-specs))) 205 (unless (string-match " gnus-tmp-decoded-group[ )]" 206 (gnus-prin1-to-string (nth 2 spec))) 207 (setq gnus-format-specs (delq spec gnus-format-specs))))) 208 209 ;; Go through all the formats and see whether they need updating. 210 (let (new-format entry type val updated) 211 (while (setq type (pop types)) 212 ;; Jump to the proper buffer to find out the value of the 213 ;; variable, if possible. (It may be buffer-local.) 214 (save-excursion 215 (let ((buffer (intern (format "gnus-%s-buffer" type)))) 216 (when (and (boundp buffer) 217 (setq val (symbol-value buffer)) 218 (gnus-buffer-exists-p val)) 219 (set-buffer val)) 220 (setq new-format (symbol-value 221 (intern (format "gnus-%s-line-format" type))))) 222 (setq entry (cdr (assq type gnus-format-specs))) 223 (if (and (car entry) 224 (equal (car entry) new-format)) 225 ;; Use the old format. 226 (set (intern (format "gnus-%s-line-format-spec" type)) 227 (cadr entry)) 228 ;; This is a new format. 229 (setq val 230 (if (not (stringp new-format)) 231 ;; This is a function call or something. 232 new-format 233 ;; This is a "real" format. 234 (gnus-parse-format 235 new-format 236 (symbol-value 237 (intern (format "gnus-%s-line-format-alist" type))) 238 (not (string-match "mode$" (symbol-name type)))))) 239 ;; Enter the new format spec into the list. 240 (if entry 241 (progn 242 (setcar (cdr entry) val) 243 (setcar entry new-format)) 244 (push (list type new-format val) gnus-format-specs)) 245 (set (intern (format "gnus-%s-line-format-spec" type)) val) 246 (push type updated)))) 247 248 (unless (assq 'version gnus-format-specs) 249 (push (cons 'version emacs-version) gnus-format-specs)) 250 updated)) 251 252(defvar gnus-mouse-face-0 'highlight) 253(defvar gnus-mouse-face-1 'highlight) 254(defvar gnus-mouse-face-2 'highlight) 255(defvar gnus-mouse-face-3 'highlight) 256(defvar gnus-mouse-face-4 'highlight) 257 258(defun gnus-mouse-face-function (form type) 259 `(gnus-put-text-property 260 (point) (progn ,@form (point)) 261 gnus-mouse-face-prop 262 ,(if (equal type 0) 263 'gnus-mouse-face 264 `(quote ,(symbol-value (intern (format "gnus-mouse-face-%d" type))))))) 265 266(defvar gnus-face-0 'bold) 267(defvar gnus-face-1 'italic) 268(defvar gnus-face-2 'bold-italic) 269(defvar gnus-face-3 'bold) 270(defvar gnus-face-4 'bold) 271 272(defun gnus-face-face-function (form type) 273 `(gnus-add-text-properties 274 (point) (progn ,@form (point)) 275 '(gnus-face t face ,(symbol-value (intern (format "gnus-face-%d" type)))))) 276 277(defun gnus-balloon-face-function (form type) 278 `(gnus-put-text-property 279 (point) (progn ,@form (point)) 280 ,(if (fboundp 'balloon-help-mode) 281 ''balloon-help 282 ''help-echo) 283 ,(intern (format "gnus-balloon-face-%d" type)))) 284 285(defun gnus-spec-tab (column) 286 (if (> column 0) 287 `(insert-char ? (max (- ,column (current-column)) 0)) 288 (let ((column (abs column))) 289 `(if (> (current-column) ,column) 290 (let ((end (point))) 291 (if (= (move-to-column ,column) ,column) 292 (delete-region (point) end) 293 (delete-region (1- (point)) end) 294 (insert " "))) 295 (insert-char ? (max (- ,column (current-column)) 0)))))) 296 297(defun gnus-correct-length (string) 298 "Return the correct width of STRING." 299 (let ((length 0)) 300 (mapcar (lambda (char) (incf length (gnus-char-width char))) string) 301 length)) 302 303(defun gnus-correct-substring (string start &optional end) 304 (let ((wstart 0) 305 (wend 0) 306 (wseek 0) 307 (seek 0) 308 (length (length string)) 309 (string (concat string "\0"))) 310 ;; Find the start position. 311 (while (and (< seek length) 312 (< wseek start)) 313 (incf wseek (gnus-char-width (aref string seek))) 314 (incf seek)) 315 (setq wstart seek) 316 ;; Find the end position. 317 (while (and (<= seek length) 318 (or (not end) 319 (<= wseek end))) 320 (incf wseek (gnus-char-width (aref string seek))) 321 (incf seek)) 322 (setq wend seek) 323 (substring string wstart (1- wend)))) 324 325(defun gnus-string-width-function () 326 (cond 327 (gnus-use-correct-string-widths 328 'gnus-correct-length) 329 ((fboundp 'string-width) 330 'string-width) 331 (t 332 'length))) 333 334(defun gnus-substring-function () 335 (cond 336 (gnus-use-correct-string-widths 337 'gnus-correct-substring) 338 ((fboundp 'string-width) 339 'gnus-correct-substring) 340 (t 341 'substring))) 342 343(defun gnus-tilde-max-form (el max-width) 344 "Return a form that limits EL to MAX-WIDTH." 345 (let ((max (abs max-width)) 346 (length-fun (gnus-string-width-function)) 347 (substring-fun (gnus-substring-function))) 348 (if (symbolp el) 349 `(if (> (,length-fun ,el) ,max) 350 ,(if (< max-width 0) 351 `(,substring-fun ,el (- (,length-fun ,el) ,max)) 352 `(,substring-fun ,el 0 ,max)) 353 ,el) 354 `(let ((val (eval ,el))) 355 (if (> (,length-fun val) ,max) 356 ,(if (< max-width 0) 357 `(,substring-fun val (- (,length-fun val) ,max)) 358 `(,substring-fun val 0 ,max)) 359 val))))) 360 361(defun gnus-tilde-cut-form (el cut-width) 362 "Return a form that cuts CUT-WIDTH off of EL." 363 (let ((cut (abs cut-width)) 364 (length-fun (gnus-string-width-function)) 365 (substring-fun (gnus-substring-function))) 366 (if (symbolp el) 367 `(if (> (,length-fun ,el) ,cut) 368 ,(if (< cut-width 0) 369 `(,substring-fun ,el 0 (- (,length-fun ,el) ,cut)) 370 `(,substring-fun ,el ,cut)) 371 ,el) 372 `(let ((val (eval ,el))) 373 (if (> (,length-fun val) ,cut) 374 ,(if (< cut-width 0) 375 `(,substring-fun val 0 (- (,length-fun val) ,cut)) 376 `(,substring-fun val ,cut)) 377 val))))) 378 379(defun gnus-tilde-ignore-form (el ignore-value) 380 "Return a form that is blank when EL is IGNORE-VALUE." 381 (if (symbolp el) 382 `(if (equal ,el ,ignore-value) 383 "" ,el) 384 `(let ((val (eval ,el))) 385 (if (equal val ,ignore-value) 386 "" val)))) 387 388(defun gnus-pad-form (el pad-width) 389 "Return a form that pads EL to PAD-WIDTH accounting for multi-column 390characters correctly. This is because `format' may pad to columns or to 391characters when given a pad value." 392 (let ((pad (abs pad-width)) 393 (side (< 0 pad-width)) 394 (length-fun (gnus-string-width-function))) 395 (if (symbolp el) 396 `(let ((need (- ,pad (,length-fun ,el)))) 397 (if (> need 0) 398 (concat ,(when side '(make-string need ?\ )) 399 ,el 400 ,(when (not side) '(make-string need ?\ ))) 401 ,el)) 402 `(let* ((val (eval ,el)) 403 (need (- ,pad (,length-fun val)))) 404 (if (> need 0) 405 (concat ,(when side '(make-string need ?\ )) 406 val 407 ,(when (not side) '(make-string need ?\ ))) 408 val))))) 409 410(defun gnus-parse-format (format spec-alist &optional insert) 411 ;; This function parses the FORMAT string with the help of the 412 ;; SPEC-ALIST and returns a list that can be eval'ed to return the 413 ;; string. If the FORMAT string contains the specifiers %( and %) 414 ;; the text between them will have the mouse-face text property. 415 ;; If the FORMAT string contains the specifiers %[ and %], the text between 416 ;; them will have the balloon-help text property. 417 (let ((case-fold-search nil)) 418 (if (string-match 419 "\\`\\(.*\\)%[0-9]?[{(�]\\(.*\\)%[0-9]?[�})]\\(.*\n?\\)\\'\\|%[-0-9]*=\\|%[-0-9]*\\*" 420 format) 421 (gnus-parse-complex-format format spec-alist) 422 ;; This is a simple format. 423 (gnus-parse-simple-format format spec-alist insert)))) 424 425(defun gnus-parse-complex-format (format spec-alist) 426 (let ((cursor-spec nil)) 427 (save-excursion 428 (gnus-set-work-buffer) 429 (insert format) 430 (goto-char (point-min)) 431 (while (re-search-forward "\"" nil t) 432 (replace-match "\\\"" nil t)) 433 (goto-char (point-min)) 434 (insert "(\"") 435 ;; Convert all font specs into font spec lists. 436 (while (re-search-forward "%\\([0-9]+\\)?\\([��{}()]\\)" nil t) 437 (let ((number (if (match-beginning 1) 438 (match-string 1) "0")) 439 (delim (aref (match-string 2) 0))) 440 (if (or (= delim ?\() 441 (= delim ?\{) 442 (= delim ?\�)) 443 (replace-match (concat "\"(" 444 (cond ((= delim ?\() "mouse") 445 ((= delim ?\{) "face") 446 (t "balloon")) 447 " " number " \"") 448 t t) 449 (replace-match "\")\"")))) 450 (goto-char (point-max)) 451 (insert "\")") 452 ;; Convert point position commands. 453 (goto-char (point-min)) 454 (let ((case-fold-search nil)) 455 (while (re-search-forward "%\\([-0-9]+\\)?\\*" nil t) 456 (replace-match "\"(point)\"" t t) 457 (setq cursor-spec t))) 458 ;; Convert TAB commands. 459 (goto-char (point-min)) 460 (while (re-search-forward "%\\([-0-9]+\\)=" nil t) 461 (replace-match (format "\"(tab %s)\"" (match-string 1)) t t)) 462 ;; Convert the buffer into the spec. 463 (goto-char (point-min)) 464 (let ((form (read (current-buffer)))) 465 (if cursor-spec 466 `(let (gnus-position) 467 ,@(gnus-complex-form-to-spec form spec-alist) 468 (if gnus-position 469 (gnus-put-text-property gnus-position (1+ gnus-position) 470 'gnus-position t))) 471 `(progn 472 ,@(gnus-complex-form-to-spec form spec-alist))))))) 473 474(defun gnus-complex-form-to-spec (form spec-alist) 475 (delq nil 476 (mapcar 477 (lambda (sform) 478 (cond 479 ((stringp sform) 480 (gnus-parse-simple-format sform spec-alist t)) 481 ((eq (car sform) 'point) 482 '(setq gnus-position (point))) 483 ((eq (car sform) 'tab) 484 (gnus-spec-tab (cadr sform))) 485 (t 486 (funcall (intern (format "gnus-%s-face-function" (car sform))) 487 (gnus-complex-form-to-spec (cddr sform) spec-alist) 488 (nth 1 sform))))) 489 form))) 490 491 492(defun gnus-xmas-format (fstring &rest args) 493 "A version of `format' which preserves text properties. 494 495Required for XEmacs, where the built in `format' function strips all text 496properties from both the format string and any inserted strings. 497 498Only supports the format sequence %s, and %% for inserting 499literal % characters. A pad width and an optional - (to right pad) 500are supported for %s." 501 (let ((re "%%\\|%\\(-\\)?\\([1-9][0-9]*\\)?s") 502 (n (length args))) 503 (with-temp-buffer 504 (insert fstring) 505 (goto-char (point-min)) 506 (while (re-search-forward re nil t) 507 (goto-char (match-end 0)) 508 (cond 509 ((string= (match-string 0) "%%") 510 (delete-char -1)) 511 (t 512 (if (null args) 513 (error 'wrong-number-of-arguments #'my-format n fstring)) 514 (let* ((minlen (string-to-number (or (match-string 2) ""))) 515 (arg (car args)) 516 (str (if (stringp arg) arg (format "%s" arg))) 517 (lpad (null (match-string 1))) 518 (padlen (max 0 (- minlen (length str))))) 519 (replace-match "") 520 (if lpad (insert-char ?\ padlen)) 521 (insert str) 522 (unless lpad (insert-char ?\ padlen)) 523 (setq args (cdr args)))))) 524 (buffer-string)))) 525 526(defun gnus-parse-simple-format (format spec-alist &optional insert) 527 ;; This function parses the FORMAT string with the help of the 528 ;; SPEC-ALIST and returns a list that can be eval'ed to return a 529 ;; string. 530 (let ((max-width 0) 531 spec flist fstring elem result dontinsert user-defined 532 type value pad-width spec-beg cut-width ignore-value 533 tilde-form tilde elem-type extended-spec) 534 (save-excursion 535 (gnus-set-work-buffer) 536 (insert format) 537 (goto-char (point-min)) 538 (while (re-search-forward "%" nil t) 539 (setq user-defined nil 540 spec-beg nil 541 pad-width nil 542 max-width nil 543 cut-width nil 544 ignore-value nil 545 tilde-form nil 546 extended-spec nil) 547 (setq spec-beg (1- (point))) 548 549 ;; Parse this spec fully. 550 (while 551 (cond 552 ((looking-at "\\([-.0-9]+\\)\\(,[-0-9]+\\)?") 553 (setq pad-width (string-to-number (match-string 1))) 554 (when (match-beginning 2) 555 (setq max-width (string-to-number (buffer-substring 556 (1+ (match-beginning 2)) 557 (match-end 2))))) 558 (goto-char (match-end 0))) 559 ((looking-at "~") 560 (forward-char 1) 561 (setq tilde (read (current-buffer)) 562 type (car tilde) 563 value (cadr tilde)) 564 (cond 565 ((memq type '(pad pad-left)) 566 (setq pad-width value)) 567 ((eq type 'pad-right) 568 (setq pad-width (- value))) 569 ((memq type '(max-right max)) 570 (setq max-width value)) 571 ((eq type 'max-left) 572 (setq max-width (- value))) 573 ((memq type '(cut cut-left)) 574 (setq cut-width value)) 575 ((eq type 'cut-right) 576 (setq cut-width (- value))) 577 ((eq type 'ignore) 578 (setq ignore-value 579 (if (stringp value) value (format "%s" value)))) 580 ((eq type 'form) 581 (setq tilde-form value)) 582 (t 583 (error "Unknown tilde type: %s" tilde))) 584 t) 585 (t 586 nil))) 587 (cond 588 ;; User-defined spec -- find the spec name. 589 ((eq (setq spec (char-after)) ?u) 590 (forward-char 1) 591 (when (and (eq (setq user-defined (char-after)) ?&) 592 (looking-at "&\\([^;]+\\);")) 593 (setq user-defined (match-string 1)) 594 (goto-char (match-end 1)))) 595 ;; extended spec 596 ((and (eq spec ?&) (looking-at "&\\([^;]+\\);")) 597 (setq extended-spec (intern (match-string 1))) 598 (goto-char (match-end 1)))) 599 (forward-char 1) 600 (delete-region spec-beg (point)) 601 602 ;; Now we have all the relevant data on this spec, so 603 ;; we start doing stuff. 604 (insert "%") 605 (if (eq spec ?%) 606 ;; "%%" just results in a "%". 607 (insert "%") 608 (cond 609 ;; Do tilde forms. 610 ((eq spec ?@) 611 (setq elem (list tilde-form ?s))) 612 ;; Treat user defined format specifiers specially. 613 (user-defined 614 (setq elem 615 (list 616 (list (intern (format 617 (if (stringp user-defined) 618 "gnus-user-format-function-%s" 619 "gnus-user-format-function-%c") 620 user-defined)) 621 'gnus-tmp-header) 622 ?s))) 623 ;; Find the specification from `spec-alist'. 624 ((setq elem (cdr (assq (or extended-spec spec) spec-alist)))) 625 (t 626 (setq elem '("*" ?s)))) 627 (setq elem-type (cadr elem)) 628 ;; Insert the new format elements. 629 (when (and pad-width 630 (not (and (featurep 'xemacs) 631 gnus-use-correct-string-widths))) 632 (insert (number-to-string pad-width))) 633 ;; Create the form to be evaled. 634 (if (or max-width cut-width ignore-value 635 (and (featurep 'xemacs) 636 gnus-use-correct-string-widths)) 637 (progn 638 (insert ?s) 639 (let ((el (car elem))) 640 (cond ((= (cadr elem) ?c) 641 (setq el (list 'char-to-string el))) 642 ((= (cadr elem) ?d) 643 (setq el (list 'int-to-string el)))) 644 (when ignore-value 645 (setq el (gnus-tilde-ignore-form el ignore-value))) 646 (when cut-width 647 (setq el (gnus-tilde-cut-form el cut-width))) 648 (when max-width 649 (setq el (gnus-tilde-max-form el max-width))) 650 (when pad-width 651 (setq el (gnus-pad-form el pad-width))) 652 (push el flist))) 653 (insert elem-type) 654 (push (car elem) flist)))) 655 (setq fstring (buffer-substring-no-properties (point-min) (point-max)))) 656 657 ;; Do some postprocessing to increase efficiency. 658 (setq 659 result 660 (cond 661 ;; Emptiness. 662 ((string= fstring "") 663 nil) 664 ;; Not a format string. 665 ((not (string-match "%" fstring)) 666 (list fstring)) 667 ;; A format string with just a single string spec. 668 ((string= fstring "%s") 669 (list (car flist))) 670 ;; A single character. 671 ((string= fstring "%c") 672 (list (car flist))) 673 ;; A single number. 674 ((string= fstring "%d") 675 (setq dontinsert) 676 (if insert 677 (list `(princ ,(car flist))) 678 (list `(int-to-string ,(car flist))))) 679 ;; Just lots of chars and strings. 680 ((string-match "\\`\\(%[cs]\\)+\\'" fstring) 681 (nreverse flist)) 682 ;; A single string spec at the beginning of the spec. 683 ((string-match "\\`%[sc][^%]+\\'" fstring) 684 (list (car flist) (substring fstring 2))) 685 ;; A single string spec in the middle of the spec. 686 ((string-match "\\`\\([^%]+\\)%[sc]\\([^%]+\\)\\'" fstring) 687 (list (match-string 1 fstring) (car flist) (match-string 2 fstring))) 688 ;; A single string spec in the end of the spec. 689 ((string-match "\\`\\([^%]+\\)%[sc]\\'" fstring) 690 (list (match-string 1 fstring) (car flist))) 691 ;; Only string (and %) specs (XEmacs only!) 692 ((and (featurep 'xemacs) 693 gnus-make-format-preserve-properties 694 (string-match 695 "\\`\\([^%]*\\(%%\\|%-?\\([1-9][0-9]*\\)?s\\)\\)*[^%]*\\'" 696 fstring)) 697 (list (cons 'gnus-xmas-format (cons fstring (nreverse flist))))) 698 ;; A more complex spec. 699 (t 700 (list (cons 'format (cons fstring (nreverse flist))))))) 701 702 (if insert 703 (when result 704 (if dontinsert 705 result 706 (cons 'insert result))) 707 (cond ((stringp result) 708 result) 709 ((consp result) 710 (cons 'concat result)) 711 (t ""))))) 712 713(defun gnus-eval-format (format &optional alist props) 714 "Eval the format variable FORMAT, using ALIST. 715If PROPS, insert the result." 716 (let ((form (gnus-parse-format format alist props))) 717 (if props 718 (gnus-add-text-properties (point) (progn (eval form) (point)) props) 719 (eval form)))) 720 721(defun gnus-compile () 722 "Byte-compile the user-defined format specs." 723 (interactive) 724 (require 'bytecomp) 725 (let ((entries gnus-format-specs) 726 (byte-compile-warnings '(unresolved callargs redefine)) 727 entry gnus-tmp-func) 728 (save-excursion 729 (gnus-message 7 "Compiling format specs...") 730 731 (while entries 732 (setq entry (pop entries)) 733 (if (memq (car entry) '(gnus-version version)) 734 (setq gnus-format-specs (delq entry gnus-format-specs)) 735 (let ((form (caddr entry))) 736 (when (and (listp form) 737 ;; Under GNU Emacs, it's (byte-code ...) 738 (not (eq 'byte-code (car form))) 739 ;; Under XEmacs, it's (funcall #<compiled-function ...>) 740 (not (and (eq 'funcall (car form)) 741 (byte-code-function-p (cadr form))))) 742 (defalias 'gnus-tmp-func `(lambda () ,form)) 743 (byte-compile 'gnus-tmp-func) 744 (setcar (cddr entry) (gnus-byte-code 'gnus-tmp-func)))))) 745 746 (push (cons 'version emacs-version) gnus-format-specs) 747 ;; Mark the .newsrc.eld file as "dirty". 748 (gnus-dribble-touch) 749 (gnus-message 7 "Compiling user specs...done")))) 750 751(defun gnus-set-format (type &optional insertable) 752 (set (intern (format "gnus-%s-line-format-spec" type)) 753 (gnus-parse-format 754 (symbol-value (intern (format "gnus-%s-line-format" type))) 755 (symbol-value (intern (format "gnus-%s-line-format-alist" type))) 756 insertable))) 757 758(provide 'gnus-spec) 759 760;; Local Variables: 761;; coding: iso-8859-1 762;; End: 763 764;;; arch-tag: a4328fa1-1f84-4b09-97ad-4b5767cfd50f 765;;; gnus-spec.el ends here 766