Date: Sat, 14 Dec 2002 13:27:55 +0000 From: "Richard Dawe" Sender: rich AT phekda DOT freeserve DOT co DOT uk To: djgpp-workers AT delorie DOT com X-Mailer: Emacs 21.3.50 (via feedmail 8.3.emacs20_6 I) and Blat ver 1.8.6 Subject: scanf and 'hh' width conversion, revision 2 (C99) [PATCH] Message-Id: Reply-To: djgpp-workers AT delorie DOT com Hello. I was about to commit the patch to fix scanf to support the 'hh' width conversion, when I realised that I had written no documentation for it. Below is a patch that adds documentation for 'hh'. While I was looking at the scanf page, I noticed that some of the type conversions did not list the argument type as signed or unsigned. Since the C99 standard says explicitly that e.g.: %d will be put in a signed variable, I have updated the docs to agree with C99. In some cases the documentation said the opposite of C99 - signed instead of unsigned for %o. I fixed the docs to agree with C99. OK to commit? Thanks, bye, Rich =] Index: src/libc/ansi/stdio/doscan.c =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/ansi/stdio/doscan.c,v retrieving revision 1.11 diff -p -c -3 -r1.11 doscan.c *** src/libc/ansi/stdio/doscan.c 14 Jun 2002 14:19:42 -0000 1.11 --- src/libc/ansi/stdio/doscan.c 14 Dec 2002 13:24:04 -0000 *************** *** 13,22 **** #define SPC 01 #define STP 02 ! #define SHORT 0 ! #define REGULAR 1 ! #define LONG 2 ! #define LONGDOUBLE 4 #define INT 0 #define FLOAT 1 --- 13,24 ---- #define SPC 01 #define STP 02 ! #define CHAR 0 ! #define SHORT 1 ! #define REGULAR 2 ! #define LONG 4 ! #define LONGDOUBLE 8 ! #define INT 0 #define FLOAT 1 *************** _doscan_low(FILE *iop, int (*scan_getc)( *** 91,96 **** --- 93,103 ---- else if (ch=='h') { size = SHORT; ch = *fmt++; + if (ch=='h') + { + size = CHAR; + ch = *fmt++; + } } else if (ch=='L') { size = LONGDOUBLE; ch = *fmt++; *************** _doscan_low(FILE *iop, int (*scan_getc)( *** 107,116 **** This extension is supported by some compilers (e.g. Borland C). Old pre-ANSI compilers, such as Turbo C 2.0, also interpreted %E, %F and %G to store in a double (rather than a float), but ! this contradicts the ANSI Standard, so we don't support it. */ ! if (ch == 'd' || ch == 'i' || ch == 'o' || ch == 'u' || ch == 'x') { ! if (size==LONG && ch != 'x') /* ANSI: %lX is long, not long long */ size = LONGDOUBLE; else if (size != LONGDOUBLE) size = LONG; --- 114,125 ---- This extension is supported by some compilers (e.g. Borland C). Old pre-ANSI compilers, such as Turbo C 2.0, also interpreted %E, %F and %G to store in a double (rather than a float), but ! this contradicts the ANSI Standard, so we don't support it. ! %X should be treated as per the ANSI Standard - no length ! is implied by the upper-case x. */ ! if (ch == 'd' || ch == 'i' || ch == 'o' || ch == 'u') { ! if (size == LONG) size = LONGDOUBLE; else if (size != LONGDOUBLE) size = LONG; *************** _doscan_low(FILE *iop, int (*scan_getc)( *** 125,130 **** --- 134,141 ---- break; if (size==LONG) *(long*)ptr = nchars; + else if (size==CHAR) + *(char*)ptr = nchars; else if (size==SHORT) *(short*)ptr = nchars; else if (size==LONGDOUBLE) *************** _innum(int *ptr, int type, int len, int *** 292,297 **** --- 303,312 ---- case (FLOAT<<4) | LONGDOUBLE: *(long double *)ptr = _atold(numbuf); + break; + + case (INT<<4) | CHAR: + *(char *)ptr = (char)lcval; break; case (INT<<4) | SHORT: Index: src/libc/ansi/stdio/scanf.txh =================================================================== RCS file: /cvs/djgpp/djgpp/src/libc/ansi/stdio/scanf.txh,v retrieving revision 1.7 diff -p -c -3 -r1.7 scanf.txh *** src/libc/ansi/stdio/scanf.txh 30 Jun 2001 14:05:40 -0000 1.7 --- src/libc/ansi/stdio/scanf.txh 14 Dec 2002 13:24:05 -0000 *************** This allows to describe an input field t *** 29,37 **** @item A width specifier, which specifies the maximum number of input characters to use in the conversion. ! @item An optional conversion qualifier, which may be @samp{h} to specify ! @code{short}, @samp{l} to specify doubles or long ints, or @samp{L} or ! @samp{ll} (two lower-case ell letters) to specify long doubles and the long long type. If the @samp{h} qualifier appears before a specifier that implies conversion to a @code{long} or @code{float} or @code{double}, like in @samp{%hD} or @samp{%hf}, it is generally --- 29,38 ---- @item A width specifier, which specifies the maximum number of input characters to use in the conversion. ! @item An optional conversion qualifier, which may be @samp{hh} ! to specify @code{char}, @samp{h} to specify @code{short}, @samp{l} ! to specify doubles or long ints, or @samp{L} or @samp{ll} ! (two lower-case ell letters) to specify long doubles and the long long type. If the @samp{h} qualifier appears before a specifier that implies conversion to a @code{long} or @code{float} or @code{double}, like in @samp{%hD} or @samp{%hf}, it is generally *************** of one is implied. *** 57,62 **** --- 58,67 ---- Convert the input to a signed @code{int} using 10 as the base of the number representation. + @item hhd + + Convert the input to a signed @code{char} using 10 as the base. + @item hd Convert the input to a signed @code{short} using 10 as the base. *************** Convert the input to a @code{long double *** 108,167 **** @item i Convert the input, determining base automatically by the presence of ! @code{0x} or @code{0} prefixes, and store in an @code{int}. @xref{strtol}. @item hi ! Like @samp{i}, but stores the result in a @code{short}. @item li @itemx I ! Like @samp{i}, but stores the result in a @code{long}. @item Li @itemx lli @itemx lI ! Like @samp{i}, but stores the result in a @code{long long}. @item n Store the number of characters scanned so far into the @code{int} pointed to by the argument. @item hn ! Like @samp{n}, but the argument should point to a @code{short}. @item ln ! Like @samp{n}, but the argument should point to a @code{long}. @item Ln @itemx lln ! Like @samp{n}, but the argument should point to a @code{long long}. @item o ! Convert the input to a signed @code{int}, using base 8. @item ho ! Convert the input to a signed @code{short}, using base 8. @item lo @itemx O ! Convert the input to a signed @code{long}, using base 8. @item Lo @itemx llo @itemx lO ! Convert the input to a signed @code{long long}, using base 8. @item p --- 113,184 ---- @item i Convert the input, determining base automatically by the presence of ! @code{0x} or @code{0} prefixes, and store in a signed @code{int}. @xref{strtol}. + @item hhi + + Like @samp{i}, but stores the result in a signed @code{char}. + @item hi ! Like @samp{i}, but stores the result in a signed @code{short}. @item li @itemx I ! Like @samp{i}, but stores the result in a signed @code{long}. @item Li @itemx lli @itemx lI ! Like @samp{i}, but stores the result in a signed @code{long long}. @item n Store the number of characters scanned so far into the @code{int} pointed to by the argument. + @item hhn + + Like @samp{n}, but the argument should point to a signed @code{char}. + @item hn ! Like @samp{n}, but the argument should point to a signed @code{short}. @item ln ! Like @samp{n}, but the argument should point to a signed @code{long}. @item Ln @itemx lln ! Like @samp{n}, but the argument should point to a signed @code{long long}. @item o ! Convert the input to an unsigned @code{int}, using base 8. ! ! @item hho ! ! Convert the input to an unsigned @code{char}, using base 8. @item ho ! Convert the input to an unsigned @code{short}, using base 8. @item lo @itemx O ! Convert the input to an unsigned @code{long}, using base 8. @item Lo @itemx llo @itemx lO ! Convert the input to an unsigned @code{long long}, using base 8. @item p *************** stored is then terminated with a null ch *** 177,182 **** --- 194,203 ---- Convert the input to an unsigned @code{int} using 10 as the base. + @item hhu + + Convert the input to an unsigned @code{char} using 10 as the base. + @item hu Convert the input to an unsigned @code{short} using 10 as the base. *************** Convert the input to an unsigned @code{l *** 196,201 **** --- 217,227 ---- @itemx X Convert the input to an unsigned @code{int}, using base 16. + + @item hhx + @itemx hhX + + Convert the input to an unsigned @code{char}, using base 16. @item hx @itemx hX Index: src/docs/kb/wc204.txi =================================================================== RCS file: /cvs/djgpp/djgpp/src/docs/kb/wc204.txi,v retrieving revision 1.126 diff -p -c -3 -r1.126 wc204.txi *** src/docs/kb/wc204.txi 14 Dec 2002 12:55:43 -0000 1.126 --- src/docs/kb/wc204.txi 14 Dec 2002 13:24:15 -0000 *************** The function @code{atoll} was added. *** 792,801 **** @findex strtold The function @code{strtold} was added. @findex _doprnt AT r{, and }hh AT r{ conversion qualifier} @findex printf AT r{, and }hh AT r{ conversion qualifier} ! The @code{hh} conversion qualifier is now supported by @code{_doprnt} ! and the @code{printf} family of functions. @findex delay AT r{, and Windows 2000 and XP} The @code{delay} function now works on Windows 2000 and XP. However, the --- 792,810 ---- @findex strtold The function @code{strtold} was added. + @findex scanf AT r{, and the X type conversion and C99} + The behaviour of the @code{%X} type conversion for the @code{scanf}-family + of functions has changed. Previously @code{%X} denoted + a @code{long} version of the @code{%x} type conversion. Now @code{%X} + is equivalent to the @code{%x} type conversion, as required by C99. + @findex _doprnt AT r{, and }hh AT r{ conversion qualifier} @findex printf AT r{, and }hh AT r{ conversion qualifier} ! @findex _doscan AT r{, and }hh AT r{ conversion qualifier} ! @findex scanf AT r{, and }hh AT r{ conversion qualifier} ! The @code{hh} conversion qualifier is now supported by @code{_doprnt}, ! @code{_doscan}, the @code{printf} family of functions ! and the @code{scanf} family of functions. @findex delay AT r{, and Windows 2000 and XP} The @code{delay} function now works on Windows 2000 and XP. However, the Index: tests/libc/ansi/stdio/makefile =================================================================== RCS file: /cvs/djgpp/djgpp/tests/libc/ansi/stdio/makefile,v retrieving revision 1.7 diff -p -c -3 -r1.7 makefile *** tests/libc/ansi/stdio/makefile 6 Dec 2002 09:37:53 -0000 1.7 --- tests/libc/ansi/stdio/makefile 14 Dec 2002 13:24:15 -0000 *************** SRC += mktemp.c *** 16,21 **** --- 16,22 ---- SRC += printf.c SRC += printf2.c SRC += sscanf.c + SRC += sscanf2.c SRC += tmpnam.c SRC += tscanf.c SRC += tsnprtf.c Index: tests/libc/ansi/stdio/sscanf2.c =================================================================== RCS file: tests/libc/ansi/stdio/sscanf2.c diff -N tests/libc/ansi/stdio/sscanf2.c *** /dev/null 1 Jan 1970 00:00:00 -0000 --- tests/libc/ansi/stdio/sscanf2.c 14 Dec 2002 13:24:18 -0000 *************** *** 0 **** --- 1,117 ---- + #include + #include + #include + #include + + typedef struct { + const char *str; + const char *fmt; + const signed char res; + } signed_testcase_t; + + typedef struct { + const char *str; + const char *fmt; + const unsigned char res; + } unsigned_testcase_t; + + signed_testcase_t s_testcases[] = { + /* Normal */ + { "127", "%hhd", 127 }, + { "127", "%hhi", 127 }, + { "0x7f", "%hhx", 127 }, + { "0x7F", "%hhX", 127 }, + { "0x7f", "%hhx", 127 }, + { "0x7F", "%hhX", 127 }, + { "0177", "%hho", 127 }, + + /* Truncation */ + { "255", "%hhd", -1 }, + { "256", "%hhd", 0 }, + { "0xff", "%hhx", -1 }, + { "0x100", "%hhx", 0 }, + { "0377", "%hho", -1 }, + { "0400", "%hho", 0 }, + { "65535", "%hhd", -1 }, + { "65536", "%hhd", 0 }, + { "0xffff", "%hhx", -1 }, + { "0x10000", "%hhx", 0 }, + { "177777", "%hho", -1 }, + { "200000", "%hho", 0 }, + + /* Terminator */ + { NULL, NULL, 0 } + }; + + unsigned_testcase_t u_testcases[] = { + /* Normal */ + { "255", "%hhu", 255 }, + + /* Truncation */ + { "256", "%hhu", 0 }, + { "65535", "%hhu", 255 }, + { "65536", "%hhu", 0 }, + + /* Terminator */ + { NULL, NULL, 0 } + }; + + static void + fail (const int testnum, + const char *input, + const char *fmt, + const char *reason, + const long code) + { + fprintf(stderr, + "FAIL: Test %d: %s %s: %s: %ld\n", + testnum, input, fmt, reason, code); + exit(EXIT_FAILURE); + } + + int + main (void) + { + signed char s_res; + unsigned char u_res; + int testnum = 0; + int ret; + int i; + + /* Signed testcases */ + for (i = 0; s_testcases[i].str != NULL; i++) { + testnum++; + s_res = 0; + + ret = sscanf(s_testcases[i].str, s_testcases[i].fmt, &s_res); + if ((ret == EOF) || (ret < 1)) { + fail(testnum, s_testcases[i].str, s_testcases[i].fmt, + "sscanf failed", ret); + } + + if (s_testcases[i].res != s_res) { + fail(testnum, s_testcases[i].str, s_testcases[i].fmt, + "unexpected result", s_res); + } + } + + /* Unsigned testcases */ + for (i = 0; u_testcases[i].str != NULL; i++) { + testnum++; + u_res = 0; + + ret = sscanf(u_testcases[i].str, u_testcases[i].fmt, &u_res); + if ((ret == EOF) || (ret < 1)) { + fail(testnum, u_testcases[i].str, u_testcases[i].fmt, + "sscanf failed", ret); + } + + if (u_testcases[i].res != u_res) { + fail(testnum, u_testcases[i].str, u_testcases[i].fmt, + "unexpected result", u_res); + } + } + + puts("PASS"); + return(EXIT_SUCCESS); + }