197049Speter/****************************************************************************
2262685Sdelphij * Copyright (c) 1998-2004,2012 Free Software Foundation, Inc.              *
397049Speter *                                                                          *
497049Speter * Permission is hereby granted, free of charge, to any person obtaining a  *
597049Speter * copy of this software and associated documentation files (the            *
697049Speter * "Software"), to deal in the Software without restriction, including      *
797049Speter * without limitation the rights to use, copy, modify, merge, publish,      *
897049Speter * distribute, distribute with modifications, sublicense, and/or sell       *
997049Speter * copies of the Software, and to permit persons to whom the Software is    *
1097049Speter * furnished to do so, subject to the following conditions:                 *
1197049Speter *                                                                          *
1297049Speter * The above copyright notice and this permission notice shall be included  *
1397049Speter * in all copies or substantial portions of the Software.                   *
1497049Speter *                                                                          *
1597049Speter * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
1697049Speter * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
1797049Speter * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
1897049Speter * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
1997049Speter * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
2097049Speter * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
2197049Speter * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
2297049Speter *                                                                          *
2397049Speter * Except as contained in this notice, the name(s) of the above copyright   *
2497049Speter * holders shall not be used in advertising or otherwise to promote the     *
2597049Speter * sale, use or other dealings in this Software without prior written       *
2697049Speter * authorization.                                                           *
2797049Speter ****************************************************************************/
2897049Speter
2997049Speter/****************************************************************************
3097049Speter *  State-machine fallback written by Thomas E. Dickey 2002                 *
3197049Speter ****************************************************************************/
3297049Speter
3350276Speter/*
3450276Speter * This function is needed to support vwscanw
3550276Speter */
3650276Speter
3750276Speter#include <curses.priv.h>
3850276Speter
3950276Speter#if !HAVE_VSSCANF
4050276Speter
41262685SdelphijMODULE_ID("$Id: vsscanf.c,v 1.20 2012/02/22 22:26:58 tom Exp $")
4250276Speter
4397049Speter#if !(HAVE_VFSCANF || HAVE__DOSCAN)
4497049Speter
4597049Speter#include <ctype.h>
4697049Speter
4797049Speter#define L_SQUARE '['
4897049Speter#define R_SQUARE ']'
4997049Speter
5097049Spetertypedef enum {
5197049Speter    cUnknown
5297049Speter    ,cError			/* anything that isn't ANSI */
5397049Speter    ,cAssigned
5497049Speter    ,cChar
5597049Speter    ,cInt
5697049Speter    ,cFloat
5797049Speter    ,cDouble
5897049Speter    ,cPointer
5997049Speter    ,cLong
6097049Speter    ,cShort
6197049Speter    ,cRange
6297049Speter    ,cString
6397049Speter} ChunkType;
6497049Speter
6597049Spetertypedef enum {
6697049Speter    oUnknown
6797049Speter    ,oShort
6897049Speter    ,oLong
6997049Speter} OtherType;
7097049Speter
7197049Spetertypedef enum {
7297049Speter    sUnknown
7397049Speter    ,sPercent			/* last was '%' beginning a format */
7497049Speter    ,sNormal			/* ...somewhere in the middle */
7597049Speter    ,sLeft			/* last was left square bracket beginning a range */
7697049Speter    ,sRange			/* ...somewhere in the middle */
7797049Speter    ,sFinal			/* last finished a format */
7897049Speter} ScanState;
7997049Speter
8097049Speterstatic ChunkType
8197049Speterfinal_ch(int ch, OtherType other)
8297049Speter{
8397049Speter    ChunkType result = cUnknown;
8497049Speter
8597049Speter    switch (ch) {
8697049Speter    case 'c':
8797049Speter	if (other == oUnknown)
8897049Speter	    result = cChar;
8997049Speter	else
9097049Speter	    result = cError;
9197049Speter	break;
9297049Speter    case 'd':
9397049Speter    case 'i':
9497049Speter    case 'X':
9597049Speter    case 'x':
9697049Speter	switch (other) {
9797049Speter	case oUnknown:
9897049Speter	    result = cInt;
9997049Speter	    break;
10097049Speter	case oShort:
10197049Speter	    result = cShort;
10297049Speter	    break;
10397049Speter	case oLong:
10497049Speter	    result = cLong;
10597049Speter	    break;
10697049Speter	}
10797049Speter	break;
10897049Speter    case 'E':
10997049Speter    case 'e':
11097049Speter    case 'f':
11197049Speter    case 'g':
11297049Speter	switch (other) {
11397049Speter	case oUnknown:
11497049Speter	    result = cFloat;
11597049Speter	    break;
11697049Speter	case oShort:
11797049Speter	    result = cError;
11897049Speter	    break;
11997049Speter	case oLong:
12097049Speter	    result = cDouble;
12197049Speter	    break;
12297049Speter	}
12397049Speter	break;
12497049Speter    case 'n':
12597049Speter	if (other == oUnknown)
12697049Speter	    result = cAssigned;
12797049Speter	else
12897049Speter	    result = cError;
12997049Speter	break;
13097049Speter    case 'p':
13197049Speter	if (other == oUnknown)
13297049Speter	    result = cPointer;
13397049Speter	else
13497049Speter	    result = cError;
13597049Speter	break;
13697049Speter    case 's':
13797049Speter	if (other == oUnknown)
13897049Speter	    result = cString;
13997049Speter	else
14097049Speter	    result = cError;
14197049Speter	break;
14297049Speter    }
14397049Speter    return result;
14497049Speter}
14597049Speter
14697049Speterstatic OtherType
14797049Speterother_ch(int ch)
14897049Speter{
14997049Speter    OtherType result = oUnknown;
15097049Speter    switch (ch) {
15197049Speter    case 'h':
15297049Speter	result = oShort;
15397049Speter	break;
15497049Speter    case 'l':
15597049Speter	result = oLong;
15697049Speter	break;
15797049Speter    }
15897049Speter    return result;
15997049Speter}
16097049Speter#endif
16197049Speter
16250276Speter/*VARARGS2*/
16376726SpeterNCURSES_EXPORT(int)
16497049Spetervsscanf(const char *str, const char *format, va_list ap)
16550276Speter{
16697049Speter#if HAVE_VFSCANF || HAVE__DOSCAN
16776726Speter    /*
16876726Speter     * This code should work on anything descended from AT&T SVr1.
16976726Speter     */
17076726Speter    FILE strbuf;
17150276Speter
17276726Speter    strbuf._flag = _IOREAD;
17376726Speter    strbuf._ptr = strbuf._base = (unsigned char *) str;
17476726Speter    strbuf._cnt = strlen(str);
17576726Speter    strbuf._file = _NFILE;
17650276Speter
17750276Speter#if HAVE_VFSCANF
17876726Speter    return (vfscanf(&strbuf, format, ap));
17950276Speter#else
18076726Speter    return (_doscan(&strbuf, format, ap));
18150276Speter#endif
18250276Speter#else
18397049Speter    static int can_convert = -1;
18497049Speter
18597049Speter    int assigned = 0;
18697049Speter    int consumed = 0;
18797049Speter
18897049Speter    T((T_CALLED("vsscanf(%s,%s,...)"),
18997049Speter       _nc_visbuf2(1, str),
19097049Speter       _nc_visbuf2(2, format)));
19197049Speter
19276726Speter    /*
19397049Speter     * This relies on having a working "%n" format conversion.  Check if it
19497049Speter     * works.  Only very old C libraries do not support it.
19597049Speter     *
19697049Speter     * FIXME: move this check into the configure script.
19776726Speter     */
19897049Speter    if (can_convert < 0) {
19997049Speter	int check1;
20097049Speter	int check2;
20197049Speter	if (sscanf("123", "%d%n", &check1, &check2) > 0
20297049Speter	    && check1 == 123
20397049Speter	    && check2 == 3) {
20497049Speter	    can_convert = 1;
20597049Speter	} else {
20697049Speter	    can_convert = 0;
20797049Speter	}
20897049Speter    }
20997049Speter
21097049Speter    if (can_convert) {
21197049Speter	size_t len_fmt = strlen(format) + 32;
21297049Speter	char *my_fmt = malloc(len_fmt);
213166124Srafan	ChunkType chunk, ctest;
214166124Srafan	OtherType other, otest;
21597049Speter	ScanState state;
21697049Speter	unsigned n;
21797049Speter	int eaten;
21897049Speter	void *pointer;
21997049Speter
22097049Speter	if (my_fmt != 0) {
22197049Speter	    /*
22297049Speter	     * Split the original format into chunks, adding a "%n" to the end
22397049Speter	     * of each (except of course if it used %n), and use that
22497049Speter	     * information to decide where to start scanning the next chunk.
22597049Speter	     *
22697049Speter	     * FIXME:  does %n count bytes or characters?  If the latter, this
22797049Speter	     * will require further work for multibyte strings.
22897049Speter	     */
22997049Speter	    while (*format != '\0') {
23097049Speter		/* find a chunk */
23197049Speter		state = sUnknown;
23297049Speter		chunk = cUnknown;
233166124Srafan		other = oUnknown;
23497049Speter		pointer = 0;
23597049Speter		for (n = 0; format[n] != 0 && state != sFinal; ++n) {
23697049Speter		    my_fmt[n] = format[n];
23797049Speter		    switch (state) {
23897049Speter		    case sUnknown:
23997049Speter			if (format[n] == '%')
24097049Speter			    state = sPercent;
24197049Speter			break;
24297049Speter		    case sPercent:
24397049Speter			if (format[n] == '%') {
24497049Speter			    state = sUnknown;
24597049Speter			} else if (format[n] == L_SQUARE) {
24697049Speter			    state = sLeft;
24797049Speter			} else {
24897049Speter			    state = sNormal;
24997049Speter			    --n;
25097049Speter			}
25197049Speter			break;
25297049Speter		    case sLeft:
25397049Speter			state = sRange;
25497049Speter			if (format[n] == '^') {
25597049Speter			    ++n;
25697049Speter			    my_fmt[n] = format[n];
25797049Speter			}
25897049Speter			break;
25997049Speter		    case sRange:
26097049Speter			if (format[n] == R_SQUARE) {
26197049Speter			    state = sFinal;
26297049Speter			    chunk = cRange;
26397049Speter			}
26497049Speter			break;
26597049Speter		    case sNormal:
26697049Speter			if (format[n] == '*') {
26797049Speter			    state = sUnknown;
26897049Speter			} else {
269166124Srafan			    if ((ctest = final_ch(format[n], other)) != cUnknown) {
27097049Speter				state = sFinal;
271166124Srafan				chunk = ctest;
272166124Srafan			    } else if ((otest = other_ch(format[n])) != oUnknown) {
273166124Srafan				other = otest;
274166124Srafan			    } else if (isalpha(UChar(format[n]))) {
27597049Speter				state = sFinal;
27697049Speter				chunk = cError;
27797049Speter			    }
27897049Speter			}
27997049Speter			break;
28097049Speter		    case sFinal:
28197049Speter			break;
28297049Speter		    }
28397049Speter		}
28497049Speter		my_fmt[n] = '\0';
28597049Speter		format += n;
28697049Speter
28797049Speter		if (chunk == cUnknown
28897049Speter		    || chunk == cError) {
28997049Speter		    if (assigned == 0)
29097049Speter			assigned = EOF;
29197049Speter		    break;
29297049Speter		}
29397049Speter
29497049Speter		/* add %n, if the format was not that */
29597049Speter		if (chunk != cAssigned) {
296262685Sdelphij		    _nc_STRCAT(my_fmt, "%n", len_fmt);
29797049Speter		}
29897049Speter
29997049Speter		switch (chunk) {
30097049Speter		case cAssigned:
301262685Sdelphij		    _nc_STRCAT(my_fmt, "%n", len_fmt);
30297049Speter		    pointer = &eaten;
30397049Speter		    break;
30497049Speter		case cInt:
30597049Speter		    pointer = va_arg(ap, int *);
30697049Speter		    break;
30797049Speter		case cShort:
30897049Speter		    pointer = va_arg(ap, short *);
30997049Speter		    break;
31097049Speter		case cFloat:
31197049Speter		    pointer = va_arg(ap, float *);
31297049Speter		    break;
31397049Speter		case cDouble:
31497049Speter		    pointer = va_arg(ap, double *);
31597049Speter		    break;
31697049Speter		case cLong:
31797049Speter		    pointer = va_arg(ap, long *);
31897049Speter		    break;
31997049Speter		case cPointer:
32097049Speter		    pointer = va_arg(ap, void *);
32197049Speter		    break;
32297049Speter		case cChar:
32397049Speter		case cRange:
32497049Speter		case cString:
32597049Speter		    pointer = va_arg(ap, char *);
32697049Speter		    break;
32797049Speter		case cError:
32897049Speter		case cUnknown:
32997049Speter		    break;
33097049Speter		}
33197049Speter		/* do the conversion */
33297049Speter		T(("...converting chunk #%d type %d(%s,%s)",
33397049Speter		   assigned + 1, chunk,
33497049Speter		   _nc_visbuf2(1, str + consumed),
33597049Speter		   _nc_visbuf2(2, my_fmt)));
33697049Speter		if (sscanf(str + consumed, my_fmt, pointer, &eaten) > 0)
33797049Speter		    consumed += eaten;
33897049Speter		else
33997049Speter		    break;
34097049Speter		++assigned;
34197049Speter	    }
34297049Speter	    free(my_fmt);
34397049Speter	}
34497049Speter    }
34597049Speter    returnCode(assigned);
34697049Speter#endif
34750276Speter}
34850276Speter#else
34976726Speterextern
35076726SpeterNCURSES_EXPORT(void)
35176726Speter_nc_vsscanf(void);		/* quiet's gcc warning */
35276726SpeterNCURSES_EXPORT(void)
35376726Speter_nc_vsscanf(void)
35476726Speter{
35576726Speter}				/* nonempty for strict ANSI compilers */
35650276Speter#endif /* !HAVE_VSSCANF */
357