b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter/*
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter * This file originated in the realmd project
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter *
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter * Copyright 2013 Red Hat Inc
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter *
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter * This program is free software: you can redistribute it and/or modify
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter * it under the terms of the GNU Lesser General Public License as published
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter * by the Free Software Foundation; either version 2 of the licence or (at
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter * your option) any later version.
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter *
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter * See the included COPYING file for more information.
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter *
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter * Author: Stef Walter <stefw@redhat.com>
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter */
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter/*
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter * Some snippets of code from gnulib, but have since been refactored
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter * to within an inch of their life...
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter *
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter * vsprintf with automatic memory allocation.
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter * Copyright (C) 1999, 2002-2003 Free Software Foundation, Inc.
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter *
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter * This program is free software; you can redistribute it and/or modify it
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter * under the terms of the GNU Library General Public License as published
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter * by the Free Software Foundation; either version 2, or (at your option)
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter * any later version.
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter *
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter * This program is distributed in the hope that it will be useful,
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter * but WITHOUT ANY WARRANTY; without even the implied warranty of
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter * Library General Public License for more details.
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter */
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter#include "config.h"
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter#include "safe-format-string.h"
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter#include <errno.h>
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter#include <stdarg.h>
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter#include <string.h>
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter#ifndef MIN
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter#define MIN(a, b) (((a) < (b)) ? (a) : (b))
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter#endif
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter#ifndef MAX
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter#define MAX(a, b) (((a) > (b)) ? (a) : (b))
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter#endif
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walterstatic void
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Waltersafe_padding (int count,
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter int *total,
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter void (* copy_fn) (void *, const char *, size_t),
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter void *data)
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter{
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter char eight[] = " ";
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter int num;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter while (count > 0) {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter num = MIN (count, 8);
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter copy_fn (data, eight, num);
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter count -= num;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter *total += num;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter }
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter}
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walterstatic void
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walterdummy_copy_fn (void *data,
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter const char *piece,
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter size_t len)
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter{
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter}
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walterint
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Waltersafe_format_string_cb (void (* copy_fn) (void *, const char *, size_t),
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter void *data,
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter const char *format,
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter const char * const args[],
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter int num_args)
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter{
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter int at_arg = 0;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter const char *cp;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter int precision;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter int width;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter int len;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter const char *value;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter int total;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter int left;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter int i;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter if (!copy_fn)
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter copy_fn = dummy_copy_fn;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter total = 0;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter cp = format;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter while (*cp) {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter /* Piece of raw string */
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter if (*cp != '%') {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter len = strcspn (cp, "%");
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter copy_fn (data, cp, len);
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter total += len;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter cp += len;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter continue;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter }
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter cp++;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter /* An literal percent sign? */
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter if (*cp == '%') {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter copy_fn (data, "%", 1);
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter total++;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter cp++;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter continue;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter }
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter value = NULL;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter left = 0;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter precision = -1;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter width = -1;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter /* Test for positional argument. */
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter if (*cp >= '0' && *cp <= '9') {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter /* Look-ahead parsing, otherwise skipped */
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter if (cp[strspn (cp, "0123456789")] == '$') {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter unsigned int n = 0;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter for (i = 0; i < 6 && *cp >= '0' && *cp <= '9'; i++, cp++) {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter n = 10 * n + (*cp - '0');
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter }
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter /* Positional argument 0 is invalid. */
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter if (n == 0) {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter errno = EINVAL;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter return -1;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter }
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter /* Positional argument N too high */
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter if (n > num_args) {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter errno = EINVAL;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter return -1;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter }
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter value = args[n - 1];
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter cp++; /* $ */
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter }
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter }
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter /* Read the supported flags. */
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter for (; ; cp++) {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter if (*cp == '-')
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter left = 1;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter /* Supported but ignored */
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter else if (*cp != ' ')
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter break;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter }
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter /* Parse the width. */
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter if (*cp >= '0' && *cp <= '9') {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter width = 0;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter for (i = 0; i < 6 && *cp >= '0' && *cp <= '9'; i++, cp++) {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter width = 10 * width + (*cp - '0');
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter }
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter }
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter /* Parse the precision. */
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter if (*cp == '.') {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter precision = 0;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter for (i = 0, cp++; i < 6 && *cp >= '0' && *cp <= '9'; cp++, i++) {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter precision = 10 * precision + (*cp - '0');
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter }
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter }
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter /* Read the conversion character. */
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter switch (*cp++) {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter case 's':
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter /* Non-positional argument */
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter if (value == NULL) {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter /* Too many arguments used */
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter if (at_arg == num_args) {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter errno = EINVAL;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter return -1;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter }
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter value = args[at_arg++];
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter }
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter break;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter /* No other conversion characters are supported */
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter default:
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter errno = EINVAL;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter return -1;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter }
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter /* How many characters are we printing? */
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter len = strlen (value);
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter if (precision >= 0)
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter len = MIN (precision, len);
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter /* Do we need padding? */
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter safe_padding (left ? 0 : width - len, &total, copy_fn, data);
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter /* The actual data */;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter copy_fn (data, value, len);
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter total += len;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter /* Do we need padding? */
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter safe_padding (left ? width - len : 0, &total, copy_fn, data);
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter }
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter return total;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter}
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walterstatic const char **
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Waltervalist_to_args (va_list va,
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter int *num_args)
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter{
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter int alo_args;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter const char **args;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter const char *arg;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter void *mem;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter *num_args = alo_args = 0;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter args = NULL;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter for (;;) {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter arg = va_arg (va, const char *);
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter if (arg == NULL)
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter break;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter if (*num_args == alo_args) {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter alo_args += 8;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter mem = realloc (args, sizeof (const char *) * alo_args);
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter if (!mem) {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter free (args);
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter return NULL;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter }
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter args = mem;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter }
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter args[(*num_args)++] = arg;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter }
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter return args;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter}
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walterstruct sprintf_ctx {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter char *data;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter size_t length;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter size_t alloc;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter};
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walterstatic void
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Waltersnprintf_copy_fn (void *data,
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter const char *piece,
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter size_t length)
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter{
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter struct sprintf_ctx *cx = data;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter /* Don't copy if too much data */
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter if (cx->length > cx->alloc)
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter length = 0;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter else if (cx->length + length > cx->alloc)
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter length = cx->alloc - cx->length;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter if (length > 0)
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter memcpy (cx->data + cx->length, piece, length);
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter /* Null termination happens later */
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter cx->length += length;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter}
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walterint
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Waltersafe_format_string (char *str,
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter size_t len,
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter const char *format,
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter ...)
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter{
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter struct sprintf_ctx cx;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter int num_args;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter va_list va;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter const char **args;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter int error = 0;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter int ret;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter cx.data = str;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter cx.length = 0;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter cx.alloc = len;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter va_start (va, format);
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter args = valist_to_args (va, &num_args);
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter va_end (va);
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter if (args == NULL) {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter errno = ENOMEM;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter return -1;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter }
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter if (len)
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter cx.data[0] = '\0';
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter ret = safe_format_string_cb (snprintf_copy_fn, &cx, format, args, num_args);
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter if (ret < 0) {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter error = errno;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter } else if (len > 0) {
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter cx.data[MIN (cx.length, len - 1)] = '\0';
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter }
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter free (args);
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter if (error)
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter errno = error;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter return ret;
b9d8c6172e48a2633ebe196b2e88bebdf9523c20Stef Walter}