Function: format-spec

format-spec is an autoloaded and byte-compiled function defined in format-spec.el.gz.

Signature

(format-spec FORMAT SPECIFICATION &optional IGNORE-MISSING SPLIT)

Documentation

Return a string based on FORMAT and SPECIFICATION.

FORMAT is a string containing format-like specs like "su - %u %k". SPECIFICATION is an alist mapping format specification characters to their substitutions.

For instance:

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

Each %-spec may contain optional flag, width, and precision specifiers, as follows:

  %<flags><width><precision>character

The following flags are allowed:

* 0: Pad to the width, if given, with zeros instead of spaces.
* -: Pad to the width, if given, on the right instead of the left.
* <: Truncate to the width and precision, if given, on the left.
* >: Truncate to the width and precision, if given, on the right.
* ^: Convert to upper case.
* _: Convert to lower case.

The width and precision specifiers behave like the corresponding ones in format when applied to %s.

For example, "%<010b" means "substitute into the output the value associated with ?b in SPECIFICATION, either padding it with leading zeros or truncating leading characters until it's ten characters wide".

the substitution for a specification character can also be a function, taking no arguments and returning a string to be used for the replacement. It will only be called if FORMAT uses that character. For example:

  (format-spec "%n"
               `((?n . ,(lambda ()
                          (read-number "Number: ")))))

Note that it is best to make sure the function is not quoted, like above, so that it is compiled by the byte-compiler.

Any text properties of FORMAT are copied to the result, with any text properties of a %-spec itself copied to its substitution.

IGNORE-MISSING indicates how to handle %-spec characters not present in SPECIFICATION. If it is nil or omitted, emit an error; if it is the symbol ignore, leave those %-specs verbatim in the result, including their text properties, if any; if it is the symbol delete, remove those %-specs from the result; otherwise do the same as for the symbol ignore, but also leave any occurrences of "%%" in FORMAT verbatim in the result.

If SPLIT, instead of returning a single string, a list of strings is returned, where each format spec is its own element.

View in manual

Probably introduced at or before Emacs version 23.1.

Source Code

;; Defined in /usr/src/emacs/lisp/format-spec.el.gz
;;; format-spec.el --- format arbitrary formatting strings -*- lexical-binding: t -*-

;; Copyright (C) 1999-2025 Free Software Foundation, Inc.

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

;; This file is part of GNU Emacs.

;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; 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
;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:

;;; Code:

;;;###autoload
(defun format-spec (format specification &optional ignore-missing split)
  "Return a string based on FORMAT and SPECIFICATION.
FORMAT is a string containing `format'-like specs like \"su - %u %k\".
SPECIFICATION is an alist mapping format specification characters
to their substitutions.

For instance:

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

Each %-spec may contain optional flag, width, and precision
specifiers, as follows:

  %<flags><width><precision>character

The following flags are allowed:

* 0: Pad to the width, if given, with zeros instead of spaces.
* -: Pad to the width, if given, on the right instead of the left.
* <: Truncate to the width and precision, if given, on the left.
* >: Truncate to the width and precision, if given, on the right.
* ^: Convert to upper case.
* _: Convert to lower case.

The width and precision specifiers behave like the corresponding
ones in `format' when applied to %s.

For example, \"%<010b\" means \"substitute into the output the
value associated with ?b in SPECIFICATION, either padding it with
leading zeros or truncating leading characters until it's ten
characters wide\".

the substitution for a specification character can also be a
function, taking no arguments and returning a string to be used
for the replacement.  It will only be called if FORMAT uses that
character.  For example:

  (format-spec \"%n\"
               \\=`((?n . ,(lambda ()
                          (read-number \"Number: \")))))

Note that it is best to make sure the function is not quoted,
like above, so that it is compiled by the byte-compiler.

Any text properties of FORMAT are copied to the result, with any
text properties of a %-spec itself copied to its substitution.

IGNORE-MISSING indicates how to handle %-spec characters not
present in SPECIFICATION.  If it is nil or omitted, emit an
error; if it is the symbol `ignore', leave those %-specs verbatim
in the result, including their text properties, if any; if it is
the symbol `delete', remove those %-specs from the result;
otherwise do the same as for the symbol `ignore', but also leave
any occurrences of \"%%\" in FORMAT verbatim in the result.

If SPLIT, instead of returning a single string, a list of strings
is returned, where each format spec is its own element."
  (with-temp-buffer
    (let ((split-start (point-min))
          (split-result nil))
      (insert format)
      (goto-char (point-min))
      (while (search-forward "%" nil t)
        (cond
         ;; Quoted percent sign.
         ((= (following-char) ?%)
          (when (memq ignore-missing '(nil ignore delete))
            (delete-char 1)))
         ;; Valid format spec.
         ((looking-at (rx (? (group (+ (in " 0<>^_-"))))
                          (? (group (+ digit)))
                          (? (group ?. (+ digit)))
                          (group alpha)))
          (let* ((beg (point))
                 (end (match-end 0))
                 (flags (match-string 1))
                 (width (match-string 2))
                 (trunc (match-string 3))
                 (char (string-to-char (match-string 4)))
                 (text (let ((res (cdr (assq char specification))))
                         (if (functionp res) (funcall res) res))))
            (when (and split
                       (not (= (1- beg) split-start)))
              (push (buffer-substring split-start (1- beg)) split-result))
            (cond (text
                   ;; Handle flags.
                   (setq text (format-spec--do-flags
                               (format "%s" text)
                               (format-spec--parse-flags flags)
                               (and width (string-to-number width))
                               (and trunc (car (read-from-string trunc 1)))))
                   ;; Insert first, to preserve text properties.
                   (insert-and-inherit text)
                   ;; Delete the specifier body.
                   (delete-region (point) (+ end (length text)))
                   ;; Delete the percent sign.
                   (delete-region (1- beg) beg))
                  ((eq ignore-missing 'delete)
                   ;; Delete the whole format spec.
                   (delete-region (1- beg) end))
                  ((not ignore-missing)
                   (error "Invalid format character: `%%%c'" char)))
            (when split
              (push (buffer-substring (1- beg) (point)) split-result)
              (setq split-start (point)))))
         ;; Signal an error on bogus format strings.
         ((not ignore-missing)
          (error "Invalid format string"))))
      (if (not split)
          (buffer-string)
        (unless (= split-start (point-max))
          (push (buffer-substring split-start (point-max)) split-result))
        (nreverse split-result)))))