[PATCH 0/1] Implement character sets for sscanf()

From: Jessica Yu
Date: Fri Feb 19 2016 - 20:21:23 EST


Hi,

This patch adds support for the '%[' conversion specifier for sscanf().
Since functions that calculate substring lengths based on accepted or
rejected characters already exist in the kernel (namely strspn() and
strcspn()), it's not much of a stretch to add some basic support for the
bracket '%[' conversion specifier for sscanf(). This is useful in cases
where we'd like to match substrings delimited by something other than
spaces. The original motivation for this patch actually came from livepatch
(https://lkml.org/lkml/2016/2/8/790), where we were trying to come up with
a clean way to parse symbol names with substrings delimited by periods and
commas.

Patch based on linux-next-20160219.

Here are some test cases:
---
sscanf_tests.c
---
#include <linux/module.h>
#include <linux/kernel.h>

#define FAIL -1
#define PASS 0

static int test1(void)
{
int ret;
char buf[32] = { 0 };

ret = sscanf("this is a test\n", "%[^\n]", buf);
pr_info("test1:\n\tret: %d\n\tbuf0: %s\n", ret, buf);
if (ret != 1)
return FAIL;

return PASS;
}

static int test2(void)
{
int ret;
char buf[32] = { 0 };

ret = sscanf("this is a test\n", "%10[^\n]", buf);
pr_info("test2:\n\tret: %d\n\tbuf0: %s\n", ret, buf);
if (ret != 1 && strlen(buf) != 10)
return FAIL;

return PASS;
}

static int test3(void)
{
int ret;
char buf[32] = { 0 };

ret = sscanf("this is a test\n", "%[^ ]", buf);
pr_info("test3:\n\tret: %d\n\tbuf0: %s\n", ret, buf);
if (ret != 1)
return FAIL;

return PASS;
}

static int test4(void)
{
int ret;
char buf[3][32] = { 0 };

ret = sscanf("this is a test\n", "%[^ ] %[^ ] %[^ ]",
buf[0], buf[1], buf[2]);
pr_info("test4:\n\tret: %d\n\tbuf0: %s\n\tbuf2: %s\n\tbuf3: %s\n",
ret, buf[0], buf[1], buf[2]);
if (ret != 3)
return FAIL;

return PASS;
}

/* semicolon delimiters */
static int test5(void)
{
int ret;
char buf[2][32] = { 0 };

ret = sscanf("Hello;World;\n", "%[^;];%[^;]", buf[0], buf[1]);
pr_info("test5:\n\tret: %d\n\tbuf0: %s\n\tbuf2: %s\n",
ret, buf[0], buf[1]);
if (ret != 2)
return FAIL;

return PASS;
}

/* colon delimiters */
static int test6(void)
{
int ret;
char buf[4][32] = { 0 };

ret = sscanf("this:is:a:test", "%[^:]:%[^:]:%[^:]:%[^:]",
buf[0], buf[1], buf[2], buf[3]);
pr_info("test6:\n\tret: %d\n\tbuf0: %s\n\tbuf2: %s\n\tbuf3: %s\n\tbuf4: %s\n",
ret, buf[0], buf[1], buf[2], buf[3]);
if (ret != 4)
return FAIL;

return PASS;

}

/* tab delimiters */
static int test7(void)
{
int ret;
char buf[4][32] = { 0 };

ret = sscanf("this\tis\ta\ttest",
"%[^\t]\t%[^\t]\t%[^\t]\t%[^\t]",
buf[0], buf[1], buf[2], buf[3]);
pr_info("test7\n\tret: %d\n\tbuf0: %s\n\tbuf2: %s\n\tbuf3: %s\n\tbuf4: %s\n",
ret, buf[0], buf[1], buf[2], buf[3]);
if (ret != 4)
return FAIL;

return PASS;
}

/* error condition: empty char set */
static int test8(void)
{
int ret;
char buf[32] = { 0 };

ret = sscanf("this is a test", "this%[] is a test", buf);
pr_info("test8\n\tret: %d\n\tbuf0: %s\n", ret, buf);
if (ret != 0)
return FAIL;

return PASS;
}

static int test9(void)
{
int ret;
char buf[32] = { 0 };

ret = sscanf("aaaccbbzabccbaa", "%6[abc]", buf);
pr_info("test9\n\tret: %d\n\tbuf0: %s\n", ret, buf);
if (ret != 1 && strlen(buf) != 6)
return FAIL;

return PASS;
}

static int test10(void)
{
int ret;
char buf[3][32] = { 0 };

ret = sscanf("aaaccbbxyzzcbabb",
"%[abc]%[^abc]%[abc]",
buf[0], buf[1], buf[2]);
pr_info("test10\n\tret: %d\n\tbuf0: %s\n\tbuf1: %s\n\tbuf2: %s\n",
ret, buf[0], buf[1], buf[2]);
if (ret != 3)
return FAIL;

return PASS;
}

/* Intended usecase for livepatch symbol name parsing */
static int test11(void)
{
int ret;
char *sym = ".klp.sym.vmlinux.perf_ibs_event_update.isra.1,2";
char buf[2][32] = { 0 };
unsigned int pos;

ret = sscanf(sym, ".klp.sym.%[^.].%[^,],%u",
buf[0], buf[1], &pos);
pr_info("test11\n\tret: %d\n\tobjname: %s\n\tsymname: %s\n\tsympos: %u\n",
ret, buf[0], buf[1], pos);

if (ret != 3)
return FAIL;

return PASS;
}

/* error condition: empty set / cannot match ] bracket */
static int test12(void)
{
int ret;
char buf[32] = { 0 };

ret = sscanf("aaaa]bbbb", "%[^]]", buf);
pr_info("test12\n\tret: %d\n\tbuf0: %s\n", ret, buf);
if (ret != 0)
return FAIL;

return PASS;
}

/* no matching characters from the set */
static int test13(void)
{
int ret;
char buf[32] = { 0 };

ret = sscanf("this is a test\n", "%[123]", buf);
pr_info("test13\n\tret: %d\n\tbuf0: %s\n", ret, buf);
if (ret != 0)
return FAIL;

return PASS;
}

static int test14(void)
{
int ret;
char buf[3][32] = { 0 };

ret = sscanf("cbcd:091555:yzyz",
"%[abcde]:%[0123456789]:%[xyz]",
buf[0], buf[1], buf[2]);
pr_info("test13\n\tret: %d\n\tbuf0: %s\n\tbuf1: %s\n\tbuf2: %s\n",
ret, buf[0], buf[1], buf[2]);
if (ret != 3)
return FAIL;

return PASS;
}

static int __init sscanf_tests_init(void)
{
int ret;
int fail = 0;

ret = test1();
if (ret) {
pr_err("sscanf_tests: test1 FAILED");
fail++;
}

ret = test2();
if (ret) {
pr_err("sscanf_tests: test2 FAILED");
fail++;
}

ret = test3();
if (ret) {
pr_err("sscanf_tests: test3 FAILED");
fail++;
}

ret = test4();
if (ret) {
pr_err("sscanf_tests: test4 FAILED");
fail++;
}

ret = test5();
if (ret) {
pr_err("sscanf_tests: test5 FAILED");
fail++;
}

ret = test6();
if (ret) {
pr_err("sscanf_tests: test6 FAILED");
fail++;
}

ret = test7();
if (ret) {
pr_err("sscanf_tests: test7 FAILED");
fail++;
}

ret = test8();
if (ret) {
pr_err("sscanf_tests: test8 FAILED");
fail++;
}

ret = test9();
if (ret) {
pr_err("sscanf_tests: test9 FAILED");
fail++;
}

ret = test10();
if (ret) {
pr_err("sscanf_tests: test10 FAILED");
fail++;
}

ret = test11();
if (ret) {
pr_err("sscanf_tests: test11 FAILED");
fail++;
}

ret = test12();
if (ret) {
pr_err("sscanf_tests: test12 FAILED");
fail++;
}

ret = test13();
if (ret) {
pr_err("sscanf_tests: test13 FAILED");
fail++;
}

ret = test14();
if (ret) {
pr_err("sscanf_tests: test14 FAILED");
fail++;
}

if (!fail)
pr_info("sscanf_tests: ALL TESTS PASSED\n");
else
pr_info("sscanf_tests: %d TESTS FAILED\n", fail);

return 0;
}

static void __exit sscanf_tests_exit(void)
{
}

module_init(sscanf_tests_init);
module_exit(sscanf_tests_exit);
---

Thanks,
Jessica

Jessica Yu (1):
sscanf: implement basic character sets

lib/vsprintf.c | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)

--
2.4.3