mailing list of musl libc
 help / color / mirror / code / Atom feed
From: Rich Felker <dalias@aerifal.cx>
To: musl@lists.openwall.com
Subject: Re: New scanf testing framework
Date: Tue, 17 Apr 2012 23:47:09 -0400	[thread overview]
Message-ID: <20120418034709.GV7281@brightrain.aerifal.cx> (raw)
In-Reply-To: <20120418025440.GU7281@brightrain.aerifal.cx>

[-- Attachment #1: Type: text/plain, Size: 515 bytes --]

On Tue, Apr 17, 2012 at 10:54:40PM -0400, Rich Felker wrote:
> I tried it with glibc, and realized it's invoking undefined behavior
> by writing to the temp file with byte-oriented fwrite then performing
> fwscanf (wide) to read from it. I'll send a new version (along with a
> heapload of tests) in a little bit.

Here's the new version, fixed to write temp files without violating
the rule that you can't mix byte and wide io. I've committed to musl a
number of fixes based on early results of these tests.

Rich

[-- Attachment #2: scanf.c --]
[-- Type: text/plain, Size: 8480 bytes --]

#include <stdio.h>
#include <wchar.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <math.h>

int do_test(int func, const char *name,
	const char *fmt, const wchar_t *wfmt,
	const char *input, int xret, int xlen,
	const void *expect, size_t size, void *obuf)
{
	int consumed = 0;
	int err = 0;
	FILE *f;
	int ret;
	int same;

	if (func&1) {
		f = tmpfile();
		if (!f) {
			printf("%s: cannot create temp file: %s\n",
				name, strerror(errno));
			return 1;
		}
		if (func&2) fwprintf(f, L"%s", input);
		else fputs(input, f);
		rewind(f);
		if (func&2) ret = fwscanf(f, wfmt, obuf, &consumed);
		else ret = fscanf(f, fmt, obuf, &consumed);
	} else {
		wchar_t winput[1024];
		swprintf(winput, 1024, L"%s", input);
		if (func&2) ret = swscanf(winput, wfmt, obuf, &consumed);
		else ret = sscanf(input, fmt, obuf, &consumed);
	}

	if (func&1) {
		int final = ftell(f);
		if (final != xlen) {
			printf("%s: wrong final position %d, should be %d\n",
				name, final, xlen);
			err = 1;
		}
		fclose(f);
	}

	if (ret != xret) {
		printf("%s: returned %d, expected %d\n", name, ret, xret);
		return 1;
	}

	if (ret <= 0) return err;

	if (consumed != xlen) {
		printf("%s: consumed %d, should have consumed %d\n",
			name, consumed, xlen);
		err = 1;
	}

	/* We assume no padding bits except possibly long double. */
	if (size == sizeof(long double) && strchr(fmt, 'L'))
		same = *(long double *)obuf == *(long double *)expect;
	else
		same = !memcmp(expect, obuf, size);
	if (!same) {
		printf("%s: wrong value: ", name);
		return 2;
	}

	return err;
}


#define TEST_1(func,name,fmt,in,xr,xl,type,ex,ofmt,obuf) do { \
	memset(obuf, 0, sizeof ex); \
	switch (do_test(func,name,fmt "%n",fmt L"%n",in,xr,xl,(type[]){ex},sizeof ex,obuf)) { \
	case 2: if (#ex[0]=='"' || #ex[0]=='L') \
	printf(ofmt ", expected " ofmt "\n", (type *)obuf, ex); \
	else printf(ofmt ", expected " ofmt "\n", *(type *)obuf, ex); \
	case 1: err++; } } while (0)

#define TEST(fmt,in,xr,xl,type,ex,ofmt) do { \
TEST_1(0,"sscanf(" #in "," #fmt ")",fmt,in,xr,xl,type,ex,ofmt,obuf); \
TEST_1(1,"fscanf(" #in "," #fmt ")",fmt,in,xr,xl,type,ex,ofmt,obuf); \
TEST_1(2,"swscanf(" #in "," #fmt ")",fmt,in,xr,xl,type,ex,ofmt,obuf); \
TEST_1(3,"fwscanf(" #in "," #fmt ")",fmt,in,xr,xl,type,ex,ofmt,obuf); \
} while (0)

/* TEST macro takes 7 arguments:
 * - format string ("%n" is automatically appended to test termination)
 * - input text to scan
 * - expected return value (-1, 0, or 1)
 * - expected number of characters consumed
 * - data type (char or wchar_t for strings) of result
 * - expected result
 * - format for printing result mismatches
 * All arguments should be literals of the correct type, not
 * programmatically generated. The macro does some crazy stuff.
 */

int main()
{
	void *obuf = malloc(256);
	int err=0;

	TEST("%9d", "9999999999", 1, 9, int, 999999999, "%d");
	TEST("%5s", "abcdefg", 1, 5, char, "abcde", "%s");
	TEST("%5[abc]", "ababab", 1, 5, char, "ababa", "%s");
	TEST("%x", "0xq", 0, 2, int, 0, "%d");
	TEST("%Lf", "1.0", 1, 3, long double, 1.0, "%La");

	TEST("%d", "123", 1, 3, int, 123, "%d");
	TEST("%d", "123a", 1, 3, int, 123, "%d");
	TEST("%d", "0x2", 1, 1, int, 0, "%d");
	TEST("%3d", "12345", 1, 3, int, 123, "%d");
	TEST("%d", "0123", 1, 4, int, 123, "%d");
	TEST("%d", "088", 1, 3, int, 88, "%d");
	TEST("%d", "+", 0, 1, int, 0, "%d");
	TEST("%1d", "+1", 0, 1, int, 0, "%d");
	TEST("%d", "+inf", 0, 1, int, 0, "%d");
	TEST("%d", "", -1, 0, int, 0, "%d");
	TEST("%d", "hello", 0, 0, int, 0, "%d");

	TEST("%i", "123", 1, 3, int, 123, "%d");
	TEST("%i", "123a", 1, 3, int, 123, "%d");
	TEST("%i", "0x2", 1, 3, int, 2, "%#x");
	TEST("%3i", "12345", 1, 3, int, 123, "%d");
	TEST("%i", "0123", 1, 4, int, 0123, "%#o");
	TEST("%i", "088", 1, 1, int, 0, "%d");
	TEST("%i", "+", 0, 1, int, 0, "%d");
	TEST("%1i", "+1", 0, 1, int, 0, "%d");
	TEST("%i", "+inf", 0, 1, int, 0, "%d");
	TEST("%i", "", -1, 0, int, 0, "%d");
	TEST("%i", "hello", 0, 0, int, 0, "%d");

	TEST("%o", "7", 1, 1, unsigned, 7, "%#o");
	TEST("%o", "007", 1, 3, unsigned, 7, "%#o");
	TEST("%o", "3778", 1, 3, unsigned, 0377, "%#o");
	TEST("%2o", "377", 1, 2, unsigned, 037, "%#o");
	TEST("%o", "+", 0, 1, unsigned, 0, "%#o");
	TEST("%1o", "+1", 0, 1, unsigned, 0, "%#o");
	TEST("%o", "+@", 0, 1, unsigned, 0, "%#o");
	TEST("%o", "", -1, 0, unsigned, 0, "%#o");
	TEST("%o", "hello", 0, 0, unsigned, 0, "%#o");

	TEST("%u", "-1", 1, 2, unsigned, -1U, "%u");

	TEST("%x", "101", 1, 3, unsigned, 0x101, "%#x");
	TEST("%x", "123a", 1, 4, unsigned, 0x123a, "%#x");
	TEST("%x", "0xff", 1, 4, unsigned, 0xff, "%#x");
	TEST("%x", "123g", 1, 3, unsigned, 0x123, "%#x");
	TEST("%3x", "12345", 1, 3, unsigned, 0x123, "%#x");
	TEST("%x", "0123", 1, 4, unsigned, 0x123, "%#x");
	TEST("%x", "088", 1, 3, unsigned, 0x88, "%#x");
	TEST("%x", "+", 0, 1, unsigned, 0, "%#x");
	TEST("%1x", "+1", 0, 1, unsigned, 0, "%#x");
	TEST("%x", "-0x", 0, 3, unsigned, 0, "%#x");
	TEST("%3x", "-0x1", 0, 3, unsigned, 0, "%#x");
	TEST("%x", "", -1, 0, unsigned, 0, "%#x");
	TEST("%x", "hello", 0, 0, unsigned, 0, "%#x");

	TEST("%f", "1.1", 1, 3, float, 1.1f, "%a");
	TEST("%lf", "1.1", 1, 3, double, 1.1, "%a");
	TEST("%Lf", "1.1", 1, 3, long double, 1.1l, "%a");
	TEST("%4f", "1.251", 1, 4, float, 1.25f, "%a");
	TEST("%f", "1.25p", 1, 4, float, 1.25f, "%a");
	TEST("%f", "-0x1p-2", 1, 7, float, -0x1p-2f, "%a");
	TEST("%f", "+1.e5", 1, 5, float, 100000.0f, "%f");
	TEST("%f", "-inf", 1, 4, float, -INFINITY, "%f");

	TEST("%f", "+", 0, 1, float, 0.0f, "%a");
	TEST("%f", "+.", 0, 2, float, 0.0f, "%a");
	TEST("%f", "+..", 0, 2, float, 0.0f, "%a");
	TEST("%f", "+.e", 0, 2, float, 0.0f, "%a");
	TEST("%f", "+1.e", 0, 4, float, 0.0f, "%a");
	TEST("%f", "+1.e.", 0, 4, float, 0.0f, "%f");
	TEST("%6f", "-0x1p-2", 0, 6, float, 0.0f, "%a");

	TEST("%f", "", -1, 0, float, 0.0f, "%f");
	TEST("%f", "bye", 0, 0, float, 0.0f, "%f");

	TEST("%f", "i", 0, 1, float, 0.0f, "%f");
	TEST("%f", "in", 0, 2, float, 0.0f, "%f");
	TEST("%f", "inf", 1, 3, float, INFINITY, "%f");
	TEST("%f", "infi", 0, 4, float, 0.0f, "%f");
	TEST("%f", "infin", 0, 5, float, 0.0f, "%f");
	TEST("%f", "infini", 0, 6, float, 0.0f, "%f");
	TEST("%f", "infinit", 0, 7, float, 0.0f, "%f");
	TEST("%f", "infinity", 1, 8, float, INFINITY, "%f");

	TEST("%s", "happy", 1, 5, char, "happy", "%s");
	TEST("%s", "happy birthday", 1, 5, char, "happy", "%s");
	TEST("%s", "   hello", 1, 8, char, "hello", "%s");
	TEST("%s", "goodbye   ", 1, 7, char, "goodbye", "%s");
	TEST("%4s", "goodbye", 1, 4, char, "good", "%s");
	TEST("%s", "", -1, 0, char, "", "%s");
	TEST("%s", "     ", -1, 5, char, "", "%s");

	TEST("%ls", "happy", 1, 5, wchar_t, L"happy", "%ls");
	TEST("%ls", "happy birthday", 1, 5, wchar_t, L"happy", "%ls");
	TEST("%ls", "   hello", 1, 8, wchar_t, L"hello", "%ls");
	TEST("%ls", "goodbye   ", 1, 7, wchar_t, L"goodbye", "%ls");
	TEST("%4ls", "goodbye", 1, 4, wchar_t, L"good", "%ls");
	TEST("%ls", "", -1, 0, wchar_t, L"", "%ls");
	TEST("%ls", "     ", -1, 5, wchar_t, L"", "%ls");

	TEST("%[abc]", "abab", 1, 4, char, "abab", "%s");
	TEST("%[abc]", "abadaba", 1, 3, char, "aba", "%s");
	TEST("%3[abc]", "abab", 1, 3, char, "aba", "%s");
	TEST("%[abc]", "", -1, 0, char, "", "%s");
	TEST("%[abc]", "d", 0, 0, char, "", "%s");
	TEST("%[]]", "][", 1, 1, char, "]", "%s");
	TEST("%[^]]", "][", 0, 0, char, "", "%s");
	TEST("%[-]", "--", 1, 2, char, "--", "%s");
	TEST("%[@-]", "A", 0, 0, char, "", "%s");
	TEST("%[-z]", "a", 0, 0, char, "", "%s");

	TEST("%l[abc]", "abab", 1, 4, wchar_t, L"abab", "%ls");
	TEST("%l[abc]", "abadaba", 1, 3, wchar_t, L"aba", "%ls");
	TEST("%3l[abc]", "abab", 1, 3, wchar_t, L"aba", "%ls");
	TEST("%l[abc]", "", -1, 0, wchar_t, L"", "%ls");
	TEST("%l[abc]", "d", 0, 0, wchar_t, L"", "%ls");
	TEST("%l[]]", "][", 1, 1, wchar_t, L"]", "%ls");
	TEST("%l[^]]", "][", 0, 0, wchar_t, L"", "%ls");
	TEST("%l[-]", "--", 1, 2, wchar_t, L"--", "%ls");
	TEST("%l[@-]", "A", 0, 0, wchar_t, L"", "%ls");
	TEST("%l[-z]", "a", 0, 0, wchar_t, L"", "%ls");

	TEST("%c", "wow", 1, 1, char, "w", "%s");
	TEST("%3c", "wow", 1, 3, char, "wow", "%s");
	TEST("%3c", "wo", 0, 2, char, "", "%s");
	TEST("%c", "", -1, 0, char, "", "%s");
	TEST("%c", " wow", 1, 1, char, " ", "%s");

	TEST("%lc", "wow", 1, 1, wchar_t, L"w", "%ls");
	TEST("%3lc", "wow", 1, 3, wchar_t, L"wow", "%ls");
	TEST("%3lc", "wo", 0, 2, wchar_t, L"", "%ls");
	TEST("%lc", "", -1, 0, wchar_t, L"", "%ls");
	TEST("%lc", " wow", 1, 1, wchar_t, L" ", "%ls");

	return !!err;
}

      reply	other threads:[~2012-04-18  3:47 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-04-18  1:06 Rich Felker
2012-04-18  2:54 ` Rich Felker
2012-04-18  3:47   ` Rich Felker [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20120418034709.GV7281@brightrain.aerifal.cx \
    --to=dalias@aerifal.cx \
    --cc=musl@lists.openwall.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/musl/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).