xref: /openbsd-src/lib/libcurses/base/vsscanf.c (revision c7ef0cfc17afcba97172c25e1e3a943e893bc632)
1*c7ef0cfcSnicm /* $OpenBSD: vsscanf.c,v 1.3 2023/10/17 09:52:09 nicm Exp $ */
281d8c4e1Snicm 
381d8c4e1Snicm /****************************************************************************
4*c7ef0cfcSnicm  * Copyright 2020 Thomas E. Dickey                                          *
5*c7ef0cfcSnicm  * Copyright 1998-2004,2012 Free Software Foundation, Inc.                  *
681d8c4e1Snicm  *                                                                          *
781d8c4e1Snicm  * Permission is hereby granted, free of charge, to any person obtaining a  *
881d8c4e1Snicm  * copy of this software and associated documentation files (the            *
981d8c4e1Snicm  * "Software"), to deal in the Software without restriction, including      *
1081d8c4e1Snicm  * without limitation the rights to use, copy, modify, merge, publish,      *
1181d8c4e1Snicm  * distribute, distribute with modifications, sublicense, and/or sell       *
1281d8c4e1Snicm  * copies of the Software, and to permit persons to whom the Software is    *
1381d8c4e1Snicm  * furnished to do so, subject to the following conditions:                 *
1481d8c4e1Snicm  *                                                                          *
1581d8c4e1Snicm  * The above copyright notice and this permission notice shall be included  *
1681d8c4e1Snicm  * in all copies or substantial portions of the Software.                   *
1781d8c4e1Snicm  *                                                                          *
1881d8c4e1Snicm  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
1981d8c4e1Snicm  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
2081d8c4e1Snicm  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
2181d8c4e1Snicm  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
2281d8c4e1Snicm  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
2381d8c4e1Snicm  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
2481d8c4e1Snicm  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
2581d8c4e1Snicm  *                                                                          *
2681d8c4e1Snicm  * Except as contained in this notice, the name(s) of the above copyright   *
2781d8c4e1Snicm  * holders shall not be used in advertising or otherwise to promote the     *
2881d8c4e1Snicm  * sale, use or other dealings in this Software without prior written       *
2981d8c4e1Snicm  * authorization.                                                           *
3081d8c4e1Snicm  ****************************************************************************/
3181d8c4e1Snicm 
3281d8c4e1Snicm /****************************************************************************
3381d8c4e1Snicm  *  State-machine fallback written by Thomas E. Dickey 2002                 *
3481d8c4e1Snicm  ****************************************************************************/
3581d8c4e1Snicm 
3681d8c4e1Snicm /*
3781d8c4e1Snicm  * This function is needed to support vwscanw
3881d8c4e1Snicm  */
3981d8c4e1Snicm 
4081d8c4e1Snicm #include <curses.priv.h>
4181d8c4e1Snicm 
4281d8c4e1Snicm #if !HAVE_VSSCANF
4381d8c4e1Snicm 
44*c7ef0cfcSnicm MODULE_ID("$Id: vsscanf.c,v 1.3 2023/10/17 09:52:09 nicm Exp $")
4581d8c4e1Snicm 
4681d8c4e1Snicm #if !(HAVE_VFSCANF || HAVE__DOSCAN)
4781d8c4e1Snicm 
4881d8c4e1Snicm #include <ctype.h>
4981d8c4e1Snicm 
5081d8c4e1Snicm #define L_SQUARE '['
5181d8c4e1Snicm #define R_SQUARE ']'
5281d8c4e1Snicm 
5381d8c4e1Snicm typedef enum {
5481d8c4e1Snicm     cUnknown
5581d8c4e1Snicm     ,cError			/* anything that isn't ANSI */
5681d8c4e1Snicm     ,cAssigned
5781d8c4e1Snicm     ,cChar
5881d8c4e1Snicm     ,cInt
5981d8c4e1Snicm     ,cFloat
6081d8c4e1Snicm     ,cDouble
6181d8c4e1Snicm     ,cPointer
6281d8c4e1Snicm     ,cLong
6381d8c4e1Snicm     ,cShort
6481d8c4e1Snicm     ,cRange
6581d8c4e1Snicm     ,cString
6681d8c4e1Snicm } ChunkType;
6781d8c4e1Snicm 
6881d8c4e1Snicm typedef enum {
6981d8c4e1Snicm     oUnknown
7081d8c4e1Snicm     ,oShort
7181d8c4e1Snicm     ,oLong
7281d8c4e1Snicm } OtherType;
7381d8c4e1Snicm 
7481d8c4e1Snicm typedef enum {
7581d8c4e1Snicm     sUnknown
7681d8c4e1Snicm     ,sPercent			/* last was '%' beginning a format */
7781d8c4e1Snicm     ,sNormal			/* ...somewhere in the middle */
7881d8c4e1Snicm     ,sLeft			/* last was left square bracket beginning a range */
7981d8c4e1Snicm     ,sRange			/* ...somewhere in the middle */
8081d8c4e1Snicm     ,sFinal			/* last finished a format */
8181d8c4e1Snicm } ScanState;
8281d8c4e1Snicm 
8381d8c4e1Snicm static ChunkType
final_ch(int ch,OtherType other)8481d8c4e1Snicm final_ch(int ch, OtherType other)
8581d8c4e1Snicm {
8681d8c4e1Snicm     ChunkType result = cUnknown;
8781d8c4e1Snicm 
8881d8c4e1Snicm     switch (ch) {
8981d8c4e1Snicm     case 'c':
9081d8c4e1Snicm 	if (other == oUnknown)
9181d8c4e1Snicm 	    result = cChar;
9281d8c4e1Snicm 	else
9381d8c4e1Snicm 	    result = cError;
9481d8c4e1Snicm 	break;
9581d8c4e1Snicm     case 'd':
9681d8c4e1Snicm     case 'i':
9781d8c4e1Snicm     case 'X':
9881d8c4e1Snicm     case 'x':
9981d8c4e1Snicm 	switch (other) {
10081d8c4e1Snicm 	case oUnknown:
10181d8c4e1Snicm 	    result = cInt;
10281d8c4e1Snicm 	    break;
10381d8c4e1Snicm 	case oShort:
10481d8c4e1Snicm 	    result = cShort;
10581d8c4e1Snicm 	    break;
10681d8c4e1Snicm 	case oLong:
10781d8c4e1Snicm 	    result = cLong;
10881d8c4e1Snicm 	    break;
10981d8c4e1Snicm 	}
11081d8c4e1Snicm 	break;
11181d8c4e1Snicm     case 'E':
11281d8c4e1Snicm     case 'e':
11381d8c4e1Snicm     case 'f':
11481d8c4e1Snicm     case 'g':
11581d8c4e1Snicm 	switch (other) {
11681d8c4e1Snicm 	case oUnknown:
11781d8c4e1Snicm 	    result = cFloat;
11881d8c4e1Snicm 	    break;
11981d8c4e1Snicm 	case oShort:
12081d8c4e1Snicm 	    result = cError;
12181d8c4e1Snicm 	    break;
12281d8c4e1Snicm 	case oLong:
12381d8c4e1Snicm 	    result = cDouble;
12481d8c4e1Snicm 	    break;
12581d8c4e1Snicm 	}
12681d8c4e1Snicm 	break;
12781d8c4e1Snicm     case 'n':
12881d8c4e1Snicm 	if (other == oUnknown)
12981d8c4e1Snicm 	    result = cAssigned;
13081d8c4e1Snicm 	else
13181d8c4e1Snicm 	    result = cError;
13281d8c4e1Snicm 	break;
13381d8c4e1Snicm     case 'p':
13481d8c4e1Snicm 	if (other == oUnknown)
13581d8c4e1Snicm 	    result = cPointer;
13681d8c4e1Snicm 	else
13781d8c4e1Snicm 	    result = cError;
13881d8c4e1Snicm 	break;
13981d8c4e1Snicm     case 's':
14081d8c4e1Snicm 	if (other == oUnknown)
14181d8c4e1Snicm 	    result = cString;
14281d8c4e1Snicm 	else
14381d8c4e1Snicm 	    result = cError;
14481d8c4e1Snicm 	break;
14581d8c4e1Snicm     }
14681d8c4e1Snicm     return result;
14781d8c4e1Snicm }
14881d8c4e1Snicm 
14981d8c4e1Snicm static OtherType
other_ch(int ch)15081d8c4e1Snicm other_ch(int ch)
15181d8c4e1Snicm {
15281d8c4e1Snicm     OtherType result = oUnknown;
15381d8c4e1Snicm     switch (ch) {
15481d8c4e1Snicm     case 'h':
15581d8c4e1Snicm 	result = oShort;
15681d8c4e1Snicm 	break;
15781d8c4e1Snicm     case 'l':
15881d8c4e1Snicm 	result = oLong;
15981d8c4e1Snicm 	break;
16081d8c4e1Snicm     }
16181d8c4e1Snicm     return result;
16281d8c4e1Snicm }
16381d8c4e1Snicm #endif
16481d8c4e1Snicm 
165*c7ef0cfcSnicm /*VARARGS2*/
16681d8c4e1Snicm NCURSES_EXPORT(int)
vsscanf(const char * str,const char * format,va_list ap)16781d8c4e1Snicm vsscanf(const char *str, const char *format, va_list ap)
16881d8c4e1Snicm {
16981d8c4e1Snicm #if HAVE_VFSCANF || HAVE__DOSCAN
17081d8c4e1Snicm     /*
17181d8c4e1Snicm      * This code should work on anything descended from AT&T SVr1.
17281d8c4e1Snicm      */
17381d8c4e1Snicm     FILE strbuf;
17481d8c4e1Snicm 
17581d8c4e1Snicm     strbuf._flag = _IOREAD;
17681d8c4e1Snicm     strbuf._ptr = strbuf._base = (unsigned char *) str;
17781d8c4e1Snicm     strbuf._cnt = strlen(str);
17881d8c4e1Snicm     strbuf._file = _NFILE;
17981d8c4e1Snicm 
18081d8c4e1Snicm #if HAVE_VFSCANF
18181d8c4e1Snicm     return (vfscanf(&strbuf, format, ap));
18281d8c4e1Snicm #else
18381d8c4e1Snicm     return (_doscan(&strbuf, format, ap));
18481d8c4e1Snicm #endif
18581d8c4e1Snicm #else
18681d8c4e1Snicm     static int can_convert = -1;
18781d8c4e1Snicm 
18881d8c4e1Snicm     int assigned = 0;
18981d8c4e1Snicm     int consumed = 0;
19081d8c4e1Snicm 
19181d8c4e1Snicm     T((T_CALLED("vsscanf(%s,%s,...)"),
19281d8c4e1Snicm        _nc_visbuf2(1, str),
19381d8c4e1Snicm        _nc_visbuf2(2, format)));
19481d8c4e1Snicm 
19581d8c4e1Snicm     /*
19681d8c4e1Snicm      * This relies on having a working "%n" format conversion.  Check if it
19781d8c4e1Snicm      * works.  Only very old C libraries do not support it.
19881d8c4e1Snicm      *
19981d8c4e1Snicm      * FIXME: move this check into the configure script.
20081d8c4e1Snicm      */
20181d8c4e1Snicm     if (can_convert < 0) {
20281d8c4e1Snicm 	int check1;
20381d8c4e1Snicm 	int check2;
20481d8c4e1Snicm 	if (sscanf("123", "%d%n", &check1, &check2) > 0
20581d8c4e1Snicm 	    && check1 == 123
20681d8c4e1Snicm 	    && check2 == 3) {
20781d8c4e1Snicm 	    can_convert = 1;
20881d8c4e1Snicm 	} else {
20981d8c4e1Snicm 	    can_convert = 0;
21081d8c4e1Snicm 	}
21181d8c4e1Snicm     }
21281d8c4e1Snicm 
21381d8c4e1Snicm     if (can_convert) {
21481d8c4e1Snicm 	size_t len_fmt = strlen(format) + 32;
21581d8c4e1Snicm 	char *my_fmt = malloc(len_fmt);
21681d8c4e1Snicm 	ChunkType chunk, ctest;
21781d8c4e1Snicm 	OtherType other, otest;
21881d8c4e1Snicm 	ScanState state;
21981d8c4e1Snicm 	unsigned n;
22081d8c4e1Snicm 	int eaten;
22181d8c4e1Snicm 	void *pointer;
22281d8c4e1Snicm 
22381d8c4e1Snicm 	if (my_fmt != 0) {
22481d8c4e1Snicm 	    /*
22581d8c4e1Snicm 	     * Split the original format into chunks, adding a "%n" to the end
22681d8c4e1Snicm 	     * of each (except of course if it used %n), and use that
22781d8c4e1Snicm 	     * information to decide where to start scanning the next chunk.
22881d8c4e1Snicm 	     *
22981d8c4e1Snicm 	     * FIXME:  does %n count bytes or characters?  If the latter, this
23081d8c4e1Snicm 	     * will require further work for multibyte strings.
23181d8c4e1Snicm 	     */
23281d8c4e1Snicm 	    while (*format != '\0') {
23381d8c4e1Snicm 		/* find a chunk */
23481d8c4e1Snicm 		state = sUnknown;
23581d8c4e1Snicm 		chunk = cUnknown;
23681d8c4e1Snicm 		other = oUnknown;
23781d8c4e1Snicm 		pointer = 0;
23881d8c4e1Snicm 		for (n = 0; format[n] != 0 && state != sFinal; ++n) {
23981d8c4e1Snicm 		    my_fmt[n] = format[n];
24081d8c4e1Snicm 		    switch (state) {
24181d8c4e1Snicm 		    case sUnknown:
24281d8c4e1Snicm 			if (format[n] == '%')
24381d8c4e1Snicm 			    state = sPercent;
24481d8c4e1Snicm 			break;
24581d8c4e1Snicm 		    case sPercent:
24681d8c4e1Snicm 			if (format[n] == '%') {
24781d8c4e1Snicm 			    state = sUnknown;
24881d8c4e1Snicm 			} else if (format[n] == L_SQUARE) {
24981d8c4e1Snicm 			    state = sLeft;
25081d8c4e1Snicm 			} else {
25181d8c4e1Snicm 			    state = sNormal;
25281d8c4e1Snicm 			    --n;
25381d8c4e1Snicm 			}
25481d8c4e1Snicm 			break;
25581d8c4e1Snicm 		    case sLeft:
25681d8c4e1Snicm 			state = sRange;
25781d8c4e1Snicm 			if (format[n] == '^') {
25881d8c4e1Snicm 			    ++n;
25981d8c4e1Snicm 			    my_fmt[n] = format[n];
26081d8c4e1Snicm 			}
26181d8c4e1Snicm 			break;
26281d8c4e1Snicm 		    case sRange:
26381d8c4e1Snicm 			if (format[n] == R_SQUARE) {
26481d8c4e1Snicm 			    state = sFinal;
26581d8c4e1Snicm 			    chunk = cRange;
26681d8c4e1Snicm 			}
26781d8c4e1Snicm 			break;
26881d8c4e1Snicm 		    case sNormal:
26981d8c4e1Snicm 			if (format[n] == '*') {
27081d8c4e1Snicm 			    state = sUnknown;
27181d8c4e1Snicm 			} else {
27281d8c4e1Snicm 			    if ((ctest = final_ch(format[n], other)) != cUnknown) {
27381d8c4e1Snicm 				state = sFinal;
27481d8c4e1Snicm 				chunk = ctest;
27581d8c4e1Snicm 			    } else if ((otest = other_ch(format[n])) != oUnknown) {
27681d8c4e1Snicm 				other = otest;
27781d8c4e1Snicm 			    } else if (isalpha(UChar(format[n]))) {
27881d8c4e1Snicm 				state = sFinal;
27981d8c4e1Snicm 				chunk = cError;
28081d8c4e1Snicm 			    }
28181d8c4e1Snicm 			}
28281d8c4e1Snicm 			break;
28381d8c4e1Snicm 		    case sFinal:
28481d8c4e1Snicm 			break;
28581d8c4e1Snicm 		    }
28681d8c4e1Snicm 		}
28781d8c4e1Snicm 		my_fmt[n] = '\0';
28881d8c4e1Snicm 		format += n;
28981d8c4e1Snicm 
29081d8c4e1Snicm 		if (chunk == cUnknown
29181d8c4e1Snicm 		    || chunk == cError) {
29281d8c4e1Snicm 		    if (assigned == 0)
29381d8c4e1Snicm 			assigned = EOF;
29481d8c4e1Snicm 		    break;
29581d8c4e1Snicm 		}
29681d8c4e1Snicm 
29781d8c4e1Snicm 		/* add %n, if the format was not that */
29881d8c4e1Snicm 		if (chunk != cAssigned) {
299*c7ef0cfcSnicm 		    _nc_STRCAT(my_fmt, "%n", len_fmt);
30081d8c4e1Snicm 		}
30181d8c4e1Snicm 
30281d8c4e1Snicm 		switch (chunk) {
30381d8c4e1Snicm 		case cAssigned:
304*c7ef0cfcSnicm 		    _nc_STRCAT(my_fmt, "%n", len_fmt);
30581d8c4e1Snicm 		    pointer = &eaten;
30681d8c4e1Snicm 		    break;
30781d8c4e1Snicm 		case cInt:
30881d8c4e1Snicm 		    pointer = va_arg(ap, int *);
30981d8c4e1Snicm 		    break;
31081d8c4e1Snicm 		case cShort:
31181d8c4e1Snicm 		    pointer = va_arg(ap, short *);
31281d8c4e1Snicm 		    break;
31381d8c4e1Snicm 		case cFloat:
31481d8c4e1Snicm 		    pointer = va_arg(ap, float *);
31581d8c4e1Snicm 		    break;
31681d8c4e1Snicm 		case cDouble:
31781d8c4e1Snicm 		    pointer = va_arg(ap, double *);
31881d8c4e1Snicm 		    break;
31981d8c4e1Snicm 		case cLong:
32081d8c4e1Snicm 		    pointer = va_arg(ap, long *);
32181d8c4e1Snicm 		    break;
32281d8c4e1Snicm 		case cPointer:
32381d8c4e1Snicm 		    pointer = va_arg(ap, void *);
32481d8c4e1Snicm 		    break;
32581d8c4e1Snicm 		case cChar:
32681d8c4e1Snicm 		case cRange:
32781d8c4e1Snicm 		case cString:
32881d8c4e1Snicm 		    pointer = va_arg(ap, char *);
32981d8c4e1Snicm 		    break;
33081d8c4e1Snicm 		case cError:
33181d8c4e1Snicm 		case cUnknown:
33281d8c4e1Snicm 		    break;
33381d8c4e1Snicm 		}
33481d8c4e1Snicm 		/* do the conversion */
33581d8c4e1Snicm 		T(("...converting chunk #%d type %d(%s,%s)",
33681d8c4e1Snicm 		   assigned + 1, chunk,
33781d8c4e1Snicm 		   _nc_visbuf2(1, str + consumed),
33881d8c4e1Snicm 		   _nc_visbuf2(2, my_fmt)));
33981d8c4e1Snicm 		if (sscanf(str + consumed, my_fmt, pointer, &eaten) > 0)
34081d8c4e1Snicm 		    consumed += eaten;
34181d8c4e1Snicm 		else
34281d8c4e1Snicm 		    break;
34381d8c4e1Snicm 		++assigned;
34481d8c4e1Snicm 	    }
34581d8c4e1Snicm 	    free(my_fmt);
34681d8c4e1Snicm 	}
34781d8c4e1Snicm     }
34881d8c4e1Snicm     returnCode(assigned);
34981d8c4e1Snicm #endif
35081d8c4e1Snicm }
35181d8c4e1Snicm #else
35281d8c4e1Snicm extern
35381d8c4e1Snicm NCURSES_EXPORT(void)
35481d8c4e1Snicm _nc_vsscanf(void);		/* quiet's gcc warning */
35581d8c4e1Snicm NCURSES_EXPORT(void)
_nc_vsscanf(void)35681d8c4e1Snicm _nc_vsscanf(void)
35781d8c4e1Snicm {
35881d8c4e1Snicm }				/* nonempty for strict ANSI compilers */
35981d8c4e1Snicm #endif /* !HAVE_VSSCANF */
360