xref: /freebsd/contrib/ncurses/ncurses/base/safe_sprintf.c (revision 21817992b3314c908ab50f0bb88d2ee750b9c4ac)
10e3d5408SPeter Wemm /****************************************************************************
2*21817992SBaptiste Daroussin  * Copyright 2018-2021,2023 Thomas E. Dickey                                *
3e1865124SBaptiste Daroussin  * Copyright 1998-2012,2013 Free Software Foundation, Inc.                  *
40e3d5408SPeter Wemm  *                                                                          *
50e3d5408SPeter Wemm  * Permission is hereby granted, free of charge, to any person obtaining a  *
60e3d5408SPeter Wemm  * copy of this software and associated documentation files (the            *
70e3d5408SPeter Wemm  * "Software"), to deal in the Software without restriction, including      *
80e3d5408SPeter Wemm  * without limitation the rights to use, copy, modify, merge, publish,      *
90e3d5408SPeter Wemm  * distribute, distribute with modifications, sublicense, and/or sell       *
100e3d5408SPeter Wemm  * copies of the Software, and to permit persons to whom the Software is    *
110e3d5408SPeter Wemm  * furnished to do so, subject to the following conditions:                 *
120e3d5408SPeter Wemm  *                                                                          *
130e3d5408SPeter Wemm  * The above copyright notice and this permission notice shall be included  *
140e3d5408SPeter Wemm  * in all copies or substantial portions of the Software.                   *
150e3d5408SPeter Wemm  *                                                                          *
160e3d5408SPeter Wemm  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
170e3d5408SPeter Wemm  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
180e3d5408SPeter Wemm  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
190e3d5408SPeter Wemm  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
200e3d5408SPeter Wemm  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
210e3d5408SPeter Wemm  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
220e3d5408SPeter Wemm  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
230e3d5408SPeter Wemm  *                                                                          *
240e3d5408SPeter Wemm  * Except as contained in this notice, the name(s) of the above copyright   *
250e3d5408SPeter Wemm  * holders shall not be used in advertising or otherwise to promote the     *
260e3d5408SPeter Wemm  * sale, use or other dealings in this Software without prior written       *
270e3d5408SPeter Wemm  * authorization.                                                           *
280e3d5408SPeter Wemm  ****************************************************************************/
290e3d5408SPeter Wemm 
300e3d5408SPeter Wemm /****************************************************************************
3173f0a83dSXin LI  *  Author: Thomas E. Dickey        1997-on                                 *
320e3d5408SPeter Wemm  ****************************************************************************/
330e3d5408SPeter Wemm 
340e3d5408SPeter Wemm #include <curses.priv.h>
350e3d5408SPeter Wemm #include <ctype.h>
360e3d5408SPeter Wemm 
37*21817992SBaptiste Daroussin MODULE_ID("$Id: safe_sprintf.c,v 1.37 2023/09/30 10:42:42 tom Exp $")
380e3d5408SPeter Wemm 
390e3d5408SPeter Wemm #if USE_SAFE_SPRINTF
400e3d5408SPeter Wemm 
417a69bbfbSPeter Wemm typedef enum {
427a69bbfbSPeter Wemm     Flags, Width, Prec, Type, Format
437a69bbfbSPeter Wemm } PRINTF;
440e3d5408SPeter Wemm 
45aae38d10SBaptiste Daroussin #define VA_INTGR(type) ival = (int) va_arg(ap, type)
460e3d5408SPeter Wemm #define VA_FLOAT(type) fval = va_arg(ap, type)
470e3d5408SPeter Wemm #define VA_POINT(type) pval = (void *)va_arg(ap, type)
480e3d5408SPeter Wemm 
490e3d5408SPeter Wemm /*
500e3d5408SPeter Wemm  * Scan a variable-argument list for printf to determine the number of
510e3d5408SPeter Wemm  * characters that would be emitted.
520e3d5408SPeter Wemm  */
530e3d5408SPeter Wemm static int
_nc_printf_length(const char * fmt,va_list ap)540e3d5408SPeter Wemm _nc_printf_length(const char *fmt, va_list ap)
550e3d5408SPeter Wemm {
560e3d5408SPeter Wemm     size_t length = BUFSIZ;
570e3d5408SPeter Wemm     char *buffer;
580e3d5408SPeter Wemm     char *format;
590e3d5408SPeter Wemm     int len = 0;
604a1a9510SRong-En Fan     size_t fmt_len;
614a1a9510SRong-En Fan     char fmt_arg[BUFSIZ];
620e3d5408SPeter Wemm 
630e3d5408SPeter Wemm     if (fmt == 0 || *fmt == '\0')
644a1a9510SRong-En Fan 	return 0;
654a1a9510SRong-En Fan     fmt_len = strlen(fmt) + 1;
664a1a9510SRong-En Fan     if ((format = typeMalloc(char, fmt_len)) == 0)
670e3d5408SPeter Wemm 	  return -1;
680e3d5408SPeter Wemm     if ((buffer = typeMalloc(char, length)) == 0) {
690e3d5408SPeter Wemm 	free(format);
700e3d5408SPeter Wemm 	return -1;
710e3d5408SPeter Wemm     }
720e3d5408SPeter Wemm 
730e3d5408SPeter Wemm     while (*fmt != '\0') {
740e3d5408SPeter Wemm 	if (*fmt == '%') {
750e3d5408SPeter Wemm 	    static char dummy[] = "";
760e3d5408SPeter Wemm 	    PRINTF state = Flags;
770e3d5408SPeter Wemm 	    char *pval = dummy;	/* avoid const-cast */
780e3d5408SPeter Wemm 	    double fval = 0.0;
790e3d5408SPeter Wemm 	    int done = FALSE;
800e3d5408SPeter Wemm 	    int ival = 0;
810e3d5408SPeter Wemm 	    int prec = -1;
820e3d5408SPeter Wemm 	    int type = 0;
830e3d5408SPeter Wemm 	    int used = 0;
840e3d5408SPeter Wemm 	    int width = -1;
850e3d5408SPeter Wemm 	    size_t f = 0;
860e3d5408SPeter Wemm 
870e3d5408SPeter Wemm 	    format[f++] = *fmt;
880e3d5408SPeter Wemm 	    while (*++fmt != '\0' && len >= 0 && !done) {
890e3d5408SPeter Wemm 		format[f++] = *fmt;
900e3d5408SPeter Wemm 
9139f2269fSPeter Wemm 		if (isdigit(UChar(*fmt))) {
920e3d5408SPeter Wemm 		    int num = *fmt - '0';
930e3d5408SPeter Wemm 		    if (state == Flags && num != 0)
940e3d5408SPeter Wemm 			state = Width;
950e3d5408SPeter Wemm 		    if (state == Width) {
960e3d5408SPeter Wemm 			if (width < 0)
970e3d5408SPeter Wemm 			    width = 0;
980e3d5408SPeter Wemm 			width = (width * 10) + num;
990e3d5408SPeter Wemm 		    } else if (state == Prec) {
1000e3d5408SPeter Wemm 			if (prec < 0)
1010e3d5408SPeter Wemm 			    prec = 0;
1020e3d5408SPeter Wemm 			prec = (prec * 10) + num;
1030e3d5408SPeter Wemm 		    }
1040e3d5408SPeter Wemm 		} else if (*fmt == '*') {
1050e3d5408SPeter Wemm 		    VA_INTGR(int);
1060e3d5408SPeter Wemm 		    if (state == Flags)
1070e3d5408SPeter Wemm 			state = Width;
1080e3d5408SPeter Wemm 		    if (state == Width) {
1090e3d5408SPeter Wemm 			width = ival;
1100e3d5408SPeter Wemm 		    } else if (state == Prec) {
1110e3d5408SPeter Wemm 			prec = ival;
1120e3d5408SPeter Wemm 		    }
11373f0a83dSXin LI 		    _nc_SPRINTF(fmt_arg,
11473f0a83dSXin LI 				_nc_SLIMIT(sizeof(fmt_arg))
11573f0a83dSXin LI 				"%d", ival);
1164a1a9510SRong-En Fan 		    fmt_len += strlen(fmt_arg);
11706bfebdeSXin LI 		    if ((format = _nc_doalloc(format, fmt_len)) == 0) {
11873f0a83dSXin LI 			free(buffer);
1194a1a9510SRong-En Fan 			return -1;
1204a1a9510SRong-En Fan 		    }
12173f0a83dSXin LI 		    --f;
12273f0a83dSXin LI 		    _nc_STRCPY(&format[f], fmt_arg, fmt_len - f);
1230e3d5408SPeter Wemm 		    f = strlen(format);
12439f2269fSPeter Wemm 		} else if (isalpha(UChar(*fmt))) {
1250e3d5408SPeter Wemm 		    done = TRUE;
1260e3d5408SPeter Wemm 		    switch (*fmt) {
1270e3d5408SPeter Wemm 		    case 'Z':	/* FALLTHRU */
1280e3d5408SPeter Wemm 		    case 'h':	/* FALLTHRU */
1290e3d5408SPeter Wemm 		    case 'l':	/* FALLTHRU */
1300e3d5408SPeter Wemm 			done = FALSE;
1310e3d5408SPeter Wemm 			type = *fmt;
1320e3d5408SPeter Wemm 			break;
1330e3d5408SPeter Wemm 		    case 'i':	/* FALLTHRU */
1340e3d5408SPeter Wemm 		    case 'd':	/* FALLTHRU */
1350e3d5408SPeter Wemm 		    case 'u':	/* FALLTHRU */
1360e3d5408SPeter Wemm 		    case 'x':	/* FALLTHRU */
1370e3d5408SPeter Wemm 		    case 'X':	/* FALLTHRU */
1380e3d5408SPeter Wemm 			if (type == 'l')
1390e3d5408SPeter Wemm 			    VA_INTGR(long);
1400e3d5408SPeter Wemm 			else if (type == 'Z')
1410e3d5408SPeter Wemm 			    VA_INTGR(size_t);
1420e3d5408SPeter Wemm 			else
1430e3d5408SPeter Wemm 			    VA_INTGR(int);
1440e3d5408SPeter Wemm 			used = 'i';
1450e3d5408SPeter Wemm 			break;
1460e3d5408SPeter Wemm 		    case 'f':	/* FALLTHRU */
1470e3d5408SPeter Wemm 		    case 'e':	/* FALLTHRU */
1480e3d5408SPeter Wemm 		    case 'E':	/* FALLTHRU */
1490e3d5408SPeter Wemm 		    case 'g':	/* FALLTHRU */
1500e3d5408SPeter Wemm 		    case 'G':	/* FALLTHRU */
1510e3d5408SPeter Wemm 			VA_FLOAT(double);
1520e3d5408SPeter Wemm 			used = 'f';
1530e3d5408SPeter Wemm 			break;
1540e3d5408SPeter Wemm 		    case 'c':
1550e3d5408SPeter Wemm 			VA_INTGR(int);
1560e3d5408SPeter Wemm 			used = 'i';
1570e3d5408SPeter Wemm 			break;
1580e3d5408SPeter Wemm 		    case 's':
1590e3d5408SPeter Wemm 			VA_POINT(char *);
1600e3d5408SPeter Wemm 			if (prec < 0)
161aae38d10SBaptiste Daroussin 			    prec = (int) strlen(pval);
1620e3d5408SPeter Wemm 			if (prec > (int) length) {
163aae38d10SBaptiste Daroussin 			    length = length + (size_t) prec;
1640e3d5408SPeter Wemm 			    buffer = typeRealloc(char, length, buffer);
1650e3d5408SPeter Wemm 			    if (buffer == 0) {
1660e3d5408SPeter Wemm 				free(format);
1670e3d5408SPeter Wemm 				return -1;
1680e3d5408SPeter Wemm 			    }
1690e3d5408SPeter Wemm 			}
1700e3d5408SPeter Wemm 			used = 'p';
1710e3d5408SPeter Wemm 			break;
1720e3d5408SPeter Wemm 		    case 'p':
1730e3d5408SPeter Wemm 			VA_POINT(void *);
1740e3d5408SPeter Wemm 			used = 'p';
1750e3d5408SPeter Wemm 			break;
1760e3d5408SPeter Wemm 		    case 'n':
1770e3d5408SPeter Wemm 			VA_POINT(int *);
1780e3d5408SPeter Wemm 			used = 0;
1790e3d5408SPeter Wemm 			break;
1800e3d5408SPeter Wemm 		    default:
1810e3d5408SPeter Wemm 			break;
1820e3d5408SPeter Wemm 		    }
1830e3d5408SPeter Wemm 		} else if (*fmt == '.') {
1840e3d5408SPeter Wemm 		    state = Prec;
1850e3d5408SPeter Wemm 		} else if (*fmt == '%') {
1860e3d5408SPeter Wemm 		    done = TRUE;
1870e3d5408SPeter Wemm 		    used = 'p';
1880e3d5408SPeter Wemm 		}
1890e3d5408SPeter Wemm 	    }
1900e3d5408SPeter Wemm 	    format[f] = '\0';
1910e3d5408SPeter Wemm 	    switch (used) {
1920e3d5408SPeter Wemm 	    case 'i':
19373f0a83dSXin LI 		_nc_SPRINTF(buffer, _nc_SLIMIT(length) format, ival);
1940e3d5408SPeter Wemm 		break;
1950e3d5408SPeter Wemm 	    case 'f':
19673f0a83dSXin LI 		_nc_SPRINTF(buffer, _nc_SLIMIT(length) format, fval);
1970e3d5408SPeter Wemm 		break;
1980e3d5408SPeter Wemm 	    default:
19973f0a83dSXin LI 		_nc_SPRINTF(buffer, _nc_SLIMIT(length) format, pval);
2000e3d5408SPeter Wemm 		break;
2010e3d5408SPeter Wemm 	    }
2020e3d5408SPeter Wemm 	    len += (int) strlen(buffer);
2030e3d5408SPeter Wemm 	} else {
2040e3d5408SPeter Wemm 	    fmt++;
2050e3d5408SPeter Wemm 	    len++;
2060e3d5408SPeter Wemm 	}
2070e3d5408SPeter Wemm     }
2080e3d5408SPeter Wemm 
2090e3d5408SPeter Wemm     free(buffer);
2100e3d5408SPeter Wemm     free(format);
2110e3d5408SPeter Wemm     return len;
2120e3d5408SPeter Wemm }
2130e3d5408SPeter Wemm #endif
2140e3d5408SPeter Wemm 
2155ca44d1cSRong-En Fan #define my_buffer _nc_globals.safeprint_buf
2165ca44d1cSRong-En Fan #define my_length _nc_globals.safeprint_used
2175ca44d1cSRong-En Fan 
2180e3d5408SPeter Wemm /*
2190e3d5408SPeter Wemm  * Wrapper for vsprintf that allocates a buffer big enough to hold the result.
2200e3d5408SPeter Wemm  */
2217a69bbfbSPeter Wemm NCURSES_EXPORT(char *)
NCURSES_SP_NAME(_nc_printf_string)22206bfebdeSXin LI NCURSES_SP_NAME(_nc_printf_string) (NCURSES_SP_DCLx
22306bfebdeSXin LI 				    const char *fmt,
22406bfebdeSXin LI 				    va_list ap)
2250e3d5408SPeter Wemm {
226*21817992SBaptiste Daroussin     char *result = NULL;
2274a1a9510SRong-En Fan 
228*21817992SBaptiste Daroussin     if (SP_PARM != NULL && fmt != NULL) {
2290e3d5408SPeter Wemm #if USE_SAFE_SPRINTF
23006bfebdeSXin LI 	va_list ap2;
23106bfebdeSXin LI 	int len;
23206bfebdeSXin LI 
23306bfebdeSXin LI 	begin_va_copy(ap2, ap);
23406bfebdeSXin LI 	len = _nc_printf_length(fmt, ap2);
23506bfebdeSXin LI 	end_va_copy(ap2);
2360e3d5408SPeter Wemm 
2375ca44d1cSRong-En Fan 	if ((int) my_length < len + 1) {
238aae38d10SBaptiste Daroussin 	    my_length = (size_t) (2 * (len + 1));
2395ca44d1cSRong-En Fan 	    my_buffer = typeRealloc(char, my_length, my_buffer);
2404a1a9510SRong-En Fan 	}
241*21817992SBaptiste Daroussin 	if (my_buffer != NULL) {
2425ca44d1cSRong-En Fan 	    *my_buffer = '\0';
2434a1a9510SRong-En Fan 	    if (len >= 0) {
2445ca44d1cSRong-En Fan 		vsprintf(my_buffer, fmt, ap);
2450e3d5408SPeter Wemm 	    }
2465ca44d1cSRong-En Fan 	    result = my_buffer;
2474a1a9510SRong-En Fan 	}
2480e3d5408SPeter Wemm #else
2495ca44d1cSRong-En Fan #define MyCols _nc_globals.safeprint_cols
2505ca44d1cSRong-En Fan #define MyRows _nc_globals.safeprint_rows
2510e3d5408SPeter Wemm 
25206bfebdeSXin LI 	if (screen_lines(SP_PARM) > MyRows || screen_columns(SP_PARM) > MyCols) {
25306bfebdeSXin LI 	    if (screen_lines(SP_PARM) > MyRows)
25406bfebdeSXin LI 		MyRows = screen_lines(SP_PARM);
25506bfebdeSXin LI 	    if (screen_columns(SP_PARM) > MyCols)
25606bfebdeSXin LI 		MyCols = screen_columns(SP_PARM);
25706bfebdeSXin LI 	    my_length = (size_t) (MyRows * (MyCols + 1)) + 1;
258*21817992SBaptiste Daroussin 	    if (my_length < 80)
259*21817992SBaptiste Daroussin 		my_length = 80;
2605ca44d1cSRong-En Fan 	    my_buffer = typeRealloc(char, my_length, my_buffer);
2610e3d5408SPeter Wemm 	}
2620e3d5408SPeter Wemm 
263*21817992SBaptiste Daroussin 	if (my_buffer != NULL) {
2640e3d5408SPeter Wemm # if HAVE_VSNPRINTF
265*21817992SBaptiste Daroussin 	    /* SUSv2, 1997 */
266*21817992SBaptiste Daroussin 	    int used;
267*21817992SBaptiste Daroussin 
268*21817992SBaptiste Daroussin 	    do {
269*21817992SBaptiste Daroussin 		va_list ap2;
270*21817992SBaptiste Daroussin 
271*21817992SBaptiste Daroussin 		begin_va_copy(ap2, ap);
272*21817992SBaptiste Daroussin 		used = vsnprintf(my_buffer, my_length, fmt, ap2);
273*21817992SBaptiste Daroussin 		end_va_copy(ap2);
274*21817992SBaptiste Daroussin 		if (used < (int) my_length)
275*21817992SBaptiste Daroussin 		    break;
276*21817992SBaptiste Daroussin 		my_length = (size_t) ((3 * used) / 2);
277*21817992SBaptiste Daroussin 		my_buffer = typeRealloc(char, my_length, my_buffer);
278*21817992SBaptiste Daroussin 	    } while (my_buffer != NULL);
2790e3d5408SPeter Wemm # else
280*21817992SBaptiste Daroussin 	    /* ISO/ANSI C, 1989 */
281*21817992SBaptiste Daroussin 	    vsprintf(my_buffer, fmt, ap);
2820e3d5408SPeter Wemm # endif
2835ca44d1cSRong-En Fan 	    result = my_buffer;
2840e3d5408SPeter Wemm 	}
2850e3d5408SPeter Wemm #endif
286*21817992SBaptiste Daroussin     } else if (my_buffer != NULL) {	/* see _nc_freeall() */
2875ca44d1cSRong-En Fan 	free(my_buffer);
288*21817992SBaptiste Daroussin 	my_buffer = NULL;
2895ca44d1cSRong-En Fan 	my_length = 0;
2904a1a9510SRong-En Fan     }
2914a1a9510SRong-En Fan     return result;
2920e3d5408SPeter Wemm }
29306bfebdeSXin LI 
29406bfebdeSXin LI #if NCURSES_SP_FUNCS
29506bfebdeSXin LI NCURSES_EXPORT(char *)
_nc_printf_string(const char * fmt,va_list ap)29606bfebdeSXin LI _nc_printf_string(const char *fmt, va_list ap)
29706bfebdeSXin LI {
29806bfebdeSXin LI     return NCURSES_SP_NAME(_nc_printf_string) (CURRENT_SCREEN, fmt, ap);
29906bfebdeSXin LI }
30006bfebdeSXin LI #endif
301