From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wm1-f45.google.com (mail-wm1-f45.google.com [209.85.128.45]) (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 35A9D436363 for ; Fri, 6 Feb 2026 19:11:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.45 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770405095; cv=none; b=ULpJ+Jf7ClhqhC9Wdtbb7CoaQZr9F7Whw7Io2DMBDBsZ2wNplrwfQDRQwKs4DLC9wfELZU6us0+N5I3Xv4DQ+bHDZ0KrI4zBoh1mlR2q68KNnsvglp1AnXs1/SkM/55NHcAektP+eWHQhTMMhOuAfUqT/RtsfZVEZnH1wcdJ4L8= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770405095; c=relaxed/simple; bh=01rOcWVRQdcMxDteLy9JHTq0fR3BYq/tzcjbuBbYcTg=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=IBVtMWhfRePwKCIUHtaTdaFqI6hwdlGARVjxxzqz7rZbkkhI71yCAWoxmWQSwYX4E3+aHlhnzqlPcm465bl7QVGfYwgGiTgKcJudB2RXrwq4+KHDVoEUT9UASPAMzhbFwhZxIGVfZVHUIyKcNlXI1BYTCOsbdDIVNhPTHs2Wm/Q= 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=kVOE/1L1; arc=none smtp.client-ip=209.85.128.45 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="kVOE/1L1" Received: by mail-wm1-f45.google.com with SMTP id 5b1f17b1804b1-4806e0f6b69so19306125e9.3 for ; Fri, 06 Feb 2026 11:11:35 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1770405094; x=1771009894; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=SdQzE2kro63jy9mBieyRiu/WHjypXr/LWjfAL7M+ET4=; b=kVOE/1L1Qw3LN9u01mLrreBsAlMIhSLT0X3auCsu8VDP+Zp+8JujMsBWJtIf5C7zoV QtPJFVOHkXQARui7V3bfAmmVW6onegOp14CGpL/2QzT81rY2Yy0aoAGm1Zs+kX7uPgcV NvCDF4iFJ0VtM/t3RlVoziKw6SMF2sg7urBOlO0zinRfrksBMzcuuuuswQPbB00Yjqbu 8CYmBt9gqdbl/12juqfcblChmvzFYLSuS/kB+YdgoUZhSKgF8q8dMOvDjWMsnhG7oQsB UG4qAi5Fc6WXT144yZjplttLwQqjiA1ugCZJKbcutk2kjh9cmqeOEkRUx5MSfxcx+ue/ 1Yyw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1770405094; x=1771009894; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=SdQzE2kro63jy9mBieyRiu/WHjypXr/LWjfAL7M+ET4=; b=QvyfCQH35psR6B7wq40S4tnj6DHreyT1Hva/aI2wrIaGnxhR8rD8/ON89VkcdnHeBH kqly4tZZutix5I2pCrdMDrl9ce+0k+MnObSWOhrBF2pitRHhKdbhY+Heae6en1NdIZEn QZzmpKCkDtMuxClLWp2GVGlVBGt4RBpBd1MkT6OswZuAM+weRYPjacr8zXDItVmB1PzT CIwPyKdzTnAO/AR+Ys+lPmOMDTzBKkVbwaRB30RI16D5uKiSZQKzGKzOxeo8XJbcFn0A +cQT/m/jrCMfkzMVT8Oob7VYt3bNLU0fSGH9jPclBOr+ClYURRksyveon1Dxec8HX3rs NSkA== X-Forwarded-Encrypted: i=1; AJvYcCXqxu0CgBPSuUuFGXWlQXwtSciVkg64s1jtD83OyLNAZvJLjofmUcOEhqJh8hHqqk41HXIilz9/idxdGHU=@vger.kernel.org X-Gm-Message-State: AOJu0YxX7mUbbtSqOxTYY4Ef+QEmxMAGIPYVxfMM7MwIXBYJ4ScNVQyd 5sLnPzk0AqlsYHKLDpABb2ksfjf6Mrm3Ms6Vy3IkBlueDeXrVqNvzkoPDdTrhg== X-Gm-Gg: AZuq6aIn36EJa1722kMpptz/NDphO6KJs65Srijz4Hdv59tvKju96MFV8+e8O1yIDbN QNN3cNV+aAuMGbS1YXsn+aWmyhGunJFSQ9mTSPgbYA6ov36H7KOIgChaSjdmhPTis6OJ42REPfZ 2Z+KHouBD3YP2xB3XRZn+kZuZC1Rt6NeJGI22mCBw/KAq+iyrBOsj0isCvcFv6UTtndwVRCY9Xo MXYkXjbxd9sISBKABAQQvZ62yJ5ZMrwc/gxFHOX9bMX7LShIRLqn0vIAh9sFrTgBbGTVIle3Gyf k7jVxSxANOkf7+wEIIB6D4l0vH3Z7nYu5/G1OBCiYg0X0rD1mThO7rhP7qEEzuVkdzJce03puuJ 9Gh/PRus4UcPCcSynVA4y7EYK7XlxNm/ThjfgkEnzMOmAMpBiYFKbHFKnzs6XkTJ76tXb/Hd+Du /gHTJrtS/jtfNFhIxX2TeDsw+RFNBTpKyHKs5KAuTVhhjMotysOK66OadqQG61OvX9CD3LDOqh X-Received: by 2002:a05:600c:34d1:b0:477:a21c:2066 with SMTP id 5b1f17b1804b1-483201d5d77mr48244185e9.5.1770405093497; Fri, 06 Feb 2026 11:11:33 -0800 (PST) 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 ffacd0b85a97d-4362972fb81sm8703681f8f.20.2026.02.06.11.11.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 06 Feb 2026 11:11:33 -0800 (PST) From: david.laight.linux@gmail.com To: Willy Tarreau , =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= , linux-kernel@vger.kernel.org, Cheng Li Cc: David Laight Subject: [PATCH v2 next 07/11] tools/nolibc/printf: Add support for conversion flags "#- +" and format "%X" Date: Fri, 6 Feb 2026 19:11:17 +0000 Message-Id: <20260206191121.3602-8-david.laight.linux@gmail.com> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20260206191121.3602-1-david.laight.linux@gmail.com> References: <20260206191121.3602-1-david.laight.linux@gmail.com> 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 Add support for all the normal flag chacacters except '0' (zero pad). '-' left alignment. '+' and ' ' Sign characters for non-negative numbers. '#' adds 0x to hex numbers. Partially support "%X", outputs lower case a..f the same as "%x". Move the "%s" code in with the numeric formats to save a va_arg() call. Prepend the sign (or "0x") after conversion to ascii and use the length returned by u64toh_r() and u64toa_r(). Both needed for precision and zero-padding in the next patch. Signed-off-by: David Laight --- Changes for v2: - Add support for left justifying fields (removed from patch 6). - Merge in the changes from the old patch 8. - Add support for "%#x" (formally part of patch 9). tools/include/nolibc/stdio.h | 97 ++++++++++++++++++++++++++---------- 1 file changed, 71 insertions(+), 26 deletions(-) diff --git a/tools/include/nolibc/stdio.h b/tools/include/nolibc/stdio.h index b14cf8224403..032416e900ee 100644 --- a/tools/include/nolibc/stdio.h +++ b/tools/include/nolibc/stdio.h @@ -240,10 +240,16 @@ char *fgets(char *s, int size, FILE *stream) } -/* simple printf(). It supports the following formats: - * - %[-][width][{l,t,z,ll,L,j,q}]{d,u,c,x,p,s,m,%} - * - %% - * - invalid formats are copied to the output buffer +/* printf(). Supports most of the normal integer and string formats. + * - %[#-+ ][width][{l,t,z,ll,L,j,q}]{d,i,u,c,x,X,p,s,m,%} + * - %% generates a single % + * - %m outputs strerror(errno). + * - # only affects %x and prepends 0x to non-zero values. + * - %o (octal) isn't supported. + * - %X outputs a..f the same as %x. + * - No support for zero padding, precision or variable widths. + * - No support for wide characters. + * - invalid formats are copied to the output buffer. */ /* This code uses 'flag' variables that are indexed by the low 6 bits @@ -279,7 +285,7 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, va_list unsigned int written, width; unsigned int flags, ch_flag; size_t len; - char tmpbuf[21]; + char tmpbuf[32 + 24]; const char *outstr; written = 0; @@ -334,19 +340,32 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, va_list /* Conversion specifiers. */ - /* Numeric conversion specifiers. */ - ch_flag = _NOLIBC_PF_CHAR_IS_ONE_OF(ch, 'c', 'd', 'i', 'u', 'x', 'p'); - if (ch_flag != 0) { + /* Numeric and pointer conversion specifiers. + * + * Use an explicit bound check (rather than _NOLIBC_PF_CHAR_IS_ONE_OF()) + * so that 'X' can be allowed through. + * 'X' gets treated and 'x' because _NOLIBC_PF_FLAG() returns the same + * value for both. + */ + if ((ch < 'a' || ch > 'z') && ch != 'X') + goto non_numeric_conversion; + + /* We need to check for "%p" or "%#x" later, merging here gives better code. + * But '#' collides with 'c' so shift right. + */ + ch_flag = _NOLIBC_PF_FLAG(ch) | (flags & _NOLIBC_PF_FLAG('#')) >> 1; + if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'c', 'd', 'i', 'u', 'x', 'p', 's')) { unsigned long long v; long long signed_v; - char *out = tmpbuf; + char *out = tmpbuf + 32; + int sign = 0; /* 'long' is needed for pointer/string conversions and ltz lengths. * A single test can be used provided 'p' (the same bit as '0') * is masked from flags. */ if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag | (flags & ~_NOLIBC_PF_FLAG('p')), - 'p', 'l', 't', 'z')) { + 'p', 's', 'l', 't', 'z')) { v = va_arg(args, unsigned long); signed_v = (long)v; } else if (_NOLIBC_PF_FLAGS_CONTAIN(flags, 'j', 'q')) { @@ -365,40 +384,62 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, va_list goto do_output; } + if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 's')) { + /* "%s" - character string. */ + if (!v) { + outstr = "(null)"; + len = 6; + goto do_output; + } + outstr = (void *)v; +do_strnlen_output: + len = strnlen(outstr, INT_MAX); + goto do_output; + } + if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'd', 'i')) { /* "%d" and "%i" - signed decimal numbers. */ if (signed_v < 0) { - *out++ = '-'; + sign = '-'; v = -(signed_v + 1); v++; + } else if (_NOLIBC_PF_FLAGS_CONTAIN(flags, '+')) { + sign = '+'; + } else if (_NOLIBC_PF_FLAGS_CONTAIN(flags, ' ')) { + sign = ' '; } } /* Convert the number to ascii in the required base. */ if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'd', 'i', 'u')) { /* Base 10 */ - u64toa_r(v, out); + len = u64toa_r(v, out); } else { /* Base 16 */ - if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'p')) { - *(out++) = '0'; - *(out++) = 'x'; + if (_NOLIBC_PF_FLAGS_CONTAIN(ch_flag, 'p', '#' - 1)) { + /* "%p" and "%#x" need "0x" prepending. */ + sign = 'x' | '0' << 8; } - u64toh_r(v, out); + len = u64toh_r(v, out); } - outstr = tmpbuf; - } - else if (ch == 's') { - outstr = va_arg(args, char *); - if (!outstr) - outstr="(null)"; + /* Add 0, 1 or 2 ("0x") sign characters left of any zero padding */ + for (; sign; sign >>= 8) { + len++; + *--out = sign; + } + outstr = out; + goto do_output; } - else if (ch == 'm') { + +non_numeric_conversion: + if (ch == 'm') { #ifdef NOLIBC_IGNORE_ERRNO outstr = "unknown error"; + len = __builtin_strlen(outstr); #else outstr = strerror(errno); + goto do_strnlen_output; #endif /* NOLIBC_IGNORE_ERRNO */ } else { if (ch != '%') { @@ -409,10 +450,10 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, va_list /* %% is documented as a 'conversion specifier'. * Any flags, precision or length modifier are ignored. */ + len = 1; width = 0; - outstr = "%"; + outstr = fmt - 1; } - len = strlen(outstr); } do_output: @@ -423,6 +464,10 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, va_list */ __asm__ volatile("" : "=r"(len) : "0"(len)); + /* Output 'left pad', 'value' then 'right pad'. */ + flags = _NOLIBC_PF_FLAGS_CONTAIN(flags, '-'); + if (flags && cb(state, outstr, len) != 0) + return -1; while (width > len) { unsigned int pad_len = ((width - len - 1) & 15) + 1; width -= pad_len; @@ -430,7 +475,7 @@ int __nolibc_printf(__nolibc_printf_cb cb, void *state, const char *fmt, va_list if (cb(state, " ", pad_len) != 0) return -1; } - if (cb(state, outstr, len) != 0) + if (!flags && cb(state, outstr, len) != 0) return -1; } -- 2.39.5