vsscanf.c revision 97049
197049Speter/****************************************************************************
297049Speter * Copyright (c) 1998,2000,2001,2002 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
4197049SpeterMODULE_ID("$Id: vsscanf.c,v 1.15 2002/02/03 00:49:45 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);
21397049Speter	ChunkType other, chunk, check;
21497049Speter	ScanState state;
21597049Speter	unsigned n;
21697049Speter	int eaten;
21797049Speter	void *pointer;
21897049Speter
21997049Speter	if (my_fmt != 0) {
22097049Speter	    /*
22197049Speter	     * Split the original format into chunks, adding a "%n" to the end
22297049Speter	     * of each (except of course if it used %n), and use that
22397049Speter	     * information to decide where to start scanning the next chunk.
22497049Speter	     *
22597049Speter	     * FIXME:  does %n count bytes or characters?  If the latter, this
22697049Speter	     * will require further work for multibyte strings.
22797049Speter	     */
22897049Speter	    while (*format != '\0') {
22997049Speter		/* find a chunk */
23097049Speter		state = sUnknown;
23197049Speter		chunk = cUnknown;
23297049Speter		other = cUnknown;
23397049Speter		pointer = 0;
23497049Speter		for (n = 0; format[n] != 0 && state != sFinal; ++n) {
23597049Speter		    my_fmt[n] = format[n];
23697049Speter		    switch (state) {
23797049Speter		    case sUnknown:
23897049Speter			if (format[n] == '%')
23997049Speter			    state = sPercent;
24097049Speter			break;
24197049Speter		    case sPercent:
24297049Speter			if (format[n] == '%') {
24397049Speter			    state = sUnknown;
24497049Speter			} else if (format[n] == L_SQUARE) {
24597049Speter			    state = sLeft;
24697049Speter			} else {
24797049Speter			    state = sNormal;
24897049Speter			    --n;
24997049Speter			}
25097049Speter			break;
25197049Speter		    case sLeft:
25297049Speter			state = sRange;
25397049Speter			if (format[n] == '^') {
25497049Speter			    ++n;
25597049Speter			    my_fmt[n] = format[n];
25697049Speter			}
25797049Speter			break;
25897049Speter		    case sRange:
25997049Speter			if (format[n] == R_SQUARE) {
26097049Speter			    state = sFinal;
26197049Speter			    chunk = cRange;
26297049Speter			}
26397049Speter			break;
26497049Speter		    case sNormal:
26597049Speter			if (format[n] == '*') {
26697049Speter			    state = sUnknown;
26797049Speter			} else {
26897049Speter			    if ((check = final_ch(format[n], other)) != cUnknown) {
26997049Speter				state = sFinal;
27097049Speter				chunk = check;
27197049Speter			    } else if ((check = other_ch(format[n])) != oUnknown) {
27297049Speter				other = check;
27397049Speter			    } else if (isalpha(format[n])) {
27497049Speter				state = sFinal;
27597049Speter				chunk = cError;
27697049Speter			    }
27797049Speter			}
27897049Speter			break;
27997049Speter		    case sFinal:
28097049Speter			break;
28197049Speter		    }
28297049Speter		}
28397049Speter		my_fmt[n] = '\0';
28497049Speter		format += n;
28597049Speter
28697049Speter		if (chunk == cUnknown
28797049Speter		    || chunk == cError) {
28897049Speter		    if (assigned == 0)
28997049Speter			assigned = EOF;
29097049Speter		    break;
29197049Speter		}
29297049Speter
29397049Speter		/* add %n, if the format was not that */
29497049Speter		if (chunk != cAssigned) {
29597049Speter		    strcat(my_fmt, "%n");
29697049Speter		}
29797049Speter
29897049Speter		switch (chunk) {
29997049Speter		case cAssigned:
30097049Speter		    strcat(my_fmt, "%n");
30197049Speter		    pointer = &eaten;
30297049Speter		    break;
30397049Speter		case cInt:
30497049Speter		    pointer = va_arg(ap, int *);
30597049Speter		    break;
30697049Speter		case cShort:
30797049Speter		    pointer = va_arg(ap, short *);
30897049Speter		    break;
30997049Speter		case cFloat:
31097049Speter		    pointer = va_arg(ap, float *);
31197049Speter		    break;
31297049Speter		case cDouble:
31397049Speter		    pointer = va_arg(ap, double *);
31497049Speter		    break;
31597049Speter		case cLong:
31697049Speter		    pointer = va_arg(ap, long *);
31797049Speter		    break;
31897049Speter		case cPointer:
31997049Speter		    pointer = va_arg(ap, void *);
32097049Speter		    break;
32197049Speter		case cChar:
32297049Speter		case cRange:
32397049Speter		case cString:
32497049Speter		    pointer = va_arg(ap, char *);
32597049Speter		    break;
32697049Speter		case cError:
32797049Speter		case cUnknown:
32897049Speter		    break;
32997049Speter		}
33097049Speter		/* do the conversion */
33197049Speter		T(("...converting chunk #%d type %d(%s,%s)",
33297049Speter		   assigned + 1, chunk,
33397049Speter		   _nc_visbuf2(1, str + consumed),
33497049Speter		   _nc_visbuf2(2, my_fmt)));
33597049Speter		if (sscanf(str + consumed, my_fmt, pointer, &eaten) > 0)
33697049Speter		    consumed += eaten;
33797049Speter		else
33897049Speter		    break;
33997049Speter		++assigned;
34097049Speter	    }
34197049Speter	    free(my_fmt);
34297049Speter	}
34397049Speter    }
34497049Speter    returnCode(assigned);
34597049Speter#endif
34650276Speter}
34750276Speter#else
34876726Speterextern
34976726SpeterNCURSES_EXPORT(void)
35076726Speter_nc_vsscanf(void);		/* quiet's gcc warning */
35176726SpeterNCURSES_EXPORT(void)
35276726Speter_nc_vsscanf(void)
35376726Speter{
35476726Speter}				/* nonempty for strict ANSI compilers */
35550276Speter#endif /* !HAVE_VSSCANF */
356