root/maint/gnulib/lib/string-buffer.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sb_init
  2. sb_ensure_more_bytes
  3. sb_append
  4. sb_appendvf
  5. sb_appendf
  6. sb_free
  7. sb_dupfree

   1 /* A buffer that accumulates a string by piecewise concatenation.
   2    Copyright (C) 2021 Free Software Foundation, Inc.
   3 
   4    This file is free software: you can redistribute it and/or modify
   5    it under the terms of the GNU Lesser General Public License as
   6    published by the Free Software Foundation; either version 3 of the
   7    License, or (at your option) any later version.
   8 
   9    This file is distributed in the hope that it will be useful,
  10    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12    GNU Lesser General Public License for more details.
  13 
  14    You should have received a copy of the GNU Lesser General Public License
  15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
  16 
  17 /* Written by Bruno Haible <bruno@clisp.org>, 2021.  */
  18 
  19 #include <config.h>
  20 
  21 /* Specification.  */
  22 #include "string-buffer.h"
  23 
  24 #include <stdarg.h>
  25 #include <stdio.h>
  26 #include <stdlib.h>
  27 #include <string.h>
  28 
  29 void
  30 sb_init (struct string_buffer *buffer)
     /* [previous][next][first][last][top][bottom][index][help] */
  31 {
  32   buffer->data = buffer->space;
  33   buffer->length = 0;
  34   buffer->allocated = sizeof (buffer->space);
  35   buffer->error = false;
  36 }
  37 
  38 /* Ensures that INCREMENT bytes are available beyond the current used length
  39    of BUFFER.
  40    Returns 0, or -1 in case of out-of-memory error.  */
  41 static int
  42 sb_ensure_more_bytes (struct string_buffer *buffer, size_t increment)
     /* [previous][next][first][last][top][bottom][index][help] */
  43 {
  44   size_t incremented_length = buffer->length + increment;
  45   if (incremented_length < increment)
  46     /* Overflow.  */
  47     return -1;
  48 
  49   if (buffer->allocated < incremented_length)
  50     {
  51       size_t new_allocated = 2 * buffer->allocated;
  52       if (new_allocated < buffer->allocated)
  53         /* Overflow.  */
  54         return -1;
  55       if (new_allocated < incremented_length)
  56         new_allocated = incremented_length;
  57 
  58       char *new_data;
  59       if (buffer->data == buffer->space)
  60         {
  61           new_data = (char *) malloc (new_allocated);
  62           if (new_data == NULL)
  63             /* Out-of-memory.  */
  64             return -1;
  65           memcpy (new_data, buffer->data, buffer->length);
  66         }
  67       else
  68         {
  69           new_data = (char *) realloc (buffer->data, new_allocated);
  70           if (new_data == NULL)
  71             /* Out-of-memory.  */
  72             return -1;
  73         }
  74       buffer->data = new_data;
  75       buffer->allocated = new_allocated;
  76     }
  77   return 0;
  78 }
  79 
  80 int
  81 sb_append (struct string_buffer *buffer, const char *str)
     /* [previous][next][first][last][top][bottom][index][help] */
  82 {
  83   size_t len = strlen (str);
  84   if (sb_ensure_more_bytes (buffer, len) < 0)
  85     {
  86       buffer->error = true;
  87       return -1;
  88     }
  89   memcpy (buffer->data + buffer->length, str, len);
  90   buffer->length += len;
  91   return 0;
  92 }
  93 
  94 int
  95 sb_appendvf (struct string_buffer *buffer, const char *formatstring,
     /* [previous][next][first][last][top][bottom][index][help] */
  96              va_list list)
  97 {
  98   va_list list_copy;
  99 
 100   /* Make a bit of room, so that the probability that the first vsnprintf() call
 101      succeeds is high.  */
 102   size_t room = buffer->allocated - buffer->length;
 103   if (room < 64)
 104     {
 105       if (sb_ensure_more_bytes (buffer, 64) < 0)
 106         {
 107           buffer->error = true;
 108           return -1;
 109         }
 110       room = buffer->allocated - buffer->length;
 111     }
 112 
 113   va_copy (list_copy, list);
 114 
 115   /* First vsnprintf() call.  */
 116   int ret = vsnprintf (buffer->data + buffer->length, room, formatstring, list);
 117   if (ret < 0)
 118     {
 119       /* Failed.  */
 120       buffer->error = true;
 121       ret = -1;
 122     }
 123   else
 124     {
 125       if ((size_t) ret <= room)
 126         {
 127           /* The result has fit into room bytes.  */
 128           buffer->length += (size_t) ret;
 129           ret = 0;
 130         }
 131       else
 132         {
 133           /* The result was truncated.  Make more room, for a second vsnprintf()
 134              call.  */
 135           if (sb_ensure_more_bytes (buffer, (size_t) ret) < 0)
 136             {
 137               buffer->error = true;
 138               ret = -1;
 139             }
 140           else
 141             {
 142               /* Second vsnprintf() call.  */
 143               room = buffer->allocated - buffer->length;
 144               ret = vsnprintf (buffer->data + buffer->length, room,
 145                                formatstring, list_copy);
 146               if (ret < 0)
 147                 {
 148                   /* Failed.  */
 149                   buffer->error = true;
 150                   ret = -1;
 151                 }
 152               else
 153                 {
 154                   if ((size_t) ret <= room)
 155                     {
 156                       /* The result has fit into room bytes.  */
 157                       buffer->length += (size_t) ret;
 158                       ret = 0;
 159                     }
 160                   else
 161                     /* The return values of the vsnprintf() calls are not
 162                        consistent.  */
 163                     abort ();
 164                 }
 165             }
 166         }
 167     }
 168 
 169   va_end (list_copy);
 170   return ret;
 171 }
 172 
 173 int
 174 sb_appendf (struct string_buffer *buffer, const char *formatstring, ...)
     /* [previous][next][first][last][top][bottom][index][help] */
 175 {
 176   va_list args;
 177 
 178   /* Make a bit of room, so that the probability that the first vsnprintf() call
 179      succeeds is high.  */
 180   size_t room = buffer->allocated - buffer->length;
 181   if (room < 64)
 182     {
 183       if (sb_ensure_more_bytes (buffer, 64) < 0)
 184         {
 185           buffer->error = true;
 186           return -1;
 187         }
 188       room = buffer->allocated - buffer->length;
 189     }
 190 
 191   va_start (args, formatstring);
 192 
 193   /* First vsnprintf() call.  */
 194   int ret = vsnprintf (buffer->data + buffer->length, room, formatstring, args);
 195   if (ret < 0)
 196     {
 197       /* Failed.  */
 198       buffer->error = true;
 199       ret = -1;
 200     }
 201   else
 202     {
 203       if ((size_t) ret <= room)
 204         {
 205           /* The result has fit into room bytes.  */
 206           buffer->length += (size_t) ret;
 207           ret = 0;
 208         }
 209       else
 210         {
 211           /* The result was truncated.  Make more room, for a second vsnprintf()
 212              call.  */
 213           if (sb_ensure_more_bytes (buffer, (size_t) ret) < 0)
 214             {
 215               buffer->error = true;
 216               ret = -1;
 217             }
 218           else
 219             {
 220               /* Second vsnprintf() call.  */
 221               room = buffer->allocated - buffer->length;
 222               va_end (args);
 223               va_start (args, formatstring);
 224               ret = vsnprintf (buffer->data + buffer->length, room,
 225                                formatstring, args);
 226               if (ret < 0)
 227                 {
 228                   /* Failed.  */
 229                   buffer->error = true;
 230                   ret = -1;
 231                 }
 232               else
 233                 {
 234                   if ((size_t) ret <= room)
 235                     {
 236                       /* The result has fit into room bytes.  */
 237                       buffer->length += (size_t) ret;
 238                       ret = 0;
 239                     }
 240                   else
 241                     /* The return values of the vsnprintf() calls are not
 242                        consistent.  */
 243                     abort ();
 244                 }
 245             }
 246         }
 247     }
 248 
 249   va_end (args);
 250   return ret;
 251 }
 252 
 253 void
 254 sb_free (struct string_buffer *buffer)
     /* [previous][next][first][last][top][bottom][index][help] */
 255 {
 256   if (buffer->data != buffer->space)
 257     free (buffer->data);
 258 }
 259 
 260 /* Returns the contents of BUFFER, and frees all other memory held
 261    by BUFFER.  Returns NULL upon failure or if there was an error earlier.  */
 262 char *
 263 sb_dupfree (struct string_buffer *buffer)
     /* [previous][next][first][last][top][bottom][index][help] */
 264 {
 265   if (buffer->error)
 266     goto fail;
 267 
 268   if (sb_ensure_more_bytes (buffer, 1) < 0)
 269     goto fail;
 270   buffer->data[buffer->length] = '\0';
 271   buffer->length++;
 272 
 273   if (buffer->data == buffer->space)
 274     {
 275       char *copy = (char *) malloc (buffer->length);
 276       if (copy == NULL)
 277         goto fail;
 278       memcpy (copy, buffer->data, buffer->length);
 279       return copy;
 280     }
 281   else
 282     {
 283       /* Shrink the string before returning it.  */
 284       char *contents = buffer->data;
 285       if (buffer->length < buffer->allocated)
 286         {
 287           contents = realloc (contents, buffer->length);
 288           if (contents == NULL)
 289             goto fail;
 290         }
 291       return contents;
 292     }
 293 
 294  fail:
 295   sb_free (buffer);
 296   return NULL;
 297 }

/* [previous][next][first][last][top][bottom][index][help] */