From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wm1-f52.google.com (mail-wm1-f52.google.com [209.85.128.52]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E39FA37CD3B for ; Mon, 23 Mar 2026 11:22:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.52 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774264978; cv=none; b=DDYzq8qOAcqiBlBzyNoUL9Xv1PrLiljtbGS2DGlVumIJLdMru56GHM77wvBkaAGJXRcE0Cxa220xKsqewnlBUoqRTYsDlSvxP0wBB1JyjGcCQ9iwvjWcGx6+7Zy2Sv9vZfzYAS/h58qALLNj9OsN6fF06n9e961tzsPqLzoHPeE= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774264978; c=relaxed/simple; bh=AYtxnv0aztPfD8k6HhUEnD+uNXO7LWv1ce9MbsQCJGg=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=WrbGC/iG73Ep/eZJJUPAP8eiEyjDX+sHY5Y8rr6pqeymjN5GAFvYYQHK1MEb+Ce8zObPYZAUKTCWnR3/zvWlEphx4T8AVIxr7gomq3picvXwvkrVNC1h0aMfwQMlujh8lX9QmSLHk77i0L/MOtKSDuVy5jsg1haT6mex7TUQ8kU= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=bp1uZ0GH; arc=none smtp.client-ip=209.85.128.52 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="bp1uZ0GH" Received: by mail-wm1-f52.google.com with SMTP id 5b1f17b1804b1-4853e1ce427so28031055e9.3 for ; Mon, 23 Mar 2026 04:22:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1774264975; x=1774869775; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=FYEifFgQbMcAXLTUhSlfokqJMsEhed8+Lvkgidffdcs=; b=bp1uZ0GHluueCWwpHwgtsNDnU9iRviXj1vC2kqaCO3mHIFRPYJOfmA/kdjBQC/QXyt TGTUPGkMbBSBrjzqHjNaTNdm6TRllSA4Mg/httIxYIlJJ2BJlF022GCDH3vPM72jAUtA its/7ciCeAtC1orww6yCLG7F+JgF0cHLM6r1j+nnY2PcszIFd/Vei4iH69aBkM/DSBy2 FzWFrrJHZwB6QiKr8OSsu6RiLS8jsdzdFAXO7K/JFTJFWowGSdqCoTlAUehW4QXpWfab anBNCMtIxmyPUd+39tB4SpQ0jjv+fV/lebJUmUDtMiqBBzNFigI3Lo1jVl+OOLsdemgV xMkA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774264975; x=1774869775; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=FYEifFgQbMcAXLTUhSlfokqJMsEhed8+Lvkgidffdcs=; b=HnCPTJQ6DW57H8/d4M5aoBvwIVH85tgE4qr+xLOpCq2uWMfdkuo68apsF57qdoh/mJ ijP/QUT1g3SP4SDwAHHmx9MFWxfgoEaTWRas1x4NH3O9Z95BktIVq+EjU56pFN6PEmbG 4qQ7OFGPvmcy6+vbcwjdr9wDnBRP0ehKXRllQb0jBMPwgs208XVKIegGk26RIQBWa6Ok w1vmbo45Hc8DsQW7nNxgyvPJwWe2YovRsxXXm5AzofaWFlK1ja/by5TcsP6l4bm8aLcY q7eDeYQsuyspkp5WTTH5Rka6TW+1oNIFtkij8CzfgmQEN4f7nEklAZSCWqzrywjBQZnz 85dg== X-Forwarded-Encrypted: i=1; AJvYcCX9yJrzlLBcMrPtsEe0gQUda0LZWp4FaZY0w12NR+D1vVLHqYnN4WMN5sBrXDrKC4KFMfV2XQGkIXXT5eU=@vger.kernel.org X-Gm-Message-State: AOJu0YyX9qgf73uYreUWfe1ADPSWpLDsw46zQHsO5rrommVCOy/+nk65 7mILc665adZT3DQPdXOVQYeQIRz0ou/nI0IibkrT71tYC1DKrX+pyCPP X-Gm-Gg: ATEYQzx1gII7r7S3Cqqu5oMk4/+NaM8AYOC/W/ZlWNTahpJOFTkDd8QAWPblEYQZ0bb cRXiLoquxHl/x0whlcxY/4bItPWIrnaWawY9FvYsOUUB3Hpesi/k5wvw9Z7g6p3vCa5/zITmnP4 SboDJhg+NjOxuCl/Bed3u1hZ8W3EZfxjmagYcQoNghRnnWyc4wn5cOPgjSMrwFAL6tpkST+NiGp nq4hg/fqTyYaalcry6wiGriWGN2EmCM9oaUTfPpTxl4a+ooLUTOMa7ZudXZXNMnlW7v0WbTXwjS UxT/7cjuHkBraAZhwVG4FjwCSQ6SH0lJeY4JEb8gg+srj1Hv39IdhXhX1cloqiMWxpmrOrNmFan zY7jYCTtPOYv8WHfIL51DoyTgsYizX7PeBoXkwkR+igeGpl5ipyJXfbJcBXZvH1MDh9Jcwz2LMq vzz6A7HbgDjxgRAutGe2z8q616D0vCMv/FBhTn9DtvmZOKx41YX7gnaIxT+ZFOjzPkShKY0Zlrt 5ApLmjHJGx8 X-Received: by 2002:a05:600c:4f56:b0:485:4535:73d with SMTP id 5b1f17b1804b1-486fede728bmr180154785e9.2.1774264974721; Mon, 23 Mar 2026 04:22:54 -0700 (PDT) Received: from snowdrop.snailnet.com (82-69-66-36.dsl.in-addr.zen.co.uk. [82.69.66.36]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-486fe8367d8sm325691255e9.14.2026.03.23.04.22.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Mar 2026 04:22:54 -0700 (PDT) From: david.laight.linux@gmail.com To: Willy Tarreau , =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org Cc: David Laight Subject: [PATCH 1/1] tools/nolibc/printf: Support negative variable width and precision Date: Mon, 23 Mar 2026 11:22:47 +0000 Message-Id: <20260323112247.3196-1-david.laight.linux@gmail.com> X-Mailer: git-send-email 2.39.5 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: David Laight For (eg) "%*.*s" treat a negative field width as a request to left align the output (the same as the '-' flag), and a negative precision to request the default precision. Set the default precision to -1 (not INT_MAX) and add explicit checks to the string handling for negative values (makes the tet unsigned). For numeric output check for 'precision >= 0' instead of testing _NOLIBC_PF_FLAGS_CONTAIN(flags, '.'). This needs an inverted test, some extra goto and removes an indentation. The changed conditionals fix printf("%0-#o", 0) - but '0' and '-' shouldn't both be specified. Best viewed with 'git diff -b' after being commited. Additional test cases added. Signed-off-by: David Laight --- I missed this bit in the earlier patches. Size wise it is pretty neutral. It really seems to depend on how many registers get saved across the call to _nolibc_u64toa_base() - gcc doesn't seem to use the correct registers to avoid spills. I did look at whether making 'width' negative at the top was better than keeping a '-' flag - but it bloats things because you need the absolute value at the bottom. tools/include/nolibc/stdio.h | 68 +++++++++++--------- tools/testing/selftests/nolibc/nolibc-test.c | 5 +- 2 files changed, 41 insertions(+), 32 deletions(-) diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h index 8f7e1948a651..b6d14a58cfe7 100644 --- a/tools/include/nolibc/stdio.h +++ b/tools/include/nolibc/stdio.h @@ -347,6 +347,7 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, va_list char *out; const char *outstr; unsigned int sign_prefix; + int got_width; written = 0; while (1) { @@ -377,23 +378,28 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, va_list } /* Width and precision */ - for (;; ch = *fmt++) { + for (got_width = 0;; ch = *fmt++) { if (ch == '*') { - precision = va_arg(args, unsigned int); + precision = va_arg(args, int); ch = *fmt++; } else { for (precision = 0; ch >= '0' && ch <= '9'; ch = *fmt++) precision = precision * 10 + (ch - '0'); } - if (_NOLIBC_PF_FLAGS_CONTAIN(flags, '.')) + if (got_width) break; width = precision; if (ch != '.') { /* Default precision for strings */ - precision = INT_MAX; + precision = -1; break; } - flags |= _NOLIBC_PF_FLAG('.'); + got_width = 1; + } + /* A negative width (e.g. from "%*s") requests left justify. */ + if (width < 0) { + width = -width; + flags |= _NOLIBC_PF_FLAG('-'); } /* Length modifier. @@ -457,7 +463,7 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, va_list if (!outstr) { outstr = "(null)"; /* Match glibc, nothing output if precision too small */ - len = precision >= 6 ? 6 : 0; + len = precision < 0 || precision >= 6 ? 6 : 0; goto do_output; } goto do_strlen_output; @@ -533,32 +539,34 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, va_list } /* Add zero padding */ - if (_NOLIBC_PF_FLAGS_CONTAIN(flags, '0', '.')) { - if (!_NOLIBC_PF_FLAGS_CONTAIN(flags, '.')) { - if (_NOLIBC_PF_FLAGS_CONTAIN(flags, '-')) - /* Left justify overrides zero pad */ - goto prepend_sign; - /* eg "%05d", Zero pad to field width less sign. - * Note that precision can end up negative so all - * the variables have to be 'signed int'. - */ - precision = width; - if (sign_prefix) { + if (precision < 0) { + /* No explicit precision (or negative from "%.*s"). */ + if (!_NOLIBC_PF_FLAGS_CONTAIN(flags, '0')) + goto no_zero_padding; + if (_NOLIBC_PF_FLAGS_CONTAIN(flags, '-')) + /* Left justify overrides zero pad */ + goto no_zero_padding; + /* eg "%05d", Zero pad to field width less sign. + * Note that precision can end up negative so all + * the variables have to be 'signed int'. + */ + precision = width; + if (sign_prefix) { + precision--; + if (sign_prefix >= 256) precision--; - if (sign_prefix >= 256) - precision--; - } - } - if (precision > 31) - /* Don't run off the start of outbuf[], arbitrary limit - * longer than the longest number field. */ - precision = 31; - for (; len < precision; len++) { - /* Stop gcc generating horrid code and memset(). */ - _NOLIBC_OPTIMIZER_HIDE_VAR(len); - *--out = '0'; } } + if (precision > 31) + /* Don't run off the start of outbuf[], arbitrary limit + * longer than the longest number field. */ + precision = 31; + for (; len < precision; len++) { + /* Stop gcc generating horrid code and memset(). */ + _NOLIBC_OPTIMIZER_HIDE_VAR(len); + *--out = '0'; + } +no_zero_padding: /* %#o has set sign_prefix to '0', but we don't want so add an extra * leading zero here. @@ -603,7 +611,7 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, va_list do_strlen_output: /* Open coded strnlen() (slightly smaller). */ - for (len = 0; len < precision; len++) + for (len = 0; precision < 0 || len < precision; len++) if (!outstr[len]) break; diff --git a/tools/testing/selftests/nolibc/nolibc-test.c b/tools/testing/selftests/nolibc/nolibc-test.c index 0ca695acbc44..83082486b8c3 100644 --- a/tools/testing/selftests/nolibc/nolibc-test.c +++ b/tools/testing/selftests/nolibc/nolibc-test.c @@ -1825,7 +1825,7 @@ static int run_printf(int min, int max) CASE_TEST(char); EXPECT_VFPRINTF(1, "|c|d| e|", "|%c|%.0c|%4c|", 'c', 'd', 'e'); break; CASE_TEST(octal); EXPECT_VFPRINTF(1, "|17| 0033||", "|%o|%6.4o|%.0o|", 017, 033, 0); break; CASE_TEST(octal_max); EXPECT_VFPRINTF(1, "1777777777777777777777", "%llo", ~0ULL); break; - CASE_TEST(octal_alt); EXPECT_VFPRINTF(1, "|0|01|02|034|0|", "|%#o|%#o|%#02o|%#02o|%#.0o|", 0, 1, 2, 034, 0); break; + CASE_TEST(octal_alt); EXPECT_VFPRINTF(1, "|0|01|02|034|0|0|", "|%#o|%#o|%#02o|%#02o|%#.0o|%0-#o|", 0, 1, 2, 034, 0, 0); break; CASE_TEST(hex_nolibc); EXPECT_VFPRINTF(is_nolibc, "|f|d|", "|%x|%X|", 0xf, 0xd); break; CASE_TEST(hex_libc); EXPECT_VFPRINTF(!is_nolibc, "|f|D|", "|%x|%X|", 0xf, 0xd); break; CASE_TEST(hex_alt); EXPECT_VFPRINTF(1, "|0x1| 0x2| 0|", "|%#x|%#5x|%#5x|", 1, 2, 0); break; @@ -1843,6 +1843,7 @@ static int run_printf(int min, int max) CASE_TEST(truncation); EXPECT_VFPRINTF(1, "012345678901234567890123456789", "%s", "012345678901234567890123456789"); break; CASE_TEST(string_width); EXPECT_VFPRINTF(1, " 1", "%10s", "1"); break; CASE_TEST(string_trunc); EXPECT_VFPRINTF(1, " 12345", "%10.5s", "1234567890"); break; + CASE_TEST(string_var); EXPECT_VFPRINTF(1, "| ab|ef | ij|kl |", "|%*.*s|%*.*s|%*.*s|%*.*s|", 3, 2, "abcd", -3, 2, "efgh", 3, -1, "ij", -3, -1, "kl"); break; CASE_TEST(number_width); EXPECT_VFPRINTF(1, " 1", "%10d", 1); break; CASE_TEST(number_left); EXPECT_VFPRINTF(1, "|-5 |", "|%-8d|", -5); break; CASE_TEST(string_align); EXPECT_VFPRINTF(1, "|foo |", "|%-8s|", "foo"); break; @@ -1856,7 +1857,7 @@ static int run_printf(int min, int max) CASE_TEST(num_p_tr_libc);EXPECT_VFPRINTF(!is_nolibc, "00000000000000000000000000000000005", "%035d", 5); break; CASE_TEST(number_prec); EXPECT_VFPRINTF(1, " 00005", "%10.5d", 5); break; CASE_TEST(num_prec_neg); EXPECT_VFPRINTF(1, " -00005", "%10.5d", -5); break; - CASE_TEST(num_prec_var); EXPECT_VFPRINTF(1, " -00005", "%*.*d", 10, 5, -5); break; + CASE_TEST(number_var); EXPECT_VFPRINTF(1, "| -00005|5 |", "|%*.*d|%*.*d|", 10, 5, -5, -2, -10, 5); break; CASE_TEST(num_0_prec_0); EXPECT_VFPRINTF(1, "|| |+||||", "|%.0d|% .0d|%+.0d|%.0u|%.0x|%#.0x|", 0, 0, 0, 0, 0, 0); break; CASE_TEST(errno); errno = 22; EXPECT_VFPRINTF(is_nolibc, "errno=22", "%m"); break; CASE_TEST(errno-neg); errno = -22; EXPECT_VFPRINTF(is_nolibc, "errno=-22 ", "%-12m"); break; -- 2.39.5