LCOV - code coverage report
Current view: top level - util - safe-format-string.c (source / functions) Hit Total Coverage
Test: .coverage.total Lines: 117 125 93.6 %
Date: 2015-10-19 Functions: 5 6 83.3 %

          Line data    Source code
       1             : /*
       2             :  * This file originated in the realmd project
       3             :  *
       4             :  * Copyright 2013 Red Hat Inc
       5             :  *
       6             :  * This program is free software: you can redistribute it and/or modify
       7             :  * it under the terms of the GNU Lesser General Public License as published
       8             :  * by the Free Software Foundation; either version 2 of the licence or (at
       9             :  * your option) any later version.
      10             :  *
      11             :  * See the included COPYING file for more information.
      12             :  *
      13             :  * Author: Stef Walter <stefw@redhat.com>
      14             :  */
      15             : 
      16             : /*
      17             :  * Some snippets of code from gnulib, but have since been refactored
      18             :  * to within an inch of their life...
      19             :  *
      20             :  * vsprintf with automatic memory allocation.
      21             :  * Copyright (C) 1999, 2002-2003 Free Software Foundation, Inc.
      22             :  *
      23             :  * This program is free software; you can redistribute it and/or modify it
      24             :  * under the terms of the GNU Library General Public License as published
      25             :  * by the Free Software Foundation; either version 2, or (at your option)
      26             :  * any later version.
      27             :  *
      28             :  * This program is distributed in the hope that it will be useful,
      29             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      30             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      31             :  * Library General Public License for more details.
      32             :  */
      33             : 
      34             : #include "config.h"
      35             : 
      36             : #include "safe-format-string.h"
      37             : 
      38             : #include <errno.h>
      39             : #include <stdarg.h>
      40             : #include <string.h>
      41             : 
      42             : #ifndef MIN
      43             : #define MIN(a, b)  (((a) < (b)) ? (a) : (b))
      44             : #endif
      45             : 
      46             : #ifndef MAX
      47             : #define MAX(a, b)  (((a) > (b)) ? (a) : (b))
      48             : #endif
      49             : 
      50             : static void
      51        1622 : safe_padding (int count,
      52             :               int *total,
      53             :               void (* copy_fn) (void *, const char *, size_t),
      54             :               void *data)
      55             : {
      56        1622 :     char eight[] = "        ";
      57             :     int num;
      58             : 
      59        3248 :     while (count > 0) {
      60           4 :         num = MIN (count, 8);
      61           4 :         copy_fn (data, eight, num);
      62           4 :         count -= num;
      63           4 :         *total += num;
      64             :     }
      65        1622 : }
      66             : 
      67             : static void
      68           0 : dummy_copy_fn (void *data,
      69             :                const char *piece,
      70             :                size_t len)
      71             : {
      72             : 
      73           0 : }
      74             : 
      75             : int
      76         417 : safe_format_string_cb (void (* copy_fn) (void *, const char *, size_t),
      77             :                        void *data,
      78             :                        const char *format,
      79             :                        const char * const args[],
      80             :                        int num_args)
      81             : {
      82         417 :     int at_arg = 0;
      83             :     const char *cp;
      84             :     int precision;
      85             :     int width;
      86             :     int len;
      87             :     const char *value;
      88             :     int total;
      89             :     int left;
      90             :     int i;
      91             : 
      92         417 :     if (!copy_fn)
      93           0 :         copy_fn = dummy_copy_fn;
      94             : 
      95         417 :     total = 0;
      96         417 :     cp = format;
      97             : 
      98        2072 :     while (*cp) {
      99             : 
     100             :         /* Piece of raw string */
     101        1243 :         if (*cp != '%') {
     102         426 :             len = strcspn (cp, "%");
     103         426 :             copy_fn (data, cp, len);
     104         426 :             total += len;
     105         426 :             cp += len;
     106         426 :             continue;
     107             :         }
     108             : 
     109         817 :         cp++;
     110             : 
     111             :         /* An literal percent sign? */
     112         817 :         if (*cp == '%') {
     113           1 :             copy_fn (data, "%", 1);
     114           1 :             total++;
     115           1 :             cp++;
     116           1 :             continue;
     117             :         }
     118             : 
     119         816 :         value = NULL;
     120         816 :         left = 0;
     121         816 :         precision = -1;
     122         816 :         width = -1;
     123             : 
     124             :         /* Test for positional argument.  */
     125         816 :         if (*cp >= '0' && *cp <= '9') {
     126             :             /* Look-ahead parsing, otherwise skipped */
     127         798 :             if (cp[strspn (cp, "0123456789")] == '$') {
     128         796 :                 unsigned int n = 0;
     129        1593 :                 for (i = 0; i < 6 && *cp >= '0' && *cp <= '9'; i++, cp++) {
     130         797 :                     n = 10 * n + (*cp - '0');
     131             :                 }
     132             :                 /* Positional argument 0 is invalid. */
     133         796 :                 if (n == 0) {
     134           1 :                     errno = EINVAL;
     135           1 :                     return -1;
     136             :                 }
     137             :                 /* Positional argument N too high */
     138         795 :                 if (n > num_args) {
     139           1 :                     errno = EINVAL;
     140           1 :                     return -1;
     141             :                 }
     142         794 :                 value = args[n - 1];
     143         794 :                 cp++; /* $ */
     144             :             }
     145             :         }
     146             : 
     147             :         /* Read the supported flags. */
     148           4 :         for (; ; cp++) {
     149         818 :             if (*cp == '-')
     150           2 :                 left = 1;
     151             :             /* Supported but ignored */
     152         816 :             else if (*cp != ' ')
     153         814 :                 break;
     154           4 :         }
     155             : 
     156             :         /* Parse the width. */
     157         814 :         if (*cp >= '0' && *cp <= '9') {
     158           5 :             width = 0;
     159          15 :             for (i = 0; i < 6 && *cp >= '0' && *cp <= '9'; i++, cp++) {
     160          10 :                 width = 10 * width + (*cp - '0');
     161             :             }
     162             :         }
     163             : 
     164             :         /* Parse the precision. */
     165         814 :         if (*cp == '.') {
     166           3 :             precision = 0;
     167           6 :             for (i = 0, cp++; i < 6 && *cp >= '0' && *cp <= '9'; cp++, i++) {
     168           3 :                 precision = 10 * precision + (*cp - '0');
     169             :             }
     170             :         }
     171             : 
     172             :         /* Read the conversion character.  */
     173         814 :         switch (*cp++) {
     174             :         case 's':
     175             :             /* Non-positional argument */
     176         812 :             if (value == NULL) {
     177             :                 /* Too many arguments used */
     178          18 :                 if (at_arg == num_args) {
     179           1 :                     errno = EINVAL;
     180           1 :                     return -1;
     181             :                 }
     182          17 :                 value = args[at_arg++];
     183             :             }
     184         811 :             break;
     185             : 
     186             :         /* No other conversion characters are supported */
     187             :         default:
     188           2 :             errno = EINVAL;
     189           2 :             return -1;
     190             :         }
     191             : 
     192             :         /* How many characters are we printing? */
     193         811 :         len = strlen (value);
     194         811 :         if (precision >= 0)
     195           3 :             len = MIN (precision, len);
     196             : 
     197             :         /* Do we need padding? */
     198         811 :         safe_padding (left ? 0 : width - len, &total, copy_fn, data);
     199             : 
     200             :         /* The actual data */;
     201         811 :         copy_fn (data, value, len);
     202         811 :         total += len;
     203             : 
     204             :         /* Do we need padding? */
     205         811 :         safe_padding (left ? width - len : 0, &total, copy_fn, data);
     206             :     }
     207             : 
     208         412 :     return total;
     209             : }
     210             : 
     211             : static const char **
     212          27 : valist_to_args (va_list va,
     213             :                 int *num_args)
     214             : {
     215             :     int alo_args;
     216             :     const char **args;
     217             :     const char *arg;
     218             :     void *mem;
     219             : 
     220          27 :     *num_args = alo_args = 0;
     221          27 :     args = NULL;
     222             : 
     223             :     for (;;) {
     224         101 :         arg = va_arg (va, const char *);
     225         101 :         if (arg == NULL)
     226          27 :             break;
     227          74 :         if (*num_args == alo_args) {
     228          26 :             alo_args += 8;
     229          26 :             mem = realloc (args, sizeof (const char *) * alo_args);
     230          26 :             if (!mem) {
     231           0 :                 free (args);
     232           0 :                 return NULL;
     233             :             }
     234          26 :             args = mem;
     235             :         }
     236          74 :         args[(*num_args)++] = arg;
     237          74 :     }
     238             : 
     239          27 :     return args;
     240             : }
     241             : 
     242             : struct sprintf_ctx {
     243             :     char *data;
     244             :     size_t length;
     245             :     size_t alloc;
     246             : };
     247             : 
     248             : static void
     249          79 : snprintf_copy_fn (void *data,
     250             :                   const char *piece,
     251             :                   size_t length)
     252             : {
     253          79 :     struct sprintf_ctx *cx = data;
     254             : 
     255             :     /* Don't copy if too much data */
     256          79 :     if (cx->length > cx->alloc)
     257           0 :         length = 0;
     258          79 :     else if (cx->length + length > cx->alloc)
     259          36 :         length = cx->alloc - cx->length;
     260             : 
     261          79 :     if (length > 0)
     262          44 :         memcpy (cx->data + cx->length, piece, length);
     263             : 
     264             :     /* Null termination happens later */
     265          79 :     cx->length += length;
     266          79 : }
     267             : 
     268             : int
     269          27 : safe_format_string (char *str,
     270             :                     size_t len,
     271             :                     const char *format,
     272             :                     ...)
     273             : {
     274             :     struct sprintf_ctx cx;
     275             :     int num_args;
     276             :     va_list va;
     277             :     const char **args;
     278          27 :     int error = 0;
     279             :     int ret;
     280             : 
     281          27 :     cx.data = str;
     282          27 :     cx.length = 0;
     283          27 :     cx.alloc = len;
     284             : 
     285          27 :     va_start (va, format);
     286          27 :     args = valist_to_args (va, &num_args);
     287          27 :     va_end (va);
     288             : 
     289          27 :     if (args == NULL) {
     290           1 :         errno = ENOMEM;
     291           1 :         return -1;
     292             :     }
     293             : 
     294          26 :     if (len)
     295          16 :         cx.data[0] = '\0';
     296             : 
     297          26 :     ret = safe_format_string_cb (snprintf_copy_fn, &cx, format, args, num_args);
     298          26 :     if (ret < 0) {
     299           0 :         error = errno;
     300          26 :     } else if (len > 0) {
     301          16 :         cx.data[MIN (cx.length, len - 1)] = '\0';
     302             :     }
     303             : 
     304          26 :     free (args);
     305             : 
     306          26 :     if (error)
     307           0 :         errno = error;
     308          26 :     return ret;
     309             : }

Generated by: LCOV version 1.10