1 /* vasprintf and asprintf with out-of-memory checking.
2    Copyright (C) 1999, 2002-2004, 2006-2021 Free Software Foundation, Inc.
3 
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8 
9    This program 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 General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
16 
17 #include <config.h>
18 
19 /* Specification.  */
20 #include "xvasprintf.h"
21 
22 #include <errno.h>
23 #include <limits.h>
24 #include <string.h>
25 #include <stdio.h>
26 
27 #include "xalloc.h"
28 
29 /* Checked size_t computations.  */
30 #include "xsize.h"
31 
32 static char *
xstrcat(size_t argcount,va_list args)33 xstrcat (size_t argcount, va_list args)
34 {
35   char *result;
36   va_list ap;
37   size_t totalsize;
38   size_t i;
39   char *p;
40 
41   /* Determine the total size.  */
42   totalsize = 0;
43   va_copy (ap, args);
44   for (i = argcount; i > 0; i--)
45     {
46       const char *next = va_arg (ap, const char *);
47       totalsize = xsum (totalsize, strlen (next));
48     }
49   va_end (ap);
50 
51   /* Test for overflow in the summing pass above or in (totalsize + 1) below.
52      Also, don't return a string longer than INT_MAX, for consistency with
53      vasprintf().  */
54   if (totalsize == SIZE_MAX || totalsize > INT_MAX)
55     {
56       errno = EOVERFLOW;
57       return NULL;
58     }
59 
60   /* Allocate and fill the result string.  */
61   result = XNMALLOC (totalsize + 1, char);
62   p = result;
63   for (i = argcount; i > 0; i--)
64     {
65       const char *next = va_arg (args, const char *);
66       size_t len = strlen (next);
67       memcpy (p, next, len);
68       p += len;
69     }
70   *p = '\0';
71 
72   return result;
73 }
74 
75 char *
xvasprintf(const char * format,va_list args)76 xvasprintf (const char *format, va_list args)
77 {
78   char *result;
79 
80   /* Recognize the special case format = "%s...%s".  It is a frequently used
81      idiom for string concatenation and needs to be fast.  We don't want to
82      have a separate function xstrcat() for this purpose.  */
83   {
84     size_t argcount = 0;
85     const char *f;
86 
87     for (f = format;;)
88       {
89         if (*f == '\0')
90           /* Recognized the special case of string concatenation.  */
91           return xstrcat (argcount, args);
92         if (*f != '%')
93           break;
94         f++;
95         if (*f != 's')
96           break;
97         f++;
98         argcount++;
99       }
100   }
101 
102   if (vasprintf (&result, format, args) < 0)
103     {
104       if (errno == ENOMEM)
105         xalloc_die ();
106       return NULL;
107     }
108 
109   return result;
110 }
111