1118537f2SMike Smith /*- 2118537f2SMike Smith * Copyright (c) 1990, 1993 3118537f2SMike Smith * The Regents of the University of California. All rights reserved. 4118537f2SMike Smith * 5118537f2SMike Smith * This code is derived from software contributed to Berkeley by 6118537f2SMike Smith * Chris Torek. 7118537f2SMike Smith * 8118537f2SMike Smith * Redistribution and use in source and binary forms, with or without 9118537f2SMike Smith * modification, are permitted provided that the following conditions 10118537f2SMike Smith * are met: 11118537f2SMike Smith * 1. Redistributions of source code must retain the above copyright 12118537f2SMike Smith * notice, this list of conditions and the following disclaimer. 13118537f2SMike Smith * 2. Redistributions in binary form must reproduce the above copyright 14118537f2SMike Smith * notice, this list of conditions and the following disclaimer in the 15118537f2SMike Smith * documentation and/or other materials provided with the distribution. 16118537f2SMike Smith * 3. All advertising materials mentioning features or use of this software 17118537f2SMike Smith * must display the following acknowledgement: 18118537f2SMike Smith * This product includes software developed by the University of 19118537f2SMike Smith * California, Berkeley and its contributors. 20118537f2SMike Smith * 4. Neither the name of the University nor the names of its contributors 21118537f2SMike Smith * may be used to endorse or promote products derived from this software 22118537f2SMike Smith * without specific prior written permission. 23118537f2SMike Smith * 24118537f2SMike Smith * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25118537f2SMike Smith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26118537f2SMike Smith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27118537f2SMike Smith * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28118537f2SMike Smith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29118537f2SMike Smith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30118537f2SMike Smith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31118537f2SMike Smith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32118537f2SMike Smith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33118537f2SMike Smith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34118537f2SMike Smith * SUCH DAMAGE. 35118537f2SMike Smith * 36118537f2SMike Smith * $Id$ 37118537f2SMike Smith * From: Id: vfscanf.c,v 1.13 1998/09/25 12:20:27 obrien Exp 38118537f2SMike Smith */ 39118537f2SMike Smith 40118537f2SMike Smith #include <sys/param.h> 41118537f2SMike Smith #include <sys/systm.h> 42118537f2SMike Smith #include <sys/kernel.h> 43118537f2SMike Smith #include <machine/limits.h> 44118537f2SMike Smith 45118537f2SMike Smith /* 46118537f2SMike Smith * Note that stdarg.h and the ANSI style va_start macro is used for both 47118537f2SMike Smith * ANSI and traditional C compilers. 48118537f2SMike Smith */ 49118537f2SMike Smith #include <machine/stdarg.h> 50118537f2SMike Smith 51118537f2SMike Smith #define BUF 32 /* Maximum length of numeric string. */ 52118537f2SMike Smith 53118537f2SMike Smith /* 54118537f2SMike Smith * Flags used during conversion. 55118537f2SMike Smith */ 56118537f2SMike Smith #define LONG 0x01 /* l: long or double */ 57118537f2SMike Smith #define SHORT 0x04 /* h: short */ 58118537f2SMike Smith #define SUPPRESS 0x08 /* suppress assignment */ 59118537f2SMike Smith #define POINTER 0x10 /* weird %p pointer (`fake hex') */ 60118537f2SMike Smith #define NOSKIP 0x20 /* do not skip blanks */ 61118537f2SMike Smith #define QUAD 0x400 62118537f2SMike Smith 63118537f2SMike Smith /* 64118537f2SMike Smith * The following are used in numeric conversions only: 65118537f2SMike Smith * SIGNOK, NDIGITS, DPTOK, and EXPOK are for floating point; 66118537f2SMike Smith * SIGNOK, NDIGITS, PFXOK, and NZDIGITS are for integral. 67118537f2SMike Smith */ 68118537f2SMike Smith #define SIGNOK 0x40 /* +/- is (still) legal */ 69118537f2SMike Smith #define NDIGITS 0x80 /* no digits detected */ 70118537f2SMike Smith 71118537f2SMike Smith #define DPTOK 0x100 /* (float) decimal point is still legal */ 72118537f2SMike Smith #define EXPOK 0x200 /* (float) exponent (e+3, etc) still legal */ 73118537f2SMike Smith 74118537f2SMike Smith #define PFXOK 0x100 /* 0x prefix is (still) legal */ 75118537f2SMike Smith #define NZDIGITS 0x200 /* no zero digits detected */ 76118537f2SMike Smith 77118537f2SMike Smith /* 78118537f2SMike Smith * Conversion types. 79118537f2SMike Smith */ 80118537f2SMike Smith #define CT_CHAR 0 /* %c conversion */ 81118537f2SMike Smith #define CT_CCL 1 /* %[...] conversion */ 82118537f2SMike Smith #define CT_STRING 2 /* %s conversion */ 83118537f2SMike Smith #define CT_INT 3 /* integer, i.e., strtoq or strtouq */ 84118537f2SMike Smith typedef u_quad_t (*ccfntype)(const char *, char **, int); 85118537f2SMike Smith 86118537f2SMike Smith #define isspace(c) ((c) == ' ' || (c) == '\t' || \ 87118537f2SMike Smith (c) == '\r' || (c) == '\n') 88118537f2SMike Smith #define isascii(c) (((c) & ~0x7f) == 0) 89118537f2SMike Smith #define isupper(c) ((c) >= 'A' && (c) <= 'Z') 90118537f2SMike Smith #define islower(c) ((c) >= 'a' && (c) <= 'z') 91118537f2SMike Smith #define isalpha(c) (isupper(c) || (islower(c))) 92118537f2SMike Smith #define isdigit(c) ((c) >= '0' && (c) <= '9') 93118537f2SMike Smith 94118537f2SMike Smith static u_char *__sccl(char *, u_char *); 95118537f2SMike Smith 96118537f2SMike Smith int 97118537f2SMike Smith sscanf(const char *ibuf, const char *fmt, ...) 98118537f2SMike Smith { 99118537f2SMike Smith va_list ap; 100118537f2SMike Smith int ret; 101118537f2SMike Smith 102118537f2SMike Smith va_start(ap, fmt); 103118537f2SMike Smith ret = vsscanf(ibuf, fmt, ap); 104118537f2SMike Smith va_end(ap); 105118537f2SMike Smith return(ret); 106118537f2SMike Smith } 107118537f2SMike Smith 108118537f2SMike Smith int 109118537f2SMike Smith vsscanf(const char *inp, char const *fmt0, va_list ap) 110118537f2SMike Smith { 111118537f2SMike Smith int inr; 112118537f2SMike Smith u_char *fmt = (u_char *)fmt0; 113118537f2SMike Smith int c; /* character from format, or conversion */ 114118537f2SMike Smith size_t width; /* field width, or 0 */ 115118537f2SMike Smith char *p; /* points into all kinds of strings */ 116118537f2SMike Smith int n; /* handy integer */ 117118537f2SMike Smith int flags; /* flags as defined above */ 118118537f2SMike Smith char *p0; /* saves original value of p when necessary */ 119118537f2SMike Smith int nassigned; /* number of fields assigned */ 120118537f2SMike Smith int nconversions; /* number of conversions */ 121118537f2SMike Smith int nread; /* number of characters consumed from fp */ 122118537f2SMike Smith int base; /* base argument to strtoq/strtouq */ 123118537f2SMike Smith ccfntype ccfn; /* conversion function (strtoq/strtouq) */ 124118537f2SMike Smith char ccltab[256]; /* character class table for %[...] */ 125118537f2SMike Smith char buf[BUF]; /* buffer for numeric conversions */ 126118537f2SMike Smith 127118537f2SMike Smith /* `basefix' is used to avoid `if' tests in the integer scanner */ 128118537f2SMike Smith static short basefix[17] = 129118537f2SMike Smith { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; 130118537f2SMike Smith 131118537f2SMike Smith inr = strlen(inp); 132118537f2SMike Smith 133118537f2SMike Smith nassigned = 0; 134118537f2SMike Smith nconversions = 0; 135118537f2SMike Smith nread = 0; 136118537f2SMike Smith base = 0; /* XXX just to keep gcc happy */ 137118537f2SMike Smith ccfn = NULL; /* XXX just to keep gcc happy */ 138118537f2SMike Smith for (;;) { 139118537f2SMike Smith c = *fmt++; 140118537f2SMike Smith if (c == 0) 141118537f2SMike Smith return (nassigned); 142118537f2SMike Smith if (isspace(c)) { 143118537f2SMike Smith while (inr > 0 && isspace(*inp)) 144118537f2SMike Smith nread++, inr--, inp++; 145118537f2SMike Smith continue; 146118537f2SMike Smith } 147118537f2SMike Smith if (c != '%') 148118537f2SMike Smith goto literal; 149118537f2SMike Smith width = 0; 150118537f2SMike Smith flags = 0; 151118537f2SMike Smith /* 152118537f2SMike Smith * switch on the format. continue if done; 153118537f2SMike Smith * break once format type is derived. 154118537f2SMike Smith */ 155118537f2SMike Smith again: c = *fmt++; 156118537f2SMike Smith switch (c) { 157118537f2SMike Smith case '%': 158118537f2SMike Smith literal: 159118537f2SMike Smith if (inr <= 0) 160118537f2SMike Smith goto input_failure; 161118537f2SMike Smith if (*inp != c) 162118537f2SMike Smith goto match_failure; 163118537f2SMike Smith inr--, inp++; 164118537f2SMike Smith nread++; 165118537f2SMike Smith continue; 166118537f2SMike Smith 167118537f2SMike Smith case '*': 168118537f2SMike Smith flags |= SUPPRESS; 169118537f2SMike Smith goto again; 170118537f2SMike Smith case 'l': 171118537f2SMike Smith flags |= LONG; 172118537f2SMike Smith goto again; 173118537f2SMike Smith case 'q': 174118537f2SMike Smith flags |= QUAD; 175118537f2SMike Smith goto again; 176118537f2SMike Smith case 'h': 177118537f2SMike Smith flags |= SHORT; 178118537f2SMike Smith goto again; 179118537f2SMike Smith 180118537f2SMike Smith case '0': case '1': case '2': case '3': case '4': 181118537f2SMike Smith case '5': case '6': case '7': case '8': case '9': 182118537f2SMike Smith width = width * 10 + c - '0'; 183118537f2SMike Smith goto again; 184118537f2SMike Smith 185118537f2SMike Smith /* 186118537f2SMike Smith * Conversions. 187118537f2SMike Smith * 188118537f2SMike Smith */ 189118537f2SMike Smith case 'd': 190118537f2SMike Smith c = CT_INT; 191118537f2SMike Smith ccfn = (ccfntype)strtoq; 192118537f2SMike Smith base = 10; 193118537f2SMike Smith break; 194118537f2SMike Smith 195118537f2SMike Smith case 'i': 196118537f2SMike Smith c = CT_INT; 197118537f2SMike Smith ccfn = (ccfntype)strtoq; 198118537f2SMike Smith base = 0; 199118537f2SMike Smith break; 200118537f2SMike Smith 201118537f2SMike Smith case 'o': 202118537f2SMike Smith c = CT_INT; 203118537f2SMike Smith ccfn = strtouq; 204118537f2SMike Smith base = 8; 205118537f2SMike Smith break; 206118537f2SMike Smith 207118537f2SMike Smith case 'u': 208118537f2SMike Smith c = CT_INT; 209118537f2SMike Smith ccfn = strtouq; 210118537f2SMike Smith base = 10; 211118537f2SMike Smith break; 212118537f2SMike Smith 213118537f2SMike Smith case 'x': 214118537f2SMike Smith flags |= PFXOK; /* enable 0x prefixing */ 215118537f2SMike Smith c = CT_INT; 216118537f2SMike Smith ccfn = strtouq; 217118537f2SMike Smith base = 16; 218118537f2SMike Smith break; 219118537f2SMike Smith 220118537f2SMike Smith case 's': 221118537f2SMike Smith c = CT_STRING; 222118537f2SMike Smith break; 223118537f2SMike Smith 224118537f2SMike Smith case '[': 225118537f2SMike Smith fmt = __sccl(ccltab, fmt); 226118537f2SMike Smith flags |= NOSKIP; 227118537f2SMike Smith c = CT_CCL; 228118537f2SMike Smith break; 229118537f2SMike Smith 230118537f2SMike Smith case 'c': 231118537f2SMike Smith flags |= NOSKIP; 232118537f2SMike Smith c = CT_CHAR; 233118537f2SMike Smith break; 234118537f2SMike Smith 235118537f2SMike Smith case 'p': /* pointer format is like hex */ 236118537f2SMike Smith flags |= POINTER | PFXOK; 237118537f2SMike Smith c = CT_INT; 238118537f2SMike Smith ccfn = strtouq; 239118537f2SMike Smith base = 16; 240118537f2SMike Smith break; 241118537f2SMike Smith 242118537f2SMike Smith case 'n': 243118537f2SMike Smith nconversions++; 244118537f2SMike Smith if (flags & SUPPRESS) /* ??? */ 245118537f2SMike Smith continue; 246118537f2SMike Smith if (flags & SHORT) 247118537f2SMike Smith *va_arg(ap, short *) = nread; 248118537f2SMike Smith else if (flags & LONG) 249118537f2SMike Smith *va_arg(ap, long *) = nread; 250118537f2SMike Smith else if (flags & QUAD) 251118537f2SMike Smith *va_arg(ap, quad_t *) = nread; 252118537f2SMike Smith else 253118537f2SMike Smith *va_arg(ap, int *) = nread; 254118537f2SMike Smith continue; 255118537f2SMike Smith } 256118537f2SMike Smith 257118537f2SMike Smith /* 258118537f2SMike Smith * We have a conversion that requires input. 259118537f2SMike Smith */ 260118537f2SMike Smith if (inr <= 0) 261118537f2SMike Smith goto input_failure; 262118537f2SMike Smith 263118537f2SMike Smith /* 264118537f2SMike Smith * Consume leading white space, except for formats 265118537f2SMike Smith * that suppress this. 266118537f2SMike Smith */ 267118537f2SMike Smith if ((flags & NOSKIP) == 0) { 268118537f2SMike Smith while (isspace(*inp)) { 269118537f2SMike Smith nread++; 270118537f2SMike Smith if (--inr > 0) 271118537f2SMike Smith inp++; 272118537f2SMike Smith else 273118537f2SMike Smith goto input_failure; 274118537f2SMike Smith } 275118537f2SMike Smith /* 276118537f2SMike Smith * Note that there is at least one character in 277118537f2SMike Smith * the buffer, so conversions that do not set NOSKIP 278118537f2SMike Smith * can no longer result in an input failure. 279118537f2SMike Smith */ 280118537f2SMike Smith } 281118537f2SMike Smith 282118537f2SMike Smith /* 283118537f2SMike Smith * Do the conversion. 284118537f2SMike Smith */ 285118537f2SMike Smith switch (c) { 286118537f2SMike Smith 287118537f2SMike Smith case CT_CHAR: 288118537f2SMike Smith /* scan arbitrary characters (sets NOSKIP) */ 289118537f2SMike Smith if (width == 0) 290118537f2SMike Smith width = 1; 291118537f2SMike Smith if (flags & SUPPRESS) { 292118537f2SMike Smith size_t sum = 0; 293118537f2SMike Smith for (;;) { 294118537f2SMike Smith if ((n = inr) < width) { 295118537f2SMike Smith sum += n; 296118537f2SMike Smith width -= n; 297118537f2SMike Smith inp += n; 298118537f2SMike Smith if (sum == 0) 299118537f2SMike Smith goto input_failure; 300118537f2SMike Smith break; 301118537f2SMike Smith } else { 302118537f2SMike Smith sum += width; 303118537f2SMike Smith inr -= width; 304118537f2SMike Smith inp += width; 305118537f2SMike Smith break; 306118537f2SMike Smith } 307118537f2SMike Smith } 308118537f2SMike Smith nread += sum; 309118537f2SMike Smith } else { 310118537f2SMike Smith bcopy(inp, va_arg(ap, char *), width); 311118537f2SMike Smith inr -= width; 312118537f2SMike Smith inp += width; 313118537f2SMike Smith nread += width; 314118537f2SMike Smith nassigned++; 315118537f2SMike Smith } 316118537f2SMike Smith nconversions++; 317118537f2SMike Smith break; 318118537f2SMike Smith 319118537f2SMike Smith case CT_CCL: 320118537f2SMike Smith /* scan a (nonempty) character class (sets NOSKIP) */ 321118537f2SMike Smith if (width == 0) 322118537f2SMike Smith width = (size_t)~0; /* `infinity' */ 323118537f2SMike Smith /* take only those things in the class */ 324118537f2SMike Smith if (flags & SUPPRESS) { 325118537f2SMike Smith n = 0; 326118537f2SMike Smith while (ccltab[*inp]) { 327118537f2SMike Smith n++, inr--, inp++; 328118537f2SMike Smith if (--width == 0) 329118537f2SMike Smith break; 330118537f2SMike Smith if (inr <= 0) { 331118537f2SMike Smith if (n == 0) 332118537f2SMike Smith goto input_failure; 333118537f2SMike Smith break; 334118537f2SMike Smith } 335118537f2SMike Smith } 336118537f2SMike Smith if (n == 0) 337118537f2SMike Smith goto match_failure; 338118537f2SMike Smith } else { 339118537f2SMike Smith p0 = p = va_arg(ap, char *); 340118537f2SMike Smith while (ccltab[*inp]) { 341118537f2SMike Smith inr--; 342118537f2SMike Smith *p++ = *inp++; 343118537f2SMike Smith if (--width == 0) 344118537f2SMike Smith break; 345118537f2SMike Smith if (inr <= 0) { 346118537f2SMike Smith if (p == p0) 347118537f2SMike Smith goto input_failure; 348118537f2SMike Smith break; 349118537f2SMike Smith } 350118537f2SMike Smith } 351118537f2SMike Smith n = p - p0; 352118537f2SMike Smith if (n == 0) 353118537f2SMike Smith goto match_failure; 354118537f2SMike Smith *p = 0; 355118537f2SMike Smith nassigned++; 356118537f2SMike Smith } 357118537f2SMike Smith nread += n; 358118537f2SMike Smith nconversions++; 359118537f2SMike Smith break; 360118537f2SMike Smith 361118537f2SMike Smith case CT_STRING: 362118537f2SMike Smith /* like CCL, but zero-length string OK, & no NOSKIP */ 363118537f2SMike Smith if (width == 0) 364118537f2SMike Smith width = (size_t)~0; 365118537f2SMike Smith if (flags & SUPPRESS) { 366118537f2SMike Smith n = 0; 367118537f2SMike Smith while (!isspace(*inp)) { 368118537f2SMike Smith n++, inr--, inp++; 369118537f2SMike Smith if (--width == 0) 370118537f2SMike Smith break; 371118537f2SMike Smith if (inr <= 0) 372118537f2SMike Smith break; 373118537f2SMike Smith } 374118537f2SMike Smith nread += n; 375118537f2SMike Smith } else { 376118537f2SMike Smith p0 = p = va_arg(ap, char *); 377118537f2SMike Smith while (!isspace(*inp)) { 378118537f2SMike Smith inr--; 379118537f2SMike Smith *p++ = *inp++; 380118537f2SMike Smith if (--width == 0) 381118537f2SMike Smith break; 382118537f2SMike Smith if (inr <= 0) 383118537f2SMike Smith break; 384118537f2SMike Smith } 385118537f2SMike Smith *p = 0; 386118537f2SMike Smith nread += p - p0; 387118537f2SMike Smith nassigned++; 388118537f2SMike Smith } 389118537f2SMike Smith nconversions++; 390118537f2SMike Smith continue; 391118537f2SMike Smith 392118537f2SMike Smith case CT_INT: 393118537f2SMike Smith /* scan an integer as if by strtoq/strtouq */ 394118537f2SMike Smith #ifdef hardway 395118537f2SMike Smith if (width == 0 || width > sizeof(buf) - 1) 396118537f2SMike Smith width = sizeof(buf) - 1; 397118537f2SMike Smith #else 398118537f2SMike Smith /* size_t is unsigned, hence this optimisation */ 399118537f2SMike Smith if (--width > sizeof(buf) - 2) 400118537f2SMike Smith width = sizeof(buf) - 2; 401118537f2SMike Smith width++; 402118537f2SMike Smith #endif 403118537f2SMike Smith flags |= SIGNOK | NDIGITS | NZDIGITS; 404118537f2SMike Smith for (p = buf; width; width--) { 405118537f2SMike Smith c = *inp; 406118537f2SMike Smith /* 407118537f2SMike Smith * Switch on the character; `goto ok' 408118537f2SMike Smith * if we accept it as a part of number. 409118537f2SMike Smith */ 410118537f2SMike Smith switch (c) { 411118537f2SMike Smith 412118537f2SMike Smith /* 413118537f2SMike Smith * The digit 0 is always legal, but is 414118537f2SMike Smith * special. For %i conversions, if no 415118537f2SMike Smith * digits (zero or nonzero) have been 416118537f2SMike Smith * scanned (only signs), we will have 417118537f2SMike Smith * base==0. In that case, we should set 418118537f2SMike Smith * it to 8 and enable 0x prefixing. 419118537f2SMike Smith * Also, if we have not scanned zero digits 420118537f2SMike Smith * before this, do not turn off prefixing 421118537f2SMike Smith * (someone else will turn it off if we 422118537f2SMike Smith * have scanned any nonzero digits). 423118537f2SMike Smith */ 424118537f2SMike Smith case '0': 425118537f2SMike Smith if (base == 0) { 426118537f2SMike Smith base = 8; 427118537f2SMike Smith flags |= PFXOK; 428118537f2SMike Smith } 429118537f2SMike Smith if (flags & NZDIGITS) 430118537f2SMike Smith flags &= ~(SIGNOK|NZDIGITS|NDIGITS); 431118537f2SMike Smith else 432118537f2SMike Smith flags &= ~(SIGNOK|PFXOK|NDIGITS); 433118537f2SMike Smith goto ok; 434118537f2SMike Smith 435118537f2SMike Smith /* 1 through 7 always legal */ 436118537f2SMike Smith case '1': case '2': case '3': 437118537f2SMike Smith case '4': case '5': case '6': case '7': 438118537f2SMike Smith base = basefix[base]; 439118537f2SMike Smith flags &= ~(SIGNOK | PFXOK | NDIGITS); 440118537f2SMike Smith goto ok; 441118537f2SMike Smith 442118537f2SMike Smith /* digits 8 and 9 ok iff decimal or hex */ 443118537f2SMike Smith case '8': case '9': 444118537f2SMike Smith base = basefix[base]; 445118537f2SMike Smith if (base <= 8) 446118537f2SMike Smith break; /* not legal here */ 447118537f2SMike Smith flags &= ~(SIGNOK | PFXOK | NDIGITS); 448118537f2SMike Smith goto ok; 449118537f2SMike Smith 450118537f2SMike Smith /* letters ok iff hex */ 451118537f2SMike Smith case 'A': case 'B': case 'C': 452118537f2SMike Smith case 'D': case 'E': case 'F': 453118537f2SMike Smith case 'a': case 'b': case 'c': 454118537f2SMike Smith case 'd': case 'e': case 'f': 455118537f2SMike Smith /* no need to fix base here */ 456118537f2SMike Smith if (base <= 10) 457118537f2SMike Smith break; /* not legal here */ 458118537f2SMike Smith flags &= ~(SIGNOK | PFXOK | NDIGITS); 459118537f2SMike Smith goto ok; 460118537f2SMike Smith 461118537f2SMike Smith /* sign ok only as first character */ 462118537f2SMike Smith case '+': case '-': 463118537f2SMike Smith if (flags & SIGNOK) { 464118537f2SMike Smith flags &= ~SIGNOK; 465118537f2SMike Smith goto ok; 466118537f2SMike Smith } 467118537f2SMike Smith break; 468118537f2SMike Smith 469118537f2SMike Smith /* x ok iff flag still set & 2nd char */ 470118537f2SMike Smith case 'x': case 'X': 471118537f2SMike Smith if (flags & PFXOK && p == buf + 1) { 472118537f2SMike Smith base = 16; /* if %i */ 473118537f2SMike Smith flags &= ~PFXOK; 474118537f2SMike Smith goto ok; 475118537f2SMike Smith } 476118537f2SMike Smith break; 477118537f2SMike Smith } 478118537f2SMike Smith 479118537f2SMike Smith /* 480118537f2SMike Smith * If we got here, c is not a legal character 481118537f2SMike Smith * for a number. Stop accumulating digits. 482118537f2SMike Smith */ 483118537f2SMike Smith break; 484118537f2SMike Smith ok: 485118537f2SMike Smith /* 486118537f2SMike Smith * c is legal: store it and look at the next. 487118537f2SMike Smith */ 488118537f2SMike Smith *p++ = c; 489118537f2SMike Smith if (--inr > 0) 490118537f2SMike Smith inp++; 491118537f2SMike Smith else 492118537f2SMike Smith break; /* end of input */ 493118537f2SMike Smith } 494118537f2SMike Smith /* 495118537f2SMike Smith * If we had only a sign, it is no good; push 496118537f2SMike Smith * back the sign. If the number ends in `x', 497118537f2SMike Smith * it was [sign] '0' 'x', so push back the x 498118537f2SMike Smith * and treat it as [sign] '0'. 499118537f2SMike Smith */ 500118537f2SMike Smith if (flags & NDIGITS) { 501118537f2SMike Smith if (p > buf) { 502118537f2SMike Smith inp--; 503118537f2SMike Smith inr++; 504118537f2SMike Smith } 505118537f2SMike Smith goto match_failure; 506118537f2SMike Smith } 507118537f2SMike Smith c = ((u_char *)p)[-1]; 508118537f2SMike Smith if (c == 'x' || c == 'X') { 509118537f2SMike Smith --p; 510118537f2SMike Smith inp--; 511118537f2SMike Smith inr++; 512118537f2SMike Smith } 513118537f2SMike Smith if ((flags & SUPPRESS) == 0) { 514118537f2SMike Smith u_quad_t res; 515118537f2SMike Smith 516118537f2SMike Smith *p = 0; 517118537f2SMike Smith res = (*ccfn)(buf, (char **)NULL, base); 518118537f2SMike Smith if (flags & POINTER) 519118537f2SMike Smith *va_arg(ap, void **) = 520118537f2SMike Smith (void *)(u_long)res; 521118537f2SMike Smith else if (flags & SHORT) 522118537f2SMike Smith *va_arg(ap, short *) = res; 523118537f2SMike Smith else if (flags & LONG) 524118537f2SMike Smith *va_arg(ap, long *) = res; 525118537f2SMike Smith else if (flags & QUAD) 526118537f2SMike Smith *va_arg(ap, quad_t *) = res; 527118537f2SMike Smith else 528118537f2SMike Smith *va_arg(ap, int *) = res; 529118537f2SMike Smith nassigned++; 530118537f2SMike Smith } 531118537f2SMike Smith nread += p - buf; 532118537f2SMike Smith nconversions++; 533118537f2SMike Smith break; 534118537f2SMike Smith 535118537f2SMike Smith } 536118537f2SMike Smith } 537118537f2SMike Smith input_failure: 538118537f2SMike Smith return (nconversions != 0 ? nassigned : -1); 539118537f2SMike Smith match_failure: 540118537f2SMike Smith return (nassigned); 541118537f2SMike Smith } 542118537f2SMike Smith 543118537f2SMike Smith /* 544118537f2SMike Smith * Fill in the given table from the scanset at the given format 545118537f2SMike Smith * (just after `['). Return a pointer to the character past the 546118537f2SMike Smith * closing `]'. The table has a 1 wherever characters should be 547118537f2SMike Smith * considered part of the scanset. 548118537f2SMike Smith */ 549118537f2SMike Smith static u_char * 550118537f2SMike Smith __sccl(char *tab, u_char *fmt) 551118537f2SMike Smith { 552118537f2SMike Smith int c, n, v; 553118537f2SMike Smith 554118537f2SMike Smith /* first `clear' the whole table */ 555118537f2SMike Smith c = *fmt++; /* first char hat => negated scanset */ 556118537f2SMike Smith if (c == '^') { 557118537f2SMike Smith v = 1; /* default => accept */ 558118537f2SMike Smith c = *fmt++; /* get new first char */ 559118537f2SMike Smith } else 560118537f2SMike Smith v = 0; /* default => reject */ 561118537f2SMike Smith 562118537f2SMike Smith /* XXX: Will not work if sizeof(tab*) > sizeof(char) */ 563118537f2SMike Smith for (n = 0; n < 256; n++) 564118537f2SMike Smith tab[n] = v; /* memset(tab, v, 256) */ 565118537f2SMike Smith 566118537f2SMike Smith if (c == 0) 567118537f2SMike Smith return (fmt - 1);/* format ended before closing ] */ 568118537f2SMike Smith 569118537f2SMike Smith /* 570118537f2SMike Smith * Now set the entries corresponding to the actual scanset 571118537f2SMike Smith * to the opposite of the above. 572118537f2SMike Smith * 573118537f2SMike Smith * The first character may be ']' (or '-') without being special; 574118537f2SMike Smith * the last character may be '-'. 575118537f2SMike Smith */ 576118537f2SMike Smith v = 1 - v; 577118537f2SMike Smith for (;;) { 578118537f2SMike Smith tab[c] = v; /* take character c */ 579118537f2SMike Smith doswitch: 580118537f2SMike Smith n = *fmt++; /* and examine the next */ 581118537f2SMike Smith switch (n) { 582118537f2SMike Smith 583118537f2SMike Smith case 0: /* format ended too soon */ 584118537f2SMike Smith return (fmt - 1); 585118537f2SMike Smith 586118537f2SMike Smith case '-': 587118537f2SMike Smith /* 588118537f2SMike Smith * A scanset of the form 589118537f2SMike Smith * [01+-] 590118537f2SMike Smith * is defined as `the digit 0, the digit 1, 591118537f2SMike Smith * the character +, the character -', but 592118537f2SMike Smith * the effect of a scanset such as 593118537f2SMike Smith * [a-zA-Z0-9] 594118537f2SMike Smith * is implementation defined. The V7 Unix 595118537f2SMike Smith * scanf treats `a-z' as `the letters a through 596118537f2SMike Smith * z', but treats `a-a' as `the letter a, the 597118537f2SMike Smith * character -, and the letter a'. 598118537f2SMike Smith * 599118537f2SMike Smith * For compatibility, the `-' is not considerd 600118537f2SMike Smith * to define a range if the character following 601118537f2SMike Smith * it is either a close bracket (required by ANSI) 602118537f2SMike Smith * or is not numerically greater than the character 603118537f2SMike Smith * we just stored in the table (c). 604118537f2SMike Smith */ 605118537f2SMike Smith n = *fmt; 606118537f2SMike Smith if (n == ']' || n < c) { 607118537f2SMike Smith c = '-'; 608118537f2SMike Smith break; /* resume the for(;;) */ 609118537f2SMike Smith } 610118537f2SMike Smith fmt++; 611118537f2SMike Smith /* fill in the range */ 612118537f2SMike Smith do { 613118537f2SMike Smith tab[++c] = v; 614118537f2SMike Smith } while (c < n); 615118537f2SMike Smith c = n; 616118537f2SMike Smith /* 617118537f2SMike Smith * Alas, the V7 Unix scanf also treats formats 618118537f2SMike Smith * such as [a-c-e] as `the letters a through e'. 619118537f2SMike Smith * This too is permitted by the standard.... 620118537f2SMike Smith */ 621118537f2SMike Smith goto doswitch; 622118537f2SMike Smith break; 623118537f2SMike Smith 624118537f2SMike Smith case ']': /* end of scanset */ 625118537f2SMike Smith return (fmt); 626118537f2SMike Smith 627118537f2SMike Smith default: /* just another character */ 628118537f2SMike Smith c = n; 629118537f2SMike Smith break; 630118537f2SMike Smith } 631118537f2SMike Smith } 632118537f2SMike Smith /* NOTREACHED */ 633118537f2SMike Smith } 634118537f2SMike Smith 635118537f2SMike Smith /* 636118537f2SMike Smith * Convert a string to an unsigned quad integer. 637118537f2SMike Smith * 638118537f2SMike Smith * Ignores `locale' stuff. Assumes that the upper and lower case 639118537f2SMike Smith * alphabets and digits are each contiguous. 640118537f2SMike Smith */ 641118537f2SMike Smith u_quad_t 642118537f2SMike Smith strtouq(const char *nptr, char **endptr, int base) 643118537f2SMike Smith { 644118537f2SMike Smith const char *s = nptr; 645118537f2SMike Smith u_quad_t acc; 646118537f2SMike Smith unsigned char c; 647118537f2SMike Smith u_quad_t qbase, cutoff; 648118537f2SMike Smith int neg, any, cutlim; 649118537f2SMike Smith 650118537f2SMike Smith /* 651118537f2SMike Smith * See strtoq for comments as to the logic used. 652118537f2SMike Smith */ 653118537f2SMike Smith s = nptr; 654118537f2SMike Smith do { 655118537f2SMike Smith c = *s++; 656118537f2SMike Smith } while (isspace(c)); 657118537f2SMike Smith if (c == '-') { 658118537f2SMike Smith neg = 1; 659118537f2SMike Smith c = *s++; 660118537f2SMike Smith } else { 661118537f2SMike Smith neg = 0; 662118537f2SMike Smith if (c == '+') 663118537f2SMike Smith c = *s++; 664118537f2SMike Smith } 665118537f2SMike Smith if ((base == 0 || base == 16) && 666118537f2SMike Smith c == '0' && (*s == 'x' || *s == 'X')) { 667118537f2SMike Smith c = s[1]; 668118537f2SMike Smith s += 2; 669118537f2SMike Smith base = 16; 670118537f2SMike Smith } 671118537f2SMike Smith if (base == 0) 672118537f2SMike Smith base = c == '0' ? 8 : 10; 673118537f2SMike Smith qbase = (unsigned)base; 674118537f2SMike Smith cutoff = (u_quad_t)UQUAD_MAX / qbase; 675118537f2SMike Smith cutlim = (u_quad_t)UQUAD_MAX % qbase; 676118537f2SMike Smith for (acc = 0, any = 0;; c = *s++) { 677118537f2SMike Smith if (!isascii(c)) 678118537f2SMike Smith break; 679118537f2SMike Smith if (isdigit(c)) 680118537f2SMike Smith c -= '0'; 681118537f2SMike Smith else if (isalpha(c)) 682118537f2SMike Smith c -= isupper(c) ? 'A' - 10 : 'a' - 10; 683118537f2SMike Smith else 684118537f2SMike Smith break; 685118537f2SMike Smith if (c >= base) 686118537f2SMike Smith break; 687118537f2SMike Smith if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) 688118537f2SMike Smith any = -1; 689118537f2SMike Smith else { 690118537f2SMike Smith any = 1; 691118537f2SMike Smith acc *= qbase; 692118537f2SMike Smith acc += c; 693118537f2SMike Smith } 694118537f2SMike Smith } 695118537f2SMike Smith if (any < 0) { 696118537f2SMike Smith acc = UQUAD_MAX; 697118537f2SMike Smith } else if (neg) 698118537f2SMike Smith acc = -acc; 699118537f2SMike Smith if (endptr != 0) 700118537f2SMike Smith *endptr = (char *)(any ? s - 1 : nptr); 701118537f2SMike Smith return (acc); 702118537f2SMike Smith } 703118537f2SMike Smith 704118537f2SMike Smith /* 705118537f2SMike Smith * Convert a string to a quad integer. 706118537f2SMike Smith * 707118537f2SMike Smith * Ignores `locale' stuff. Assumes that the upper and lower case 708118537f2SMike Smith * alphabets and digits are each contiguous. 709118537f2SMike Smith */ 710118537f2SMike Smith quad_t 711118537f2SMike Smith strtoq(const char *nptr, char **endptr, int base) 712118537f2SMike Smith { 713118537f2SMike Smith const char *s; 714118537f2SMike Smith u_quad_t acc; 715118537f2SMike Smith unsigned char c; 716118537f2SMike Smith u_quad_t qbase, cutoff; 717118537f2SMike Smith int neg, any, cutlim; 718118537f2SMike Smith 719118537f2SMike Smith /* 720118537f2SMike Smith * Skip white space and pick up leading +/- sign if any. 721118537f2SMike Smith * If base is 0, allow 0x for hex and 0 for octal, else 722118537f2SMike Smith * assume decimal; if base is already 16, allow 0x. 723118537f2SMike Smith */ 724118537f2SMike Smith s = nptr; 725118537f2SMike Smith do { 726118537f2SMike Smith c = *s++; 727118537f2SMike Smith } while (isspace(c)); 728118537f2SMike Smith if (c == '-') { 729118537f2SMike Smith neg = 1; 730118537f2SMike Smith c = *s++; 731118537f2SMike Smith } else { 732118537f2SMike Smith neg = 0; 733118537f2SMike Smith if (c == '+') 734118537f2SMike Smith c = *s++; 735118537f2SMike Smith } 736118537f2SMike Smith if ((base == 0 || base == 16) && 737118537f2SMike Smith c == '0' && (*s == 'x' || *s == 'X')) { 738118537f2SMike Smith c = s[1]; 739118537f2SMike Smith s += 2; 740118537f2SMike Smith base = 16; 741118537f2SMike Smith } 742118537f2SMike Smith if (base == 0) 743118537f2SMike Smith base = c == '0' ? 8 : 10; 744118537f2SMike Smith 745118537f2SMike Smith /* 746118537f2SMike Smith * Compute the cutoff value between legal numbers and illegal 747118537f2SMike Smith * numbers. That is the largest legal value, divided by the 748118537f2SMike Smith * base. An input number that is greater than this value, if 749118537f2SMike Smith * followed by a legal input character, is too big. One that 750118537f2SMike Smith * is equal to this value may be valid or not; the limit 751118537f2SMike Smith * between valid and invalid numbers is then based on the last 752118537f2SMike Smith * digit. For instance, if the range for quads is 753118537f2SMike Smith * [-9223372036854775808..9223372036854775807] and the input base 754118537f2SMike Smith * is 10, cutoff will be set to 922337203685477580 and cutlim to 755118537f2SMike Smith * either 7 (neg==0) or 8 (neg==1), meaning that if we have 756118537f2SMike Smith * accumulated a value > 922337203685477580, or equal but the 757118537f2SMike Smith * next digit is > 7 (or 8), the number is too big, and we will 758118537f2SMike Smith * return a range error. 759118537f2SMike Smith * 760118537f2SMike Smith * Set any if any `digits' consumed; make it negative to indicate 761118537f2SMike Smith * overflow. 762118537f2SMike Smith */ 763118537f2SMike Smith qbase = (unsigned)base; 764118537f2SMike Smith cutoff = neg ? (u_quad_t)-(QUAD_MIN + QUAD_MAX) + QUAD_MAX : QUAD_MAX; 765118537f2SMike Smith cutlim = cutoff % qbase; 766118537f2SMike Smith cutoff /= qbase; 767118537f2SMike Smith for (acc = 0, any = 0;; c = *s++) { 768118537f2SMike Smith if (!isascii(c)) 769118537f2SMike Smith break; 770118537f2SMike Smith if (isdigit(c)) 771118537f2SMike Smith c -= '0'; 772118537f2SMike Smith else if (isalpha(c)) 773118537f2SMike Smith c -= isupper(c) ? 'A' - 10 : 'a' - 10; 774118537f2SMike Smith else 775118537f2SMike Smith break; 776118537f2SMike Smith if (c >= base) 777118537f2SMike Smith break; 778118537f2SMike Smith if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) 779118537f2SMike Smith any = -1; 780118537f2SMike Smith else { 781118537f2SMike Smith any = 1; 782118537f2SMike Smith acc *= qbase; 783118537f2SMike Smith acc += c; 784118537f2SMike Smith } 785118537f2SMike Smith } 786118537f2SMike Smith if (any < 0) { 787118537f2SMike Smith acc = neg ? QUAD_MIN : QUAD_MAX; 788118537f2SMike Smith } else if (neg) 789118537f2SMike Smith acc = -acc; 790118537f2SMike Smith if (endptr != 0) 791118537f2SMike Smith *endptr = (char *)(any ? s - 1 : nptr); 792118537f2SMike Smith return (acc); 793118537f2SMike Smith } 794