xref: /netbsd-src/external/gpl3/gcc/dist/libstdc++-v3/src/c++11/snprintf_lite.cc (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1 // Debugging support -*- C++ -*-
2 
3 // Copyright (C) 2013-2022 Free Software Foundation, Inc.
4 //
5 // This file is part of GCC.
6 //
7 // GCC is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 3, or (at your option)
10 // any later version.
11 //
12 // GCC is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 // GNU General Public License for more details.
16 //
17 // Under Section 7 of GPL version 3, you are granted additional
18 // permissions described in the GCC Runtime Library Exception, version
19 // 3.1, as published by the Free Software Foundation.
20 
21 // You should have received a copy of the GNU General Public License and
22 // a copy of the GCC Runtime Library Exception along with this program;
23 // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
24 // <http://www.gnu.org/licenses/>.
25 
26 #include <stdarg.h>
27 #include <stddef.h>
28 #include <bits/functexcept.h>
29 
30 namespace __gnu_cxx {
31 
32   // Private helper to throw logic error if snprintf_lite runs out
33   // of space (which is not expected to ever happen).
34   // NUL-terminates __buf.
35   void
36   __throw_insufficient_space(const char *__buf, const char *__bufend)
37     __attribute__((__noreturn__));
38 
39   void
__throw_insufficient_space(const char * __buf,const char * __bufend)40   __throw_insufficient_space(const char *__buf, const char *__bufend)
41   {
42     // Include space for trailing NUL.
43     const size_t __len = __bufend - __buf + 1;
44 
45     const char __err[] = "not enough space for format expansion "
46       "(Please submit full bug report at https://gcc.gnu.org/bugs/):\n    ";
47     const size_t __errlen = sizeof(__err) - 1;
48 
49     char *const __e
50       = static_cast<char*>(__builtin_alloca(__errlen + __len));
51 
52     __builtin_memcpy(__e, __err, __errlen);
53     __builtin_memcpy(__e + __errlen, __buf, __len - 1);
54     __e[__errlen + __len - 1] = '\0';
55     std::__throw_logic_error(__e);
56   }
57 
58 
59   // Private routine to append decimal representation of VAL to the given
60   // BUFFER, but not more than BUFSIZE characters.
61   // Does not NUL-terminate the output buffer.
62   // Returns number of characters appended, or -1 if BUFSIZE is too small.
__concat_size_t(char * __buf,size_t __bufsize,size_t __val)63   int __concat_size_t(char *__buf, size_t __bufsize, size_t __val)
64   {
65     // Long enough for decimal representation.
66     int __ilen = 3 * sizeof(__val);
67     char *__cs = static_cast<char*>(__builtin_alloca(__ilen));
68     char* __out = __cs + __ilen;
69     do
70       {
71 	*--__out = "0123456789"[__val % 10];
72 	__val /= 10;
73       }
74     while (__val != 0);
75     size_t __len = __cs + __ilen - __out;
76     if (__bufsize < __len)
77       return -1;
78 
79     __builtin_memcpy(__buf, __cs + __ilen - __len, __len);
80     return __len;
81   }
82 
83 
84   // Private routine to print into __buf arguments according to format,
85   // not to exceed __bufsize.
86   // Only '%%', '%s' and '%zu' format specifiers are understood.
87   // Returns number of characters printed (excluding terminating NUL).
88   // Always NUL-terminates __buf.
89   // Throws logic_error on insufficient space.
__snprintf_lite(char * __buf,size_t __bufsize,const char * __fmt,va_list __ap)90   int __snprintf_lite(char *__buf, size_t __bufsize, const char *__fmt,
91 		      va_list __ap)
92   {
93     char *__d = __buf;
94     const char *__s = __fmt;
95     const char *const __limit = __d + __bufsize - 1;  // Leave space for NUL.
96 
97     while (__s[0] != '\0' && __d < __limit)
98       {
99 	if (__s[0] == '%')
100 	  switch (__s[1])
101 	    {
102 	    default:  // Stray '%'. Just print it.
103 	      break;
104 	    case '%':  // '%%'
105 	      __s += 1;
106 	      break;
107 	    case 's':  // '%s'.
108 	      {
109 		const char *__v = va_arg(__ap, const char *);
110 
111 		while (__v[0] != '\0' && __d < __limit)
112 		  *__d++ = *__v++;
113 
114 		if (__v[0] != '\0')
115 		  // Not enough space for __fmt expansion.
116 		  __throw_insufficient_space(__buf, __d);
117 
118 		__s += 2;  // Step over %s.
119 		continue;
120 	      }
121 	      break;
122 	    case 'z':
123 	      if (__s[2] == 'u')  // '%zu' -- expand next size_t arg.
124 		{
125 		  const int __len = __concat_size_t(__d, __limit - __d,
126 						    va_arg(__ap, size_t));
127 		  if (__len > 0)
128 		    __d += __len;
129 		  else
130 		    // Not enough space for __fmt expansion.
131 		    __throw_insufficient_space(__buf, __d);
132 
133 		  __s += 3;  // Step over %zu
134 		  continue;
135 		}
136 	      // Stray '%zX'. Just print it.
137 	      break;
138 	    }
139 	*__d++ = *__s++;
140       }
141 
142     if (__s[0] != '\0')
143       // Not enough space for __fmt expansion.
144       __throw_insufficient_space(__buf, __d);
145 
146     *__d = '\0';
147     return __d - __buf;
148   }
149 
150 }  // __gnu_cxx
151