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