xref: /dflybsd-src/contrib/libpcap/fmtutils.c (revision e75ef36f1332e115895388cede9dfd24ca1a806c)
13a289941SAaron LI /*
23a289941SAaron LI  * Copyright (c) 1993, 1994, 1995, 1996, 1997, 1998
33a289941SAaron LI  *	The Regents of the University of California.  All rights reserved.
43a289941SAaron LI  *
53a289941SAaron LI  * Redistribution and use in source and binary forms, with or without
63a289941SAaron LI  * modification, are permitted provided that the following conditions
73a289941SAaron LI  * are met:
83a289941SAaron LI  * 1. Redistributions of source code must retain the above copyright
93a289941SAaron LI  *    notice, this list of conditions and the following disclaimer.
103a289941SAaron LI  * 2. Redistributions in binary form must reproduce the above copyright
113a289941SAaron LI  *    notice, this list of conditions and the following disclaimer in the
123a289941SAaron LI  *    documentation and/or other materials provided with the distribution.
133a289941SAaron LI  * 3. All advertising materials mentioning features or use of this software
143a289941SAaron LI  *    must display the following acknowledgement:
153a289941SAaron LI  *	This product includes software developed by the Computer Systems
163a289941SAaron LI  *	Engineering Group at Lawrence Berkeley Laboratory.
173a289941SAaron LI  * 4. Neither the name of the University nor of the Laboratory may be used
183a289941SAaron LI  *    to endorse or promote products derived from this software without
193a289941SAaron LI  *    specific prior written permission.
203a289941SAaron LI  *
213a289941SAaron LI  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
223a289941SAaron LI  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
233a289941SAaron LI  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
243a289941SAaron LI  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
253a289941SAaron LI  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
263a289941SAaron LI  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
273a289941SAaron LI  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
283a289941SAaron LI  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
293a289941SAaron LI  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
303a289941SAaron LI  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
313a289941SAaron LI  * SUCH DAMAGE.
323a289941SAaron LI  */
333a289941SAaron LI 
343a289941SAaron LI /*
353a289941SAaron LI  * Utilities for message formatting used both by libpcap and rpcapd.
363a289941SAaron LI  */
373a289941SAaron LI 
383a289941SAaron LI #ifdef HAVE_CONFIG_H
393a289941SAaron LI #include <config.h>
403a289941SAaron LI #endif
413a289941SAaron LI 
423a289941SAaron LI #include "ftmacros.h"
433a289941SAaron LI 
443a289941SAaron LI #include <stddef.h>
453a289941SAaron LI #include <stdarg.h>
463a289941SAaron LI #include <stdio.h>
473a289941SAaron LI #include <string.h>
483a289941SAaron LI #include <errno.h>
493a289941SAaron LI 
50*ea16f64eSAntonio Huete Jimenez #include "pcap-int.h"
513a289941SAaron LI 
523a289941SAaron LI #include "portability.h"
533a289941SAaron LI 
543a289941SAaron LI #include "fmtutils.h"
553a289941SAaron LI 
56*ea16f64eSAntonio Huete Jimenez #ifdef _WIN32
57*ea16f64eSAntonio Huete Jimenez #include "charconv.h"
58*ea16f64eSAntonio Huete Jimenez #endif
59*ea16f64eSAntonio Huete Jimenez 
60*ea16f64eSAntonio Huete Jimenez /*
61*ea16f64eSAntonio Huete Jimenez  * Set the encoding.
62*ea16f64eSAntonio Huete Jimenez  */
63*ea16f64eSAntonio Huete Jimenez #ifdef _WIN32
64*ea16f64eSAntonio Huete Jimenez /*
65*ea16f64eSAntonio Huete Jimenez  * True if we shouold use UTF-8.
66*ea16f64eSAntonio Huete Jimenez  */
67*ea16f64eSAntonio Huete Jimenez static int use_utf_8;
68*ea16f64eSAntonio Huete Jimenez 
69*ea16f64eSAntonio Huete Jimenez void
pcap_fmt_set_encoding(unsigned int opts)70*ea16f64eSAntonio Huete Jimenez pcap_fmt_set_encoding(unsigned int opts)
71*ea16f64eSAntonio Huete Jimenez {
72*ea16f64eSAntonio Huete Jimenez 	if (opts == PCAP_CHAR_ENC_UTF_8)
73*ea16f64eSAntonio Huete Jimenez 		use_utf_8 = 1;
74*ea16f64eSAntonio Huete Jimenez }
75*ea16f64eSAntonio Huete Jimenez #else
76*ea16f64eSAntonio Huete Jimenez void
pcap_fmt_set_encoding(unsigned int opts _U_)77*ea16f64eSAntonio Huete Jimenez pcap_fmt_set_encoding(unsigned int opts _U_)
78*ea16f64eSAntonio Huete Jimenez {
79*ea16f64eSAntonio Huete Jimenez 	/*
80*ea16f64eSAntonio Huete Jimenez 	 * Nothing to do here.
81*ea16f64eSAntonio Huete Jimenez 	 */
82*ea16f64eSAntonio Huete Jimenez }
83*ea16f64eSAntonio Huete Jimenez #endif
84*ea16f64eSAntonio Huete Jimenez 
85*ea16f64eSAntonio Huete Jimenez #ifdef _WIN32
86*ea16f64eSAntonio Huete Jimenez /*
87*ea16f64eSAntonio Huete Jimenez  * Convert a null-terminated UTF-16LE string to UTF-8, putting it into
88*ea16f64eSAntonio Huete Jimenez  * a buffer starting at the specified location and stopping if we go
89*ea16f64eSAntonio Huete Jimenez  * past the specified size.  This will only put out complete UTF-8
90*ea16f64eSAntonio Huete Jimenez  * sequences.
91*ea16f64eSAntonio Huete Jimenez  *
92*ea16f64eSAntonio Huete Jimenez  * We do this ourselves because Microsoft doesn't offer a "convert and
93*ea16f64eSAntonio Huete Jimenez  * stop at a UTF-8 character boundary if we run out of space" routine.
94*ea16f64eSAntonio Huete Jimenez  */
95*ea16f64eSAntonio Huete Jimenez #define IS_LEADING_SURROGATE(c) \
96*ea16f64eSAntonio Huete Jimenez 	((c) >= 0xd800 && (c) < 0xdc00)
97*ea16f64eSAntonio Huete Jimenez #define IS_TRAILING_SURROGATE(c) \
98*ea16f64eSAntonio Huete Jimenez 	((c) >= 0xdc00 && (c) < 0xe000)
99*ea16f64eSAntonio Huete Jimenez #define SURROGATE_VALUE(leading, trailing) \
100*ea16f64eSAntonio Huete Jimenez 	(((((leading) - 0xd800) << 10) | ((trailing) - 0xdc00)) + 0x10000)
101*ea16f64eSAntonio Huete Jimenez #define REPLACEMENT_CHARACTER	0x0FFFD
102*ea16f64eSAntonio Huete Jimenez 
103*ea16f64eSAntonio Huete Jimenez static char *
utf_16le_to_utf_8_truncated(const wchar_t * utf_16,char * utf_8,size_t utf_8_len)104*ea16f64eSAntonio Huete Jimenez utf_16le_to_utf_8_truncated(const wchar_t *utf_16, char *utf_8,
105*ea16f64eSAntonio Huete Jimenez     size_t utf_8_len)
106*ea16f64eSAntonio Huete Jimenez {
107*ea16f64eSAntonio Huete Jimenez 	wchar_t c, c2;
108*ea16f64eSAntonio Huete Jimenez 	uint32_t uc;
109*ea16f64eSAntonio Huete Jimenez 
110*ea16f64eSAntonio Huete Jimenez 	if (utf_8_len == 0) {
111*ea16f64eSAntonio Huete Jimenez 		/*
112*ea16f64eSAntonio Huete Jimenez 		 * Not even enough room for a trailing '\0'.
113*ea16f64eSAntonio Huete Jimenez 		 * Don't put anything into the buffer.
114*ea16f64eSAntonio Huete Jimenez 		 */
115*ea16f64eSAntonio Huete Jimenez 		return (utf_8);
116*ea16f64eSAntonio Huete Jimenez 	}
117*ea16f64eSAntonio Huete Jimenez 
118*ea16f64eSAntonio Huete Jimenez 	while ((c = *utf_16++) != '\0') {
119*ea16f64eSAntonio Huete Jimenez 		if (IS_LEADING_SURROGATE(c)) {
120*ea16f64eSAntonio Huete Jimenez 			/*
121*ea16f64eSAntonio Huete Jimenez 			 * Leading surrogate.  Must be followed by
122*ea16f64eSAntonio Huete Jimenez 			 * a trailing surrogate.
123*ea16f64eSAntonio Huete Jimenez 			 */
124*ea16f64eSAntonio Huete Jimenez 			c2 = *utf_16;
125*ea16f64eSAntonio Huete Jimenez 			if (c2 == '\0') {
126*ea16f64eSAntonio Huete Jimenez 				/*
127*ea16f64eSAntonio Huete Jimenez 				 * Oops, string ends with a lead
128*ea16f64eSAntonio Huete Jimenez 				 * surrogate.  Try to drop in
129*ea16f64eSAntonio Huete Jimenez 				 * a REPLACEMENT CHARACTER, and
130*ea16f64eSAntonio Huete Jimenez 				 * don't move the string pointer,
131*ea16f64eSAntonio Huete Jimenez 				 * so on the next trip through
132*ea16f64eSAntonio Huete Jimenez 				 * the loop we grab the terminating
133*ea16f64eSAntonio Huete Jimenez 				 * '\0' and quit.
134*ea16f64eSAntonio Huete Jimenez 				 */
135*ea16f64eSAntonio Huete Jimenez 				uc = REPLACEMENT_CHARACTER;
136*ea16f64eSAntonio Huete Jimenez 			} else {
137*ea16f64eSAntonio Huete Jimenez 				/*
138*ea16f64eSAntonio Huete Jimenez 				 * OK, we can consume this 2-octet
139*ea16f64eSAntonio Huete Jimenez 				 * value.
140*ea16f64eSAntonio Huete Jimenez 				 */
141*ea16f64eSAntonio Huete Jimenez 				utf_16++;
142*ea16f64eSAntonio Huete Jimenez 				if (IS_TRAILING_SURROGATE(c2)) {
143*ea16f64eSAntonio Huete Jimenez 					/*
144*ea16f64eSAntonio Huete Jimenez 					 * Trailing surrogate.
145*ea16f64eSAntonio Huete Jimenez 					 * This calculation will,
146*ea16f64eSAntonio Huete Jimenez 					 * for c being a leading
147*ea16f64eSAntonio Huete Jimenez 					 * surrogate and c2 being
148*ea16f64eSAntonio Huete Jimenez 					 * a trailing surrogate,
149*ea16f64eSAntonio Huete Jimenez 					 * produce a value between
150*ea16f64eSAntonio Huete Jimenez 					 * 0x100000 and 0x10ffff,
151*ea16f64eSAntonio Huete Jimenez 					 * so it's always going to be
152*ea16f64eSAntonio Huete Jimenez 					 * a valid Unicode code point.
153*ea16f64eSAntonio Huete Jimenez 					 */
154*ea16f64eSAntonio Huete Jimenez 					uc = SURROGATE_VALUE(c, c2);
155*ea16f64eSAntonio Huete Jimenez 				} else {
156*ea16f64eSAntonio Huete Jimenez 					/*
157*ea16f64eSAntonio Huete Jimenez 					 * Not a trailing surroage;
158*ea16f64eSAntonio Huete Jimenez 					 * try to drop in a
159*ea16f64eSAntonio Huete Jimenez 					 * REPLACEMENT CHARACTER.
160*ea16f64eSAntonio Huete Jimenez 					 */
161*ea16f64eSAntonio Huete Jimenez 					uc = REPLACEMENT_CHARACTER;
162*ea16f64eSAntonio Huete Jimenez 				}
163*ea16f64eSAntonio Huete Jimenez 			}
164*ea16f64eSAntonio Huete Jimenez 		} else {
165*ea16f64eSAntonio Huete Jimenez 			/*
166*ea16f64eSAntonio Huete Jimenez 			 * Not a leading surrogate.
167*ea16f64eSAntonio Huete Jimenez 			 */
168*ea16f64eSAntonio Huete Jimenez 			if (IS_TRAILING_SURROGATE(c)) {
169*ea16f64eSAntonio Huete Jimenez 				/*
170*ea16f64eSAntonio Huete Jimenez 				 * Trailing surrogate without
171*ea16f64eSAntonio Huete Jimenez 				 * a preceding leading surrogate.
172*ea16f64eSAntonio Huete Jimenez 				 * Try to drop in a REPLACEMENT
173*ea16f64eSAntonio Huete Jimenez 				 * CHARACTER.
174*ea16f64eSAntonio Huete Jimenez 				 */
175*ea16f64eSAntonio Huete Jimenez 				uc = REPLACEMENT_CHARACTER;
176*ea16f64eSAntonio Huete Jimenez 			} else {
177*ea16f64eSAntonio Huete Jimenez 				/*
178*ea16f64eSAntonio Huete Jimenez 				 * This is a valid BMP character;
179*ea16f64eSAntonio Huete Jimenez 				 * drop it in.
180*ea16f64eSAntonio Huete Jimenez 				 */
181*ea16f64eSAntonio Huete Jimenez 				uc = c;
182*ea16f64eSAntonio Huete Jimenez 			}
183*ea16f64eSAntonio Huete Jimenez 		}
184*ea16f64eSAntonio Huete Jimenez 
185*ea16f64eSAntonio Huete Jimenez 		/*
186*ea16f64eSAntonio Huete Jimenez 		 * OK, uc is a valid Unicode character; how
187*ea16f64eSAntonio Huete Jimenez 		 * many bytes worth of UTF-8 does it require?
188*ea16f64eSAntonio Huete Jimenez 		 */
189*ea16f64eSAntonio Huete Jimenez 		if (uc < 0x0080) {
190*ea16f64eSAntonio Huete Jimenez 			/* 1 byte. */
191*ea16f64eSAntonio Huete Jimenez 			if (utf_8_len < 2) {
192*ea16f64eSAntonio Huete Jimenez 				/*
193*ea16f64eSAntonio Huete Jimenez 				 * Not enough room for that byte
194*ea16f64eSAntonio Huete Jimenez 				 * plus a trailing '\0'.
195*ea16f64eSAntonio Huete Jimenez 				 */
196*ea16f64eSAntonio Huete Jimenez 				break;
197*ea16f64eSAntonio Huete Jimenez 			}
198*ea16f64eSAntonio Huete Jimenez 			*utf_8++ = (char)uc;
199*ea16f64eSAntonio Huete Jimenez 			utf_8_len--;
200*ea16f64eSAntonio Huete Jimenez 		} else if (uc < 0x0800) {
201*ea16f64eSAntonio Huete Jimenez 			/* 2 bytes. */
202*ea16f64eSAntonio Huete Jimenez 			if (utf_8_len < 3) {
203*ea16f64eSAntonio Huete Jimenez 				/*
204*ea16f64eSAntonio Huete Jimenez 				 * Not enough room for those bytes
205*ea16f64eSAntonio Huete Jimenez 				 * plus a trailing '\0'.
206*ea16f64eSAntonio Huete Jimenez 				 */
207*ea16f64eSAntonio Huete Jimenez 				break;
208*ea16f64eSAntonio Huete Jimenez 			}
209*ea16f64eSAntonio Huete Jimenez 			*utf_8++ = ((uc >> 6) & 0x3F) | 0xC0;
210*ea16f64eSAntonio Huete Jimenez 			*utf_8++ = ((uc >> 0) & 0x3F) | 0x80;
211*ea16f64eSAntonio Huete Jimenez 			utf_8_len -= 2;
212*ea16f64eSAntonio Huete Jimenez 		} else if (uc < 0x010000) {
213*ea16f64eSAntonio Huete Jimenez 			/* 3 bytes. */
214*ea16f64eSAntonio Huete Jimenez 			if (utf_8_len < 4) {
215*ea16f64eSAntonio Huete Jimenez 				/*
216*ea16f64eSAntonio Huete Jimenez 				 * Not enough room for those bytes
217*ea16f64eSAntonio Huete Jimenez 				 * plus a trailing '\0'.
218*ea16f64eSAntonio Huete Jimenez 				 */
219*ea16f64eSAntonio Huete Jimenez 				break;
220*ea16f64eSAntonio Huete Jimenez 			}
221*ea16f64eSAntonio Huete Jimenez 			*utf_8++ = ((uc >> 12) & 0x0F) | 0xE0;
222*ea16f64eSAntonio Huete Jimenez 			*utf_8++ = ((uc >> 6) & 0x3F) | 0x80;
223*ea16f64eSAntonio Huete Jimenez 			*utf_8++ = ((uc >> 0) & 0x3F) | 0x80;
224*ea16f64eSAntonio Huete Jimenez 			utf_8_len -= 3;
225*ea16f64eSAntonio Huete Jimenez 		} else {
226*ea16f64eSAntonio Huete Jimenez 			/* 4 bytes. */
227*ea16f64eSAntonio Huete Jimenez 			if (utf_8_len < 5) {
228*ea16f64eSAntonio Huete Jimenez 				/*
229*ea16f64eSAntonio Huete Jimenez 				 * Not enough room for those bytes
230*ea16f64eSAntonio Huete Jimenez 				 * plus a trailing '\0'.
231*ea16f64eSAntonio Huete Jimenez 				 */
232*ea16f64eSAntonio Huete Jimenez 				break;
233*ea16f64eSAntonio Huete Jimenez 			}
234*ea16f64eSAntonio Huete Jimenez 			*utf_8++ = ((uc >> 18) & 0x03) | 0xF0;
235*ea16f64eSAntonio Huete Jimenez 			*utf_8++ = ((uc >> 12) & 0x3F) | 0x80;
236*ea16f64eSAntonio Huete Jimenez 			*utf_8++ = ((uc >> 6) & 0x3F) | 0x80;
237*ea16f64eSAntonio Huete Jimenez 			*utf_8++ = ((uc >> 0) & 0x3F) | 0x80;
238*ea16f64eSAntonio Huete Jimenez 			utf_8_len -= 3;
239*ea16f64eSAntonio Huete Jimenez 		}
240*ea16f64eSAntonio Huete Jimenez 	}
241*ea16f64eSAntonio Huete Jimenez 
242*ea16f64eSAntonio Huete Jimenez 	/*
243*ea16f64eSAntonio Huete Jimenez 	 * OK, we have enough room for (at least) a trailing '\0'.
244*ea16f64eSAntonio Huete Jimenez 	 * (We started out with enough room, thanks to the test
245*ea16f64eSAntonio Huete Jimenez 	 * for a zero-length buffer at the beginning, and if
246*ea16f64eSAntonio Huete Jimenez 	 * there wasn't enough room for any character we wanted
247*ea16f64eSAntonio Huete Jimenez 	 * to put into the buffer *plus* a trailing '\0',
248*ea16f64eSAntonio Huete Jimenez 	 * we'd have quit before putting it into the buffer,
249*ea16f64eSAntonio Huete Jimenez 	 * and thus would have left enough room for the trailing
250*ea16f64eSAntonio Huete Jimenez 	 * '\0'.)
251*ea16f64eSAntonio Huete Jimenez 	 *
252*ea16f64eSAntonio Huete Jimenez 	 * Drop it in.
253*ea16f64eSAntonio Huete Jimenez 	 */
254*ea16f64eSAntonio Huete Jimenez 	*utf_8 = '\0';
255*ea16f64eSAntonio Huete Jimenez 
256*ea16f64eSAntonio Huete Jimenez 	/*
257*ea16f64eSAntonio Huete Jimenez 	 * Return a pointer to the terminating '\0', in case we
258*ea16f64eSAntonio Huete Jimenez 	 * want to drop something in after that.
259*ea16f64eSAntonio Huete Jimenez 	 */
260*ea16f64eSAntonio Huete Jimenez 	return (utf_8);
261*ea16f64eSAntonio Huete Jimenez }
262*ea16f64eSAntonio Huete Jimenez #endif /* _WIN32 */
263*ea16f64eSAntonio Huete Jimenez 
2643a289941SAaron LI /*
2653a289941SAaron LI  * Generate an error message based on a format, arguments, and an
2663a289941SAaron LI  * errno, with a message for the errno after the formatted output.
2673a289941SAaron LI  */
2683a289941SAaron LI void
pcap_fmt_errmsg_for_errno(char * errbuf,size_t errbuflen,int errnum,const char * fmt,...)2693a289941SAaron LI pcap_fmt_errmsg_for_errno(char *errbuf, size_t errbuflen, int errnum,
2703a289941SAaron LI     const char *fmt, ...)
2713a289941SAaron LI {
2723a289941SAaron LI 	va_list ap;
2733a289941SAaron LI 	size_t msglen;
2743a289941SAaron LI 	char *p;
2753a289941SAaron LI 	size_t errbuflen_remaining;
2763a289941SAaron LI 
2773a289941SAaron LI 	va_start(ap, fmt);
278*ea16f64eSAntonio Huete Jimenez 	vsnprintf(errbuf, errbuflen, fmt, ap);
2793a289941SAaron LI 	va_end(ap);
2803a289941SAaron LI 	msglen = strlen(errbuf);
2813a289941SAaron LI 
2823a289941SAaron LI 	/*
2833a289941SAaron LI 	 * Do we have enough space to append ": "?
2843a289941SAaron LI 	 * Including the terminating '\0', that's 3 bytes.
2853a289941SAaron LI 	 */
2863a289941SAaron LI 	if (msglen + 3 > errbuflen) {
2873a289941SAaron LI 		/* No - just give them what we've produced. */
2883a289941SAaron LI 		return;
2893a289941SAaron LI 	}
2903a289941SAaron LI 	p = errbuf + msglen;
2913a289941SAaron LI 	errbuflen_remaining = errbuflen - msglen;
2923a289941SAaron LI 	*p++ = ':';
2933a289941SAaron LI 	*p++ = ' ';
2943a289941SAaron LI 	*p = '\0';
2953a289941SAaron LI 	errbuflen_remaining -= 2;
2963a289941SAaron LI 
2973a289941SAaron LI 	/*
2983a289941SAaron LI 	 * Now append the string for the error code.
2993a289941SAaron LI 	 */
300*ea16f64eSAntonio Huete Jimenez #if defined(HAVE__WCSERROR_S)
3013a289941SAaron LI 	/*
302*ea16f64eSAntonio Huete Jimenez 	 * We have a Windows-style _wcserror_s().
303*ea16f64eSAntonio Huete Jimenez 	 * Generate a UTF-16LE error message.
3043a289941SAaron LI 	 */
305*ea16f64eSAntonio Huete Jimenez 	wchar_t utf_16_errbuf[PCAP_ERRBUF_SIZE];
306*ea16f64eSAntonio Huete Jimenez 	errno_t err = _wcserror_s(utf_16_errbuf, PCAP_ERRBUF_SIZE, errnum);
3073a289941SAaron LI 	if (err != 0) {
3083a289941SAaron LI 		/*
3093a289941SAaron LI 		 * It doesn't appear to be documented anywhere obvious
310*ea16f64eSAntonio Huete Jimenez 		 * what the error returns from _wcserror_s().
3113a289941SAaron LI 		 */
312*ea16f64eSAntonio Huete Jimenez 		snprintf(p, errbuflen_remaining, "Error %d", errnum);
313*ea16f64eSAntonio Huete Jimenez 		return;
3143a289941SAaron LI 	}
315*ea16f64eSAntonio Huete Jimenez 
316*ea16f64eSAntonio Huete Jimenez 	/*
317*ea16f64eSAntonio Huete Jimenez 	 * Now convert it from UTF-16LE to UTF-8, dropping it in the
318*ea16f64eSAntonio Huete Jimenez 	 * remaining space in the buffer, and truncating it - cleanly,
319*ea16f64eSAntonio Huete Jimenez 	 * on a UTF-8 character boundary - if it doesn't fit.
320*ea16f64eSAntonio Huete Jimenez 	 */
321*ea16f64eSAntonio Huete Jimenez 	utf_16le_to_utf_8_truncated(utf_16_errbuf, p, errbuflen_remaining);
322*ea16f64eSAntonio Huete Jimenez 
323*ea16f64eSAntonio Huete Jimenez 	/*
324*ea16f64eSAntonio Huete Jimenez 	 * Now, if we're not in UTF-8 mode, convert errbuf to the
325*ea16f64eSAntonio Huete Jimenez 	 * local code page.
326*ea16f64eSAntonio Huete Jimenez 	 */
327*ea16f64eSAntonio Huete Jimenez 	if (!use_utf_8)
328*ea16f64eSAntonio Huete Jimenez 		utf_8_to_acp_truncated(errbuf);
3293a289941SAaron LI #elif defined(HAVE_GNU_STRERROR_R)
3303a289941SAaron LI 	/*
3313a289941SAaron LI 	 * We have a GNU-style strerror_r(), which is *not* guaranteed to
3323a289941SAaron LI 	 * do anything to the buffer handed to it, and which returns a
3333a289941SAaron LI 	 * pointer to the error string, which may or may not be in
3343a289941SAaron LI 	 * the buffer.
3353a289941SAaron LI 	 *
3363a289941SAaron LI 	 * It is, however, guaranteed to succeed.
3373a289941SAaron LI 	 */
3383a289941SAaron LI 	char strerror_buf[PCAP_ERRBUF_SIZE];
3393a289941SAaron LI 	char *errstring = strerror_r(errnum, strerror_buf, PCAP_ERRBUF_SIZE);
340*ea16f64eSAntonio Huete Jimenez 	snprintf(p, errbuflen_remaining, "%s", errstring);
3413a289941SAaron LI #elif defined(HAVE_POSIX_STRERROR_R)
3423a289941SAaron LI 	/*
3433a289941SAaron LI 	 * We have a POSIX-style strerror_r(), which is guaranteed to fill
3443a289941SAaron LI 	 * in the buffer, but is not guaranteed to succeed.
3453a289941SAaron LI 	 */
3463a289941SAaron LI 	int err = strerror_r(errnum, p, errbuflen_remaining);
3473a289941SAaron LI 	if (err == EINVAL) {
3483a289941SAaron LI 		/*
3493a289941SAaron LI 		 * UNIX 03 says this isn't guaranteed to produce a
3503a289941SAaron LI 		 * fallback error message.
3513a289941SAaron LI 		 */
352*ea16f64eSAntonio Huete Jimenez 		snprintf(p, errbuflen_remaining, "Unknown error: %d",
3533a289941SAaron LI 		    errnum);
3543a289941SAaron LI 	} else if (err == ERANGE) {
3553a289941SAaron LI 		/*
3563a289941SAaron LI 		 * UNIX 03 says this isn't guaranteed to produce a
3573a289941SAaron LI 		 * fallback error message.
3583a289941SAaron LI 		 */
359*ea16f64eSAntonio Huete Jimenez 		snprintf(p, errbuflen_remaining,
3603a289941SAaron LI 		    "Message for error %d is too long", errnum);
3613a289941SAaron LI 	}
3623a289941SAaron LI #else
3633a289941SAaron LI 	/*
364*ea16f64eSAntonio Huete Jimenez 	 * We have neither _wcserror_s() nor strerror_r(), so we're
3653a289941SAaron LI 	 * stuck with using pcap_strerror().
3663a289941SAaron LI 	 */
367*ea16f64eSAntonio Huete Jimenez 	snprintf(p, errbuflen_remaining, "%s", pcap_strerror(errnum));
3683a289941SAaron LI #endif
3693a289941SAaron LI }
3703a289941SAaron LI 
3713a289941SAaron LI #ifdef _WIN32
3723a289941SAaron LI /*
3733a289941SAaron LI  * Generate an error message based on a format, arguments, and a
3743a289941SAaron LI  * Win32 error, with a message for the Win32 error after the formatted output.
3753a289941SAaron LI  */
3763a289941SAaron LI void
pcap_fmt_errmsg_for_win32_err(char * errbuf,size_t errbuflen,DWORD errnum,const char * fmt,...)3773a289941SAaron LI pcap_fmt_errmsg_for_win32_err(char *errbuf, size_t errbuflen, DWORD errnum,
3783a289941SAaron LI     const char *fmt, ...)
3793a289941SAaron LI {
3803a289941SAaron LI 	va_list ap;
3813a289941SAaron LI 	size_t msglen;
3823a289941SAaron LI 	char *p;
3833a289941SAaron LI 	size_t errbuflen_remaining;
3843a289941SAaron LI 	DWORD retval;
385*ea16f64eSAntonio Huete Jimenez 	wchar_t utf_16_errbuf[PCAP_ERRBUF_SIZE];
386*ea16f64eSAntonio Huete Jimenez 	size_t utf_8_len;
3873a289941SAaron LI 
3883a289941SAaron LI 	va_start(ap, fmt);
389*ea16f64eSAntonio Huete Jimenez 	vsnprintf(errbuf, errbuflen, fmt, ap);
3903a289941SAaron LI 	va_end(ap);
3913a289941SAaron LI 	msglen = strlen(errbuf);
3923a289941SAaron LI 
3933a289941SAaron LI 	/*
3943a289941SAaron LI 	 * Do we have enough space to append ": "?
3953a289941SAaron LI 	 * Including the terminating '\0', that's 3 bytes.
3963a289941SAaron LI 	 */
3973a289941SAaron LI 	if (msglen + 3 > errbuflen) {
3983a289941SAaron LI 		/* No - just give them what we've produced. */
3993a289941SAaron LI 		return;
4003a289941SAaron LI 	}
4013a289941SAaron LI 	p = errbuf + msglen;
4023a289941SAaron LI 	errbuflen_remaining = errbuflen - msglen;
4033a289941SAaron LI 	*p++ = ':';
4043a289941SAaron LI 	*p++ = ' ';
4053a289941SAaron LI 	*p = '\0';
4063a289941SAaron LI 	msglen += 2;
4073a289941SAaron LI 	errbuflen_remaining -= 2;
4083a289941SAaron LI 
4093a289941SAaron LI 	/*
4103a289941SAaron LI 	 * Now append the string for the error code.
4113a289941SAaron LI 	 *
4123a289941SAaron LI 	 * XXX - what language ID to use?
4133a289941SAaron LI 	 *
4143a289941SAaron LI 	 * For UN*Xes, pcap_strerror() may or may not return localized
4153a289941SAaron LI 	 * strings.
4163a289941SAaron LI 	 *
4173a289941SAaron LI 	 * We currently don't have localized messages for libpcap, but
4183a289941SAaron LI 	 * we might want to do so.  On the other hand, if most of these
4193a289941SAaron LI 	 * messages are going to be read by libpcap developers and
4203a289941SAaron LI 	 * perhaps by developers of libpcap-based applications, English
4213a289941SAaron LI 	 * might be a better choice, so the developer doesn't have to
4223a289941SAaron LI 	 * get the message translated if it's in a language they don't
4233a289941SAaron LI 	 * happen to understand.
4243a289941SAaron LI 	 */
425*ea16f64eSAntonio Huete Jimenez 	retval = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_MAX_WIDTH_MASK,
4263a289941SAaron LI 	    NULL, errnum, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
427*ea16f64eSAntonio Huete Jimenez 	    utf_16_errbuf, PCAP_ERRBUF_SIZE, NULL);
4283a289941SAaron LI 	if (retval == 0) {
4293a289941SAaron LI 		/*
4303a289941SAaron LI 		 * Failed.
4313a289941SAaron LI 		 */
432*ea16f64eSAntonio Huete Jimenez 		snprintf(p, errbuflen_remaining,
4333a289941SAaron LI 		    "Couldn't get error message for error (%lu)", errnum);
4343a289941SAaron LI 		return;
4353a289941SAaron LI 	}
4363a289941SAaron LI 
437*ea16f64eSAntonio Huete Jimenez 	/*
438*ea16f64eSAntonio Huete Jimenez 	 * Now convert it from UTF-16LE to UTF-8.
439*ea16f64eSAntonio Huete Jimenez 	 */
440*ea16f64eSAntonio Huete Jimenez 	p = utf_16le_to_utf_8_truncated(utf_16_errbuf, p, errbuflen_remaining);
441*ea16f64eSAntonio Huete Jimenez 
442*ea16f64eSAntonio Huete Jimenez 	/*
443*ea16f64eSAntonio Huete Jimenez 	 * Now append the error number, if it fits.
444*ea16f64eSAntonio Huete Jimenez 	 */
445*ea16f64eSAntonio Huete Jimenez 	utf_8_len = p - errbuf;
446*ea16f64eSAntonio Huete Jimenez 	errbuflen_remaining -= utf_8_len;
447*ea16f64eSAntonio Huete Jimenez 	if (utf_8_len == 0) {
448*ea16f64eSAntonio Huete Jimenez 		/* The message was empty. */
449*ea16f64eSAntonio Huete Jimenez 		snprintf(p, errbuflen_remaining, "(%lu)", errnum);
450*ea16f64eSAntonio Huete Jimenez 	} else
451*ea16f64eSAntonio Huete Jimenez 		snprintf(p, errbuflen_remaining, " (%lu)", errnum);
452*ea16f64eSAntonio Huete Jimenez 
453*ea16f64eSAntonio Huete Jimenez 	/*
454*ea16f64eSAntonio Huete Jimenez 	 * Now, if we're not in UTF-8 mode, convert errbuf to the
455*ea16f64eSAntonio Huete Jimenez 	 * local code page.
456*ea16f64eSAntonio Huete Jimenez 	 */
457*ea16f64eSAntonio Huete Jimenez 	if (!use_utf_8)
458*ea16f64eSAntonio Huete Jimenez 		utf_8_to_acp_truncated(errbuf);
4593a289941SAaron LI }
4603a289941SAaron LI #endif
461