format-spec.el 3.14 KB
Newer Older
1
;;; format-spec.el --- functions for formatting arbitrary formatting strings
Glenn Morris's avatar
Glenn Morris committed
2

Paul Eggert's avatar
Paul Eggert committed
3
;; Copyright (C) 1999-2019 Free Software Foundation, Inc.
Glenn Morris's avatar
Glenn Morris committed
4 5 6 7 8 9

;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
;; Keywords: tools

;; This file is part of GNU Emacs.

10
;; GNU Emacs is free software: you can redistribute it and/or modify
Glenn Morris's avatar
Glenn Morris committed
11
;; it under the terms of the GNU General Public License as published by
12 13
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
Glenn Morris's avatar
Glenn Morris committed
14 15 16 17 18 19 20

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
21
;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
Glenn Morris's avatar
Glenn Morris committed
22 23 24 25 26

;;; Commentary:

;;; Code:

27
(defun format-spec (format specification &optional only-present)
Glenn Morris's avatar
Glenn Morris committed
28
  "Return a string based on FORMAT and SPECIFICATION.
29
FORMAT is a string containing `format'-like specs like \"su - %u %k\",
Glenn Morris's avatar
Glenn Morris committed
30
while SPECIFICATION is an alist mapping from format spec characters
31 32 33 34 35 36 37 38 39 40 41 42 43 44
to values.

For instance:

  (format-spec \"su - %u %k\"
               `((?u . ,(user-login-name))
                 (?k . \"ls\")))

Any text properties on a %-spec itself are propagated to the text
that it generates.

If ONLY-PRESENT, format spec characters not present in
SPECIFICATION are ignored, and the \"%\" characters are left
where they are, including \"%%\" strings."
Glenn Morris's avatar
Glenn Morris committed
45 46 47 48 49
  (with-temp-buffer
    (insert format)
    (goto-char (point-min))
    (while (search-forward "%" nil t)
      (cond
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
        ;; Quoted percent sign.
        ((eq (char-after) ?%)
         (unless only-present
	   (delete-char 1)))
        ;; Valid format spec.
        ((looking-at "\\([-0-9.]*\\)\\([a-zA-Z]\\)")
	 (let* ((num (match-string 1))
	        (spec (string-to-char (match-string 2)))
	        (val (assq spec specification)))
	   (if (not val)
               (unless only-present
	         (error "Invalid format character: `%%%c'" spec))
	     (setq val (cdr val))
	     ;; Pad result to desired length.
	     (let ((text (format (concat "%" num "s") val)))
	       ;; Insert first, to preserve text properties.
	       (insert-and-inherit text)
	       ;; Delete the specifier body.
               (delete-region (+ (match-beginning 0) (length text))
                              (+ (match-end 0) (length text)))
               ;; Delete the percent sign.
               (delete-region (1- (match-beginning 0)) (match-beginning 0))))))
        ;; Signal an error on bogus format strings.
        (t
          (unless only-present
	    (error "Invalid format string")))))
Glenn Morris's avatar
Glenn Morris committed
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92
    (buffer-string)))

(defun format-spec-make (&rest pairs)
  "Return an alist suitable for use in `format-spec' based on PAIRS.
PAIRS is a list where every other element is a character and a value,
starting with a character."
  (let (alist)
    (while pairs
      (unless (cdr pairs)
	(error "Invalid list of pairs"))
      (push (cons (car pairs) (cadr pairs)) alist)
      (setq pairs (cddr pairs)))
    (nreverse alist)))

(provide 'format-spec)

;;; format-spec.el ends here