1 /* Copyright (C) 2004       Manuel Novoa III    <mjn3@codepoet.org>
2  *
3  * GNU Library General Public License (LGPL) version 2 or later.
4  *
5  * Dedicated to Toni.  See uClibc/DEDICATION.mjn3 for details.
6  */
7 
8 #include "_stdio.h"
9 #include <stdarg.h>
10 
11 #ifdef __STDIO_BUFFERS
12 /* NB: we can still have __USE_OLD_VFPRINTF__ defined in this case! */
13 
vsnprintf(char * __restrict buf,size_t size,const char * __restrict format,va_list arg)14 int vsnprintf(char *__restrict buf, size_t size,
15 			  const char * __restrict format, va_list arg)
16 {
17 	FILE f;
18 	int rv;
19 
20 	f.__filedes = __STDIO_STREAM_FAKE_VSNPRINTF_FILEDES;
21 	f.__modeflags = (__FLAG_NARROW|__FLAG_WRITEONLY|__FLAG_WRITING);
22 
23 #ifdef __UCLIBC_HAS_WCHAR__
24 	f.__ungot_width[0] = 0;
25 #endif /* __UCLIBC_HAS_WCHAR__ */
26 #ifdef __STDIO_MBSTATE
27 	__INIT_MBSTATE(&(f.__state));
28 #endif /* __STDIO_MBSTATE */
29 
30 #if (defined(__STDIO_BUFFERS) || defined(__USE_OLD_VFPRINTF__)) && defined(__UCLIBC_HAS_THREADS__)
31 	f.__user_locking = 1;		/* Set user locking. */
32 	STDIO_INIT_MUTEX(f.__lock);
33 #endif
34 	f.__nextopen = NULL;
35 
36 	if (size > SIZE_MAX - (size_t) buf) {
37 		size = SIZE_MAX - (size_t) buf;
38 	}
39 
40 /* TODO: this comment seems to be wrong */
41 	/* Set these last since __bufputc initialization depends on
42 	 * __user_locking and only gets set if user locking is on. */
43 	f.__bufstart = (unsigned char *) buf;
44 	f.__bufend = (unsigned char *) buf + size;
45 	__STDIO_STREAM_INIT_BUFREAD_BUFPOS(&f);
46 	__STDIO_STREAM_DISABLE_GETC(&f);
47 	__STDIO_STREAM_ENABLE_PUTC(&f);
48 
49 #ifdef __USE_OLD_VFPRINTF__
50 	rv = vfprintf(&f, format, arg);
51 #else
52 	rv = _vfprintf_internal(&f, format, arg);
53 #endif
54 	if (size) {
55 		if (f.__bufpos == f.__bufend) {
56 			--f.__bufpos;
57 		}
58 		*f.__bufpos = 0;
59 	}
60 	return rv;
61 }
62 libc_hidden_def(vsnprintf)
63 
64 #elif defined(__USE_OLD_VFPRINTF__)
65 
66 typedef struct {
67 	FILE f;
68 	unsigned char *bufend;		/* pointer to 1 past end of buffer */
69 	unsigned char *bufpos;
70 } __FILE_vsnprintf;
71 
72 int vsnprintf(char *__restrict buf, size_t size,
73 			  const char * __restrict format, va_list arg)
74 {
75 	__FILE_vsnprintf f;
76 	int rv;
77 
78 	f.bufpos = buf;
79 
80 	if (size > SIZE_MAX - (size_t) buf) {
81 		size = SIZE_MAX - (size_t) buf;
82 	}
83 	f.bufend = buf + size;
84 
85 	f.f.__filedes = __STDIO_STREAM_FAKE_VSNPRINTF_FILEDES_NB;
86 	f.f.__modeflags = (__FLAG_NARROW|__FLAG_WRITEONLY|__FLAG_WRITING);
87 
88 #ifdef __UCLIBC_HAS_WCHAR__
89 	f.f.__ungot_width[0] = 0;
90 #endif /* __UCLIBC_HAS_WCHAR__ */
91 #ifdef __STDIO_MBSTATE
92 	__INIT_MBSTATE(&(f.f.__state));
93 #endif /* __STDIO_MBSTATE */
94 
95 #ifdef __UCLIBC_HAS_THREADS__
96 	f.f.__user_locking = 1;		/* Set user locking. */
97 	STDIO_INIT_MUTEX(f.f.__lock);
98 #endif
99 	f.f.__nextopen = NULL;
100 
101 	rv = vfprintf((FILE *) &f, format, arg);
102 	if (size) {
103 		if (f.bufpos == f.bufend) {
104 			--f.bufpos;
105 		}
106 		*f.bufpos = 0;
107 	}
108 	return rv;
109 }
110 libc_hidden_def(vsnprintf)
111 
112 #elif defined(__UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__)
113 
114 typedef struct {
115 	size_t pos;
116 	size_t len;
117 	char *buf;
118 	FILE *fp;
119 } __snpf_cookie;
120 
121 #define COOKIE ((__snpf_cookie *) cookie)
122 
123 static ssize_t snpf_write(register void *cookie, const char *buf,
124 						  size_t bufsize)
125 {
126 	size_t count;
127 	register char *p;
128 
129 	/* Note: bufsize < SSIZE_MAX because of _stdio_WRITE. */
130 
131 	if (COOKIE->len > COOKIE->pos) {
132 		count = COOKIE->len - COOKIE->pos - 1; /* Leave space for nul. */
133 		if (count > bufsize) {
134 			count = bufsize;
135 		}
136 
137 		p = COOKIE->buf + COOKIE->pos;
138 		while (count) {
139 			*p++ = *buf++;
140 			--count;
141 		}
142 		*p = 0;
143 	}
144 
145 	COOKIE->pos += bufsize;
146 
147 	return bufsize;
148 }
149 
150 #undef COOKIE
151 
152 int vsnprintf(char *__restrict buf, size_t size,
153 			  const char * __restrict format, va_list arg)
154 {
155 	_IO_cookie_file_t cf;
156 	__snpf_cookie cookie;
157 	int rv;
158 
159 	cookie.buf = buf;
160 	cookie.len = size;
161 	cookie.pos = 0;
162 	cookie.fp = &cf.__fp;
163 
164 	cf.__cookie = &cookie;
165 	cf.__gcs.write = snpf_write;
166 	cf.__gcs.read = NULL;
167 	cf.__gcs.seek = NULL;
168 	cf.__gcs.close = NULL;
169 
170 	cf.__fp.__filedes = __STDIO_STREAM_GLIBC_CUSTOM_FILEDES;
171 	cf.__fp.__modeflags = (__FLAG_NARROW|__FLAG_WRITEONLY|__FLAG_WRITING);
172 
173 #ifdef __UCLIBC_HAS_WCHAR__
174 	cf.__fp.__ungot_width[0] = 0;
175 #endif /* __UCLIBC_HAS_WCHAR__ */
176 #ifdef __STDIO_MBSTATE
177 	__INIT_MBSTATE(&(cf.__fp.__state));
178 #endif /* __STDIO_MBSTATE */
179 
180 	cf.__fp.__nextopen = NULL;
181 
182 	rv = _vfprintf_internal(&cf.__fp, format, arg);
183 
184 	return rv;
185 }
186 libc_hidden_def(vsnprintf)
187 
188 #else
189 #warning Skipping vsnprintf since no buffering, no custom streams, and not old vfprintf!
190 #ifdef __STDIO_HAS_VSNPRINTF
191 #error WHOA! __STDIO_HAS_VSNPRINTF is defined!
192 #endif
193 #endif
194