1/* $OpenBSD: vsscanf.c,v 1.3 2023/10/17 09:52:09 nicm Exp $ */ 2 3/**************************************************************************** 4 * Copyright 2020 Thomas E. Dickey * 5 * Copyright 1998-2004,2012 Free Software Foundation, Inc. * 6 * * 7 * Permission is hereby granted, free of charge, to any person obtaining a * 8 * copy of this software and associated documentation files (the * 9 * "Software"), to deal in the Software without restriction, including * 10 * without limitation the rights to use, copy, modify, merge, publish, * 11 * distribute, distribute with modifications, sublicense, and/or sell * 12 * copies of the Software, and to permit persons to whom the Software is * 13 * furnished to do so, subject to the following conditions: * 14 * * 15 * The above copyright notice and this permission notice shall be included * 16 * in all copies or substantial portions of the Software. * 17 * * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * 19 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * 20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * 21 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * 22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * 23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * 24 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. * 25 * * 26 * Except as contained in this notice, the name(s) of the above copyright * 27 * holders shall not be used in advertising or otherwise to promote the * 28 * sale, use or other dealings in this Software without prior written * 29 * authorization. * 30 ****************************************************************************/ 31 32/**************************************************************************** 33 * State-machine fallback written by Thomas E. Dickey 2002 * 34 ****************************************************************************/ 35 36/* 37 * This function is needed to support vwscanw 38 */ 39 40#include <curses.priv.h> 41 42#if !HAVE_VSSCANF 43 44MODULE_ID("$Id: vsscanf.c,v 1.3 2023/10/17 09:52:09 nicm Exp $") 45 46#if !(HAVE_VFSCANF || HAVE__DOSCAN) 47 48#include <ctype.h> 49 50#define L_SQUARE '[' 51#define R_SQUARE ']' 52 53typedef enum { 54 cUnknown 55 ,cError /* anything that isn't ANSI */ 56 ,cAssigned 57 ,cChar 58 ,cInt 59 ,cFloat 60 ,cDouble 61 ,cPointer 62 ,cLong 63 ,cShort 64 ,cRange 65 ,cString 66} ChunkType; 67 68typedef enum { 69 oUnknown 70 ,oShort 71 ,oLong 72} OtherType; 73 74typedef enum { 75 sUnknown 76 ,sPercent /* last was '%' beginning a format */ 77 ,sNormal /* ...somewhere in the middle */ 78 ,sLeft /* last was left square bracket beginning a range */ 79 ,sRange /* ...somewhere in the middle */ 80 ,sFinal /* last finished a format */ 81} ScanState; 82 83static ChunkType 84final_ch(int ch, OtherType other) 85{ 86 ChunkType result = cUnknown; 87 88 switch (ch) { 89 case 'c': 90 if (other == oUnknown) 91 result = cChar; 92 else 93 result = cError; 94 break; 95 case 'd': 96 case 'i': 97 case 'X': 98 case 'x': 99 switch (other) { 100 case oUnknown: 101 result = cInt; 102 break; 103 case oShort: 104 result = cShort; 105 break; 106 case oLong: 107 result = cLong; 108 break; 109 } 110 break; 111 case 'E': 112 case 'e': 113 case 'f': 114 case 'g': 115 switch (other) { 116 case oUnknown: 117 result = cFloat; 118 break; 119 case oShort: 120 result = cError; 121 break; 122 case oLong: 123 result = cDouble; 124 break; 125 } 126 break; 127 case 'n': 128 if (other == oUnknown) 129 result = cAssigned; 130 else 131 result = cError; 132 break; 133 case 'p': 134 if (other == oUnknown) 135 result = cPointer; 136 else 137 result = cError; 138 break; 139 case 's': 140 if (other == oUnknown) 141 result = cString; 142 else 143 result = cError; 144 break; 145 } 146 return result; 147} 148 149static OtherType 150other_ch(int ch) 151{ 152 OtherType result = oUnknown; 153 switch (ch) { 154 case 'h': 155 result = oShort; 156 break; 157 case 'l': 158 result = oLong; 159 break; 160 } 161 return result; 162} 163#endif 164 165/*VARARGS2*/ 166NCURSES_EXPORT(int) 167vsscanf(const char *str, const char *format, va_list ap) 168{ 169#if HAVE_VFSCANF || HAVE__DOSCAN 170 /* 171 * This code should work on anything descended from AT&T SVr1. 172 */ 173 FILE strbuf; 174 175 strbuf._flag = _IOREAD; 176 strbuf._ptr = strbuf._base = (unsigned char *) str; 177 strbuf._cnt = strlen(str); 178 strbuf._file = _NFILE; 179 180#if HAVE_VFSCANF 181 return (vfscanf(&strbuf, format, ap)); 182#else 183 return (_doscan(&strbuf, format, ap)); 184#endif 185#else 186 static int can_convert = -1; 187 188 int assigned = 0; 189 int consumed = 0; 190 191 T((T_CALLED("vsscanf(%s,%s,...)"), 192 _nc_visbuf2(1, str), 193 _nc_visbuf2(2, format))); 194 195 /* 196 * This relies on having a working "%n" format conversion. Check if it 197 * works. Only very old C libraries do not support it. 198 * 199 * FIXME: move this check into the configure script. 200 */ 201 if (can_convert < 0) { 202 int check1; 203 int check2; 204 if (sscanf("123", "%d%n", &check1, &check2) > 0 205 && check1 == 123 206 && check2 == 3) { 207 can_convert = 1; 208 } else { 209 can_convert = 0; 210 } 211 } 212 213 if (can_convert) { 214 size_t len_fmt = strlen(format) + 32; 215 char *my_fmt = malloc(len_fmt); 216 ChunkType chunk, ctest; 217 OtherType other, otest; 218 ScanState state; 219 unsigned n; 220 int eaten; 221 void *pointer; 222 223 if (my_fmt != 0) { 224 /* 225 * Split the original format into chunks, adding a "%n" to the end 226 * of each (except of course if it used %n), and use that 227 * information to decide where to start scanning the next chunk. 228 * 229 * FIXME: does %n count bytes or characters? If the latter, this 230 * will require further work for multibyte strings. 231 */ 232 while (*format != '\0') { 233 /* find a chunk */ 234 state = sUnknown; 235 chunk = cUnknown; 236 other = oUnknown; 237 pointer = 0; 238 for (n = 0; format[n] != 0 && state != sFinal; ++n) { 239 my_fmt[n] = format[n]; 240 switch (state) { 241 case sUnknown: 242 if (format[n] == '%') 243 state = sPercent; 244 break; 245 case sPercent: 246 if (format[n] == '%') { 247 state = sUnknown; 248 } else if (format[n] == L_SQUARE) { 249 state = sLeft; 250 } else { 251 state = sNormal; 252 --n; 253 } 254 break; 255 case sLeft: 256 state = sRange; 257 if (format[n] == '^') { 258 ++n; 259 my_fmt[n] = format[n]; 260 } 261 break; 262 case sRange: 263 if (format[n] == R_SQUARE) { 264 state = sFinal; 265 chunk = cRange; 266 } 267 break; 268 case sNormal: 269 if (format[n] == '*') { 270 state = sUnknown; 271 } else { 272 if ((ctest = final_ch(format[n], other)) != cUnknown) { 273 state = sFinal; 274 chunk = ctest; 275 } else if ((otest = other_ch(format[n])) != oUnknown) { 276 other = otest; 277 } else if (isalpha(UChar(format[n]))) { 278 state = sFinal; 279 chunk = cError; 280 } 281 } 282 break; 283 case sFinal: 284 break; 285 } 286 } 287 my_fmt[n] = '\0'; 288 format += n; 289 290 if (chunk == cUnknown 291 || chunk == cError) { 292 if (assigned == 0) 293 assigned = EOF; 294 break; 295 } 296 297 /* add %n, if the format was not that */ 298 if (chunk != cAssigned) { 299 _nc_STRCAT(my_fmt, "%n", len_fmt); 300 } 301 302 switch (chunk) { 303 case cAssigned: 304 _nc_STRCAT(my_fmt, "%n", len_fmt); 305 pointer = &eaten; 306 break; 307 case cInt: 308 pointer = va_arg(ap, int *); 309 break; 310 case cShort: 311 pointer = va_arg(ap, short *); 312 break; 313 case cFloat: 314 pointer = va_arg(ap, float *); 315 break; 316 case cDouble: 317 pointer = va_arg(ap, double *); 318 break; 319 case cLong: 320 pointer = va_arg(ap, long *); 321 break; 322 case cPointer: 323 pointer = va_arg(ap, void *); 324 break; 325 case cChar: 326 case cRange: 327 case cString: 328 pointer = va_arg(ap, char *); 329 break; 330 case cError: 331 case cUnknown: 332 break; 333 } 334 /* do the conversion */ 335 T(("...converting chunk #%d type %d(%s,%s)", 336 assigned + 1, chunk, 337 _nc_visbuf2(1, str + consumed), 338 _nc_visbuf2(2, my_fmt))); 339 if (sscanf(str + consumed, my_fmt, pointer, &eaten) > 0) 340 consumed += eaten; 341 else 342 break; 343 ++assigned; 344 } 345 free(my_fmt); 346 } 347 } 348 returnCode(assigned); 349#endif 350} 351#else 352extern 353NCURSES_EXPORT(void) 354_nc_vsscanf(void); /* quiet's gcc warning */ 355NCURSES_EXPORT(void) 356_nc_vsscanf(void) 357{ 358} /* nonempty for strict ANSI compilers */ 359#endif /* !HAVE_VSSCANF */ 360