Skip to content

Neved4/asprintf

Repository files navigation

C IEEE CodeQL

asprintf - One True asprintf, vasprintf! 🛠️

Robust, portable implementation of asprintf(), vasprintf(). Thoroughly tested.

Highlights

  • 🚀 Fast - performance akin other implementations
  • 📦 Simple - zero dependencies, lightweight (37 lines, 574 bytes) and ISO C99 compatible.
  • 🔒 Robust - safety-first, with substantial unit testing.
  • ⚙️ Compatible - interop across different systems, drop-in replacement for asprintf and vasprintf as in glibc, *BSD libc, musl libc and many more.

Motivation

To have a version of asprintf that is simple, robust and reliable that just works everywhere by being consistent across diverse platforms and system implementations, that is backward-compatible with existing libc libraries, minimizes unexpected behavior, is thoroughly tested, focuses on correctness, is straightforward, sticks to C99 and POSIX standards, to review other implementations and deepen my understanding of pointers and systems programming.

Getting Started

Setup

If you have clib installed, run:

$ clib install Neved4/asprintf

If you don't have the above, start by cloning the repository:

$ git clone https://github.com/Neved4/asprintf

Once you've cloned the repository, build by executing:

$ make asprintf

Alternatively, if you have zig:

$ zig cc asprintf.c -t asprintf

Usage

// asprintf, vasprintf - print to allocated string

#include "asprintf.h"

int asprintf(char **strp, const char *fmt, ...);

int vasprintf(char **strp, const char *fmt, va_list ap);

Examples

Consider a getconf() function to retrieve a config specified path, that supports both XDG_CONFIG_HOME and fallbacks:

char *getconf() {
    const char *file = "tz.conf", *home = getenv("HOME"),
        *xdg_config_home = getenv("XDG_CONFIG_HOME");
    char *config = NULL;

    // Path building logic

    return config;
}

After which we'll have to lay down our path building logic:

Before asprintf

if (access("tz.conf", F_OK) != -1) {
    config = strdup("tz.conf");
} else if (xdg_config_home) {
    size_t len = strlen(xdg_config_home) + strlen("twc") + strlen(file) + 3; 
    config = (char *)malloc(len);
    if (config != NULL) {
        snprintf(config, len, "%s/%s/%s", xdg_config_home, "twc", file);
    }
} else if (home) {
    size_t len = strlen(home) + strlen(".config/twc") + strlen(file) + 3; 
    config = (char *)malloc(len);
    if (config != NULL) {
        snprintf(config, len, "%s/%s/%s", home, ".config/twc", file);
    }
}

After asprintf

if (access("tz.conf", F_OK) != -1) {
    config = strdup("tz.conf");
} else if (xdg_config_home) {
    asprintf(&config, "%s/%s/%s", xdg_config_home, "twc", file);
} else if (home) {
    asprintf(&config, "%s/%s/%s", home, ".config/twc", file);
}

Testing

To run all the tests against asprintf execute the following command:

cc test.c asprintf.c -o test && ./test

To use your system's default asprintf(3), run this instead:

cc test.c -o test && ./test

To link it against other asprintf implementations, run:

cc test.c /path/to/asprintf.c -o test && ./test

Any of the above will output something like:

 Ok "Memory allocation"
 Ok "Empty string as input"
 Ok "String formatting variations"
 Ok "Special characters in format string"
Err "Boundary cases for integers"

// More tests

-----------
Passing: 4
 Failed: 1
  Total: 5

Docker

To compile the binary inside a Docker image, run:

docker build .

Compatibility

Runs on Linux, macOS and *BSD systems on both x86_64 and arm64. Builds with clang, gcc, tcc, zig and any other compiler that supports C99 or later.

Should be compatible with glibc, GLib, FreeBSD libc, musl libc asprintf.

Standards

asprintf is compatible with both POSIX.1-2017,1 and C99.2

License

asprintf is licensed under the terms of the MIT License.

See the LICENSE file for details.

See Also

Standards

System implementations

Source Docs
Debian asprintf manpages.debian.org/asprintf
FreeBSD asprintf man.freebsd.org/asprintf
OpenBSD asprintf man.openbsd.org/asprintf
NetBSD asprintf man.netbsd.org/asprintf.3
Solaris asprintf docs.oracle.com/asprintf-3c
z/OS asprintf ibm.com/docs/zos/asprintf

Other libc implementations

Other asprintf implementations

Further reading

Footnotes

  1. IEEE Std 1003.1-2017: Standard for Information Technology — Portable Operating System Interface (POSIX®),
    ISO/IEC/IEEE 9945:2009/COR 2:2017. URL: https://pubs.opengroup.org/onlinepubs/9699919799/

  2. ISO/IEC 9899: Standard for Information Technology — Programming languages — C, ISO/IEC 9899:2023.
    URL: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3096.pdf