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