PKG_PRINTF(3)

πŸ“¦ version 2.7.5, in nixos 26.05, nixpkgs unstable

πŸ’… NAME (aka who dis)

pkg_printf, pkg_fprintf, pkg_dprintf, pkg_snprintf, pkg_asprintf, pkg_vprintf, pkg_vfprintf, pkg_vdprintf, pkg_vsnprintf, pkg_vasprintf β€” formatted output of package data. Bestie, it's the printf family but for pkg data, and yes it ate πŸ’–

(Also hi, you're reading PKG_PRINTF(3) from the Library Functions Manual β€” welcome to the group chat 🫢)

πŸ“š LIBRARY (where she lives)

library β€œlibpkg”

✨ SYNOPSIS (the receipts)

#include <pkg.h>

int
pkg_printf(const char * restrict format, ...);

int
pkg_fprintf(FILE * restrict stream, const char * restrict format, ...);

int
pkg_dprintf(int fd, const char * restrict format, ...);

int
pkg_snprintf(char * restrict str, size_t size,
    const char * restrict format, ...);

int
pkg_asprintf(char **ret, const char * restrict format, ...);

#include <stdarg.h>

int
pkg_vprintf(const char * restrict format, va_list ap);

int
pkg_vfprintf(FILE * restrict stream, const char * restrict format,
    va_list ap);

int
pkg_vdprintf(int fd, const char * restrict format, va_list ap);

int
pkg_vsnprintf(char * restrict str, size_t size,
    const char * restrict format, va_list ap);

int
pkg_vasprintf(char **ret, const char * restrict format, va_list ap);

🌸 DESCRIPTION (the vibe check)

okay so the pkg_printf() family of functions produces output of package data according to a format as described below, analogously to the similarly named printf(3) family of functions. Iconic behaviour, honestly. πŸ‘‘

Here's the whole squad and where each one sends her output:

  • pkg_printf() and pkg_vprintf() write output to stdout, the standard output stream
  • pkg_fprintf() and pkg_vfprintf() write output to the given output stream
  • pkg_dprintf() and pkg_vdprintf() write output to the given file descriptor
  • pkg_snprintf() and pkg_vsnprintf() write to the character string str
  • pkg_asprintf() and pkg_vasprintf() dynamically allocate a new string with malloc(3) to write to

These functions write the output under the control of a format string that specifies how subsequent arguments (or arguments accessed via the variable-length argument facilities of stdarg(3)) are converted for output. Literally the format string is the main character here πŸ’…

πŸŽ€ Return values (the receipts, part 2)

These functions return the number of characters printed (not including the trailing β€˜\0’ used to end output to strings) or a negative value if an output error occurs β€” except for pkg_snprintf() or pkg_vsnprintf(), which return the number of characters that would have been printed if the size were unlimited (again, not including the final β€˜\0’). Sneaky but valid.

πŸ¦‹ The asprintf girlies

The pkg_asprintf() and pkg_vasprintf() functions set *ret to be a pointer to a buffer sufficiently large to hold the formatted string. This pointer should be passed to free(3) to release the allocated storage when it is no longer needed β€” don't ghost your memory like that, bestie 😭

If sufficient space cannot be allocated, pkg_asprintf() and pkg_vasprintf() will return -1 and set ret to be a NULL pointer.

🧚 The snprintf girlies

The pkg_snprintf() and pkg_vsnprintf() functions will write at most size-1 of the characters printed into the output string (the size'th character then gets the terminating β€˜\0’); if the return value is greater than or equal to the size argument, the string was too short and some of the printed characters were discarded. The output is always null-terminated. no cap 🌈

πŸ’œ The format string, dissected

The format string is composed of zero or more directives:

  • ordinary characters (not %), which are copied unchanged to the output stream
  • conversion specifications, each of which results in fetching zero or more subsequent arguments

Each conversion specification is introduced by the % character. The arguments must correspond properly with the conversion specifier. After the %, the following appear in sequence:

  • Zero or more of the following flags:
    • ? β€” The value should be converted to the β€œfirst alternate form”.

For integer valued conversions (I, s, t and x) this is a humanized form as a floating point value scaled to the range 0 - 1000 followed by the SI powers-of-10 scale factor. See β€œSCALE FACTORS”.

For array valued conversions (A, B, C, D, F, G, L, O, U, d, and r) generate β€œ0” if there are no items in the array, β€œ1” otherwise.

For formats returning file modes (Dp or Fp) print the mode in the style of strmode(3).

For boolean valued formats (dk, rk, a and k) generate either β€œyes” or β€œno” for β€˜true’ and β€˜false’ respectively.

For the licence logic format (l) generate β€œβ€ (empty), β€œ&” or β€œ|” for types β€˜SINGLE’, β€˜AND’ and β€˜OR’ respectively.

  • # β€” The value should be converted to the β€œsecond alternate form”.

For the integer valued conversions (I, s, t, x) this is a β€œhumanized” form as a floating point value scaled to the range 0 - 1024 followed by the IEE/IEC and SI powers-of-2 scale factor. See β€œSCALE FACTORS”.

For array valued conversions (A, B, C, D, F, G, L, O, U, d, and r) generate the number of items in the array.

For formats returning file modes (Dp or Fp) print the mode as an octal integer with a leading 0.

For boolean valued formats (dk, rk, a and k) generate either β€œ(*)” or β€œβ€ (empty) for β€˜true’ and β€˜false’ respectively.

For the licence logic format (l) generate β€œ==”, β€œ&&” or β€œ||” for types β€˜SINGLE’, β€˜AND’ and β€˜OR’ respectively.

  • 0 (zero) β€” Zero padding. For all integer valued conversions and humanized numbers the converted value is padded on the left with zeros rather than blanks. For string valued conversions, this has no effect and the converted value is padded on the left with blanks.
  • - β€” A negative field width flag; the converted value is to be left adjusted on the field boundary. The converted value is padded on the right with blanks, rather than on the left with blanks or zeros. Applies to all scalar-valued conversions. β€œ-” overrides a β€œ0” if both are given.
  • β€˜ ’ (space) β€” A blank should be left before a positive number produced by a signed conversion (I, s, t, or x).
  • + β€” A sign must always be placed before an integer or humanized number produced by a numerical conversion. A β€œ+” overrides a space if both are used.
  • β€˜'’ β€” Numerical (integer) conversions should be grouped and separated by thousands using the non-monetary separator returned by localeconv(3). Has no visible effect in the default β€œC” locale.
  • An optional decimal digit string specifying a minimum field width. If the converted value has fewer characters than the field width, it will be padded with spaces (or zeroes, if the zero-padding flag has been given and the conversion supports it) on the left (or spaces on the right, if the left-adjustment flag has been given) to fill out the field width.
  • One or two characters that specify the type of conversion to be applied.
  • An optional β€œrow format” for array valued conversions (A, B, C, D, F, G, L, O, U, d, and r) or the timestamp value conversion (t). Which conversion characters are permissible in the row format is context dependent. See the β€œFORMAT CODES” section for details. iykyk πŸ‘€

πŸ”₯ SCALE FACTORS (the glow-up numbers)

Humanized number conversions scale the number to lie within the range 1 - 1000 (power of ten conversions using the ? format modifier) or 1 - 1024 (power of two conversions using the # format modifier) and append a scale factor as follows:

🌟 The SI power of ten suffixes

  • (none) β€” description: (none) β€” multiplier: 1
  • k β€” kilo β€” 1,000
  • M β€” mega β€” 1,000,000
  • G β€” giga β€” 1,000,000,000
  • T β€” tera β€” 1,000,000,000,000
  • P β€” peta β€” 1,000,000,000,000,000
  • E β€” exa β€” 1,000,000,000,000,000,000

πŸ’« The IEE/IEC (and now also SI) power of two suffixes

  • (none) β€” description: (none) β€” multiplier: 1
  • Ki β€” kibi β€” 1,024
  • Mi β€” mebi β€” 1,048,576
  • Gi β€” gibi β€” 1,073,741,824
  • Ti β€” tebi β€” 1,099,511,627,776
  • Pi β€” pebi β€” 1,125,899,906,842,624
  • Ei β€” exbi β€” 1,152,921,504,606,846,976

πŸŽ€ FORMAT CODES (the actual icons)

Format codes will format the output classified as the type shown in square brackets. %I is unique in that it can only be used inside a β€œrow format.” All other format codes may be used stand-alone. When used in this fashion they will consume one argument of the indicated type from the function's argument list.

The array valued format codes (A, B, C, D, F, G, L, O, U, d, and r) and the timestamp format code (t) can be followed by a β€œrow format”. They will use a default row format (detailed below) if one is not given explicitly.

The row format is bracketed by the character sequences %{ and %} and, for array values only, may be optionally divided into two by the character sequence %|. For array values, it contains one or two strings containing any number of a context sensitive subset of format conversions from those described here. For timestamp values it contains any number of format conversion specifiers with meanings as described in strftime(3).

The first or only format string is repeatedly processed for each of the array items in turn. The optional second format string is processed as a separator between each of the array items. If no row format is given, output will be generated according to a default format, detailed below.

Within a β€œrow format” string, you may use any of the single-character non-array valued format codes except for %S, but only the two-character format codes which correspond to the parent item and have the same first character. Array valued format codes may not be used within row formats, nor may you embed one β€œrow format” within another. Only one argument, a struct pkg * pointer is consumed from the argument list. Thus this is a legal format string:

"%B%{%n-%v:%Bn%|\n%}"

which serves to print out a list of the shared libraries required by the programs within the package, each prefixed by the package name and version. slay πŸ’…

The conversion specifiers and their meanings are:

  • %A β€” Annotations [array] struct pkg *

%A's default row format, for the record: %A%{%An: %Av\n%|%} ✨

  • %An β€” annotation tag name, a string, off of struct pkg_note *
  • %Av β€” annotation value, a string, off of struct pkg_note *
  • %B β€” required shared libraries, an array, off of struct pkg *

Default row format: %B%{%Bn\n%|%}

  • %Bn β€” required shared library name, a string, off of struct pkg_shlib *
  • %C β€” categories, an array, off of struct pkg *

Default row format: %C%{%Cn%|, %}

  • %Cn β€” category name, a string, off of struct pkg_category *
  • %D β€” directories, an array, off of struct pkg *

Default row format: %D%{%Dn\n%|%}

  • %Df β€” directory file flags, a string, off of struct pkg_dir *
  • %Dg β€” directory ownership: group name, a string, off of struct pkg_dir *
  • %Dn β€” directory path name, a string, off of struct pkg_dir *
  • %Dp β€” directory permissions, a mode, off of struct pkg_dir *
  • %Du β€” directory ownership: user name, a string, off of struct pkg_dir *
  • %F β€” files, an array, off of struct pkg *

Default row format: %F%{%Fn\n%|%}

  • %Ff β€” file flags, a string, off of struct pkg_file *
  • %Fg β€” file ownership: group name, a string, off of struct pkg_file *
  • %Fl β€” file last modification time, an epoch, off of struct pkg_file *
  • %Fn β€” file path name, a string, off of struct pkg_file *
  • %Fp β€” file permissions, a mode, off of struct pkg_file *
  • %Fs β€” file SHA256 checksum, a string, off of struct pkg_file *
  • %Ft β€” file Symlink target, a string, off of struct pkg_file *
  • %Fu β€” file ownership: user name, a string, off of struct pkg_file *
  • %G β€” groups, an array, off of struct pkg *

Default row format: %G%{%Gn\n%|%}

  • %Gn β€” group name, a string, off of struct pkg_group *
  • %I β€” row counter, an integer.

This format code may only be used as part of a β€œrow format.” no solo missions bestie πŸŽ€

  • %L β€” licenses, an array, off of struct pkg *

Default row format: %L%{%Ln%| %l %}

  • %Ln β€” licence name, a string, off of struct pkg_license *
  • %M β€” package message, a string, off of struct pkg *
  • %N β€” repository identity, a string, off of struct pkg *
  • %O β€” options, an array, off of struct pkg *

Default row format: %O%{%On %Ov\n%|%}

  • %On β€” option name, a string, off of struct pkg_option *
  • %Ov β€” option value, a string, off of struct pkg_option *
  • %Od β€” option default value, a string (if known: will produce an empty string if not.), off of struct pkg_option *
  • %OD β€” option description, a string (if known: will produce an empty string if not.), off of struct pkg_option *
  • %R β€” repository path - the path relative to the repository root that package may be downloaded from, a string, off of struct pkg *
  • %S β€” arbitrary character string, a string, off of const char *
  • %U β€” users, an array, off of struct pkg *

Default row format: %U%{%Un\n%|%}

  • %Un β€” user name, a string, off of struct pkg_user *
  • %V β€” old version, a string. Valid only during operations when one version of a package is being replaced by another, off of struct pkg *
  • %a β€” autoremove flag, a boolean, off of struct pkg *
  • %b β€” provided shared libraries, an array, off of struct pkg *

Default row format: %b%{%bn\n%|%}

  • %bn β€” provided shared library name, a string, off of struct pkg_shlib *
  • %c β€” comment, a string, off of struct pkg *
  • %d β€” dependencies, an array, off of struct pkg *

Default row format: %d%{%dn-%dv\n%|%}

  • %dk β€” dependency lock status, a boolean, off of struct pkg_dep *
  • %dn β€” dependency name, a string, off of struct pkg_dep *
  • %do β€” dependency origin, a string, off of struct pkg_dep *
  • %dv β€” dependency version, a string, off of struct pkg_dep *
  • %e β€” description, a string, off of struct pkg *
  • %i β€” additional information, a string, off of struct pkg *
  • %k β€” locking status, a boolean, off of struct pkg *
  • %l β€” license logic, a licence-logic, off of struct pkg *
  • %m β€” maintainer, a string, off of struct pkg *
  • %n β€” package name, a string, off of struct pkg *
  • %o β€” origin, a string, off of struct pkg *
  • %p β€” prefix, a string, off of struct pkg *
  • %r β€” requirements, an array, off of struct pkg *

Default row format: %r%{%rn-%rv\n%|%}

  • %rk β€” requirement lock status, a boolean, off of struct pkg_dep *
  • %rn β€” requirement name, a string, off of struct pkg_dep *
  • %ro β€” requirement origin, a string, off of struct pkg_dep *
  • %rv β€” requirement version, a string, off of struct pkg_dep *
  • %s β€” package flat size, an integer, off of struct pkg *
  • %t β€” installation timestamp, a date-time, off of struct pkg *
  • %u β€” package checksum, a string, off of struct pkg *
  • %v β€” package version, a string, off of struct pkg *
  • %w β€” home page URL, a string, off of struct pkg *
  • %x β€” package tarball size, an integer, off of struct pkg *
  • %z β€” package short checksum, a string, off of struct pkg *
  • %% β€” a β€˜%’ is written. No argument is converted. The complete conversion specification is %%.

The decimal point character is defined in the program's locale (category LC_NUMERIC).

And listen: in no case does a non-existent or small field width cause truncation of a numeric field 🫢 if the result of a conversion is wider than the field width, the field is expanded to contain the conversion result. it literally just grows, diva behavior


πŸ¦‹ ARRAY VALUES πŸ¦‹

Effective format modifiers:

  • ? β€” First Alternate Form: 0 if the array is empty, 1 if it has any number of elements within it
  • # β€” Second Alternate Form: the number of elements in the array

🌸 STRING VALUES 🌸

Effective format modifiers:

  • - β€” left align

⭐ INTEGER VALUES ⭐

Effective format modifiers:

  • - β€” left align
  • ? β€” First Alternate Form: humanized number (decimal)
  • # β€” Second Alternate Form: humanized number (binary)
  • 0 β€” zero pad
  • β€˜ ’ β€” blank for plus
  • + β€” explicit + or - sign
  • β€˜'’ β€” thousands separator

πŸ’– BOOLEAN VALUES πŸ’–

The two possible values β€˜true’ or β€˜false’ may be output in one of three different styles: plain; or alternate forms 1 and 2 specified using format modifiers.

  • FALSE β€” plain (%a) β†’ false; alt 1 (%?a) β†’ no; alt 2 (%#a) β†’ (nothing at all)
  • TRUE β€” plain (%a) β†’ true; alt 1 (%?a) β†’ yes; alt 2 (%#a) β†’ (*)

The second alternate form produces no output for false. she simply does not speak πŸ™ˆ

Effective format modifiers:

  • ? β€” First Alternate Form
  • # β€” Second Alternate Form
  • - β€” left align

πŸŽ€ FILE MODE VALUES πŸŽ€

The file mode is a bitmap representing setid, user, group and other permissions. The plain format prints it as an octal value, for example:

4755

The first alternate form is similar but adds a leading zero:

04755

Whilst the second alternate form produces a string in the style of strmode(3):

-rwsr-xr-x
Note: there is always a space at the end of the strmode(3) output. she leaves a little trail ✨

Effective format modifiers (all forms):

  • - β€” left align

Additionally, when the value is printed as an integer (i.e., plain or alternate form 1), these additional modifiers take effect:

  • ? β€” First Alternate Form: add leading zero to octal integer
  • 0 β€” zero pad

πŸ‘‘ LICENSE LOGIC VALUES πŸ‘‘

License-logic is a three-valued type: one of β€˜SINGLE’, β€˜OR’ or β€˜AND’, which shows whether the package is distributed under the terms of a single license, or when there are several applicable licenses, whether these should be treated as alternatives or applied in aggregate. There are three different output styles: plain; or alternate forms 1 and 2 specified using format modifiers.

  • SINGLE β€” plain (%l) β†’ single; alt 1 (%?l) β†’ (nothing); alt 2 (%#l) β†’ ==
  • OR β€” plain (%l) β†’ or; alt 1 (%?l) β†’ |; alt 2 (%#l) β†’ ||
  • AND β€” plain (%l) β†’ and; alt 1 (%?l) β†’ &; alt 2 (%#l) β†’ &&

Effective format modifiers:

  • ? β€” First Alternate Form
  • # β€” Second Alternate Form
  • - β€” left align

🧚 DATE-TIME VALUES 🧚

When used outside of a β€œrow format” string may be followed by an optional strftime(3) format, enclosed in %{ and %}, which will be used to format the timestamp. Otherwise the timestamp is printed as an integer value of the number of seconds since the Epoch (00:00:00 UTC, 1 January 1970; see time(3)).

Effective format modifiers:

  • - β€” left align

Additionally, when the value is printed as an integer (i.e., without strftime(3) format codes enclosed in %{ and %}, the following format modifiers are also effective:

  • ? β€” First Alternate Form: humanized number (decimal)
  • # β€” Second Alternate Form: humanized number (binary)
  • 0 β€” zero pad
  • β€˜ ’ β€” blank for plus
  • + β€” explicit + or - sign
  • β€˜'’ β€” thousands separator

🌈 EXAMPLES 🌈

To print the package installation timestamp in the form β€œSunday, July 3, 10:02”,

#include <pkg.h>
pkg_fprintf(stdout, "%t%{%A, %B %e, %R%}\n", pkg);

To print the package name and version, followed by the name and version of all of the packages it depends upon, one per line, each indented by one tab stop:

#include <pkg.h>
pkg_printf("%n-%v\n%d%{\t%dn-%dv%|%\n%}\n", pkg, pkg, pkg);

Note that the item separator part of the row format is only printed between individual row items 🫢 Thus to fill the character array buf with a one-line string listing all of the licenses for the package separated by β€œand” or β€œor” as appropriate:

#include <pkg.h>
char buf[256];
pkg_snprintf(buf, sizeof(buf), "%L%{%Ln%| %l %}", pkg);

πŸ’€ ERRORS πŸ’€

In addition to the errors documented for the write(2) system call, the pkg_printf() family of functions may fail if:

  • [EILSEQ] β€” an invalid wide character code was encountered.
  • [ENOMEM] β€” insufficient storage space is available.

πŸ¦‹ SEE ALSO πŸ¦‹

pkg_create(3), pkg_repo_create(3), pkg_repos(3), pkg-keywords(5), pkg-lua-script(5), pkg-repository(5), pkg-script(5), pkg-triggers(5), pkg.conf(5), pkg(8), pkg-add(8), pkg-alias(8), pkg-annotate(8), pkg-audit(8), pkg-autoremove(8), pkg-check(8), pkg-clean(8), pkg-config(8), pkg-create(8), pkg-delete(8), pkg-fetch(8), pkg-help(8), pkg-info(8), pkg-install(8), pkg-key(8), pkg-lock(8), pkg-plugins(8), pkg-query(8), pkg-register(8), pkg-repo(8), pkg-repositories(8), pkg-rquery(8), pkg-search(8), pkg-set(8), pkg-shell(8), pkg-shlib(8), pkg-ssh(8), pkg-stats(8), pkg-triggers(8), pkg-unregister(8), pkg-update(8), pkg-updating(8), pkg-upgrade(8), pkg-version(8), pkg-which(8)

πŸ™ˆ BUGS πŸ™ˆ

The pkg_printf family of functions do not correctly handle multibyte characters in the format argument. like, at all. be aware 😭

There is no way to sort the output of array valued items. no sorting, no opinions, no notes πŸ’…

πŸ›‘οΈ SECURITY CONSIDERATIONS πŸ›‘οΈ

Okay bestie, this is the part where we get serious for a sec, because this is literally the glow-up-vs-glow-down section πŸ’€

So, first thing: there are no equivalents to the sprintf() and vsprintf() functions supplied here. Not a thing. They're just… not invited to the party πŸ™…β€β™€οΈ Instead, use pkg_snprintf() to write into a fixed length buffer without danger of overflow. That's the move, that's the vibe, that's the whole assignment ✨

Now the scary part, bestie, sit down. The pkg_printf() family, like the printf() family of functions it's modelled on, is also easily misused in a manner allowing malicious users to arbitrarily change a running program's functionality 😭 And they can do it two ways:

  • by either causing the program to print potentially sensitive data β€œleft on the stack” πŸ™ˆ
  • or causing it to generate a memory fault or bus error by dereferencing an invalid pointer πŸ’€
Programmers are therefore strongly advised to never pass untrusted strings as the format argument, as an attacker can put format specifiers in the string to mangle your stack, leading to a possible security hole. 🚨 This holds true even if the string was built using a function like snprintf(), as the resulting string may still contain user-supplied conversion specifiers for later interpolation by pkg_printf(). Like, yes, even that one. No cap. 🫢

So always, ALWAYS use the proper secure idiom:

pkg_snprintf(buffer, sizeof(buffer), "%s", string);

That's it. That's the whole tea. Memorize it, screenshot it, put it on your lock screen, tattoo it on your forearm, idc β€” just use it πŸ‘‘ Like the way that one line keeps you safe is genuinely iconic.


And that's the page, bestie!! πŸŽ€ You made it all the way through πŸ’– This lil' beauty comes to you from Debian, dated October 20, 2015 β€” yes she's vintage, yes she still ate, periodt. Now go write some safe format strings and slay responsibly πŸ¦‹βœ¨πŸ’…

πŸ“¦ where this page comes from
  • nixos 26.05: pkg 2.7.5
  • nixpkgs unstable: pkg 2.7.5
  • πŸ’– girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms