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