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