XenevaOS
Loading...
Searching...
No Matches
string.h
Go to the documentation of this file.
1#ifndef __LINUX_STRING_H__
2#define __LINUX_STRING_H__
3
4/*
5 * DCL <linux/string.h> -- memcpy/memset/memcmp and friends.
6 *
7 * These are Xeneva's own routines (BaseHdr/string.h), not mainline's lib/:
8 * the tty layer copies into and out of flip buffers with them, and a second
9 * implementation would only be a second thing to get right. Mainline includes
10 * <linux/string.h> for the same reason -- to see the prototypes -- so the
11 * header exists to resolve the include and nothing more.
12 *
13 * tty_buffer.c's use is exactly memcpy() into char_buf_ptr(), memset() over
14 * the flags byte for a run of same-flagged characters, and the zeroing of
15 * consumed bytes in receive_buf() so a buffer can be handed on.
16 *
17 * The DRM/KMS stage then made this header carry real content, all of it for
18 * callers that reach no other string header:
19 *
20 * - strnchr, str_has_prefix, mem_is_zero and strtomem_pad, which mainline
21 * keeps here. drm_modes.c parses the video= command line with the first
22 * two, drm_edid.c asks whether a 128-byte block is all zero, and
23 * drm_connector.c:614-615 copies an HDMI vendor/product string into a
24 * fixed-width, possibly non-NUL-terminated field.
25 *
26 * - str_yes_no, re-exported from <linux/string_choices.h> rather than
27 * copied. drm_edid.c:6319 prints an HDMI capability as yes/no and
28 * includes neither that header nor <linux/string_helpers.h>, its other
29 * carrier in this tree. Copying the body would collide with the header
30 * in the files that do include it -- two static definitions of the same
31 * name in one translation unit is not a warning.
32 *
33 * - strscpy_pad and scnprintf as declarations only, with bodies in
34 * DCL/linux_math_shim.c. Both need something this header cannot supply:
35 * strscpy_pad's error return is -E2BIG, which kernel.h:281 defines
36 * *after* it includes this file at :121, so an inline here cannot be
37 * parsed in every kernel.h-initiated chain; scnprintf must reach
38 * _vsnprintf's va_list plumbing (<Log/_print.h>, KernelAA64's own
39 * header). A declaration is all the call sites need either way.
40 */
41
42#include <string.h> /* native memcpy/memset/memcmp/strlen */
43#include <stddef.h> /* size_t; ssize_t for strscpy_pad */
44#include <stdbool.h> /* bool, for mem_is_zero */
45#include <linux/string_choices.h> /* str_yes_no */
46
47/*
48 * strreplace(str, old, new) -- swap every `old` character in `str` for `new`
49 * and hand the same pointer back. mainline declares it here (:282) although
50 * the body lives in lib/string_helpers.c:985, and the declaration is load
51 * bearing: string_helpers.c:747 calls it, and with no prototype in scope the
52 * call is implicitly `int strreplace()`, which clang then rejects as
53 * "conflicting types" when it reaches the real definition 240 lines later.
54 */
55char *strreplace(char *str, char old, char new);
56
57/*
58 * strnchr(s, count, c) -- strchr() bounded to `count` bytes.
59 *
60 * The difference from strchr() is the whole reason drm_modes.c uses it
61 * (drm_modes.c:2302, :2390, :2392, :2396): the string being searched is a
62 * slice of a larger command-line buffer -- the connector name before the
63 * ':' or '-' or '@' separators -- and it is *not* NUL-terminated at the
64 * point the caller cares about. `name_end`/`options_off` are how long that
65 * slice is. A plain strchr() would run past the slice into the rest of the
66 * option string and report the separator belonging to a later field, so
67 * `video=HDMI-A-1:1280x720@60` would be read as a name that itself contains
68 * '@'. mainline's body is this loop in lib/string.c; count is decremented
69 * before the NUL test so `count == 0` answers NULL rather than reading s.
70 */
71static inline char *strnchr(const char *s, size_t count, int c)
72{
73 for (; count-- && *s != '\0'; ++s)
74 if (*s == (char)c)
75 return (char *)s;
76 return NULL;
77}
78
79/*
80 * str_has_prefix(str, prefix) -- strlen(prefix) if str starts with prefix,
81 * else 0.
82 *
83 * The return has to be the *length*, not a boolean, for the one call site:
84 * drm_modes.c:2318 matches a command-line name against each named mode with
85 *
86 * ret = str_has_prefix(name, mode->name);
87 * if (ret != name_end)
88 * continue;
89 *
90 * where `name` is the unbounded slice and `name_end` its measured length, so
91 * the comparison is "starts with mode->name *and* is exactly that long". A
92 * strncmp-style 0/1 answer would compare `1 != name_end` and every named
93 * mode would be rejected except a one-character one. strncmp is bounded by
94 * len, so a short `str` (NUL before len) also answers 0, as mainline
95 * documents.
96 */
97static inline size_t str_has_prefix(const char *str, const char *prefix)
98{
99 size_t len = strlen(prefix);
100
101 return strncmp(str, prefix, len) == 0 ? len : 0;
102}
103
104/*
105 * mem_is_zero(s, n) -- every one of the first n bytes is zero.
106 *
107 * Only caller: edid_block_is_zero() at drm_edid.c:1857, which answers
108 * whether an EDID block is a blank/absent one (`mem_is_zero(edid,
109 * EDID_LENGTH)`). mainline implements it over memchr_inv(); DCL has no
110 * memchr_inv and the block is 128 bytes on a cold path, so the plain byte
111 * loop is the same answer without a new lib helper. The void* is walked as
112 * unsigned char* deliberately: the EDID buffer's alignment is not known to
113 * this header and a word-wide read would be a fault risk for no gain.
114 */
115static inline bool mem_is_zero(const void *s, size_t n)
116{
117 const unsigned char *p = (const unsigned char *)s;
118 size_t i;
119
120 for (i = 0; i < n; i++)
121 if (p[i])
122 return false;
123 return true;
124}
125
126/*
127 * strtomem_pad(dest, src, pad) -- copy the NUL-terminated `src` into the
128 * fixed-width array `dest`, filling whatever is left with `pad`.
129 *
130 * drm_connector.c:614-615 is the call:
131 *
132 * strtomem_pad(connector->hdmi.vendor, vendor, 0);
133 * strtomem_pad(connector->hdmi.product, product, 0);
134 *
135 * where the fields are `unsigned char vendor[DRM_CONNECTOR_HDMI_VENDOR_LEN]
136 * __nonstring` (drm_connector.h:1933) -- HDMI 2.1 wants them fixed-width with
137 * no NUL, and a bytes-typed readout must see the padding rather than
138 * whatever the struct held before. The source strings come from userspace
139 * through the connector init path and are not guaranteed to be shorter than
140 * the field, so the copy must stop at sizeof(dest) without reading the
141 * source past it -- the loop below reads src[i] only while i < dest_len,
142 * which is strnlen(src, dest_len)'s guarantee without needing a strnlen DCL
143 * does not carry.
144 *
145 * The helper is a real inline and the macro supplies sizeof(dest) and the
146 * compile-time array check: dest is an array, not a pointer, and the size is
147 * not an argument (same contract as mainline's strtomem_pad).
148 * __must_be_array(dest) is the check -- its _Static_assert stops the build
149 * for a pointer, where sizeof would silently be the pointer's size and the
150 * copy would truncate -- but its *value* is not part of the length here.
151 * mainline adds it (it folds to 0 on GNU/Linux), and on this target it does
152 * not: aarch64-unknown-windows gives an empty struct -- the shape
153 * __BUILD_BUG_ON_ZERO_MSG folds to -- size 4, so
154 * `sizeof(dest) + __must_be_array(dest)` made every copy run 4 bytes past
155 * the field until the check was split onto its own line. The do/while is
156 * mainline's shape too; both call sites are statements.
157 */
158static inline void __dcl_strtomem_pad(void *dest, size_t dest_len,
159 const char *src, char pad)
160{
161 unsigned char *d = (unsigned char *)dest;
162 size_t i = 0;
163
164 while (i < dest_len && src[i] != '\0') {
165 d[i] = (unsigned char)src[i];
166 i++;
167 }
168 while (i < dest_len)
169 d[i++] = (unsigned char)pad;
170}
171
172#define strtomem_pad(dest, src, pad) do { \
173 (void)__must_be_array(dest); \
174 __dcl_strtomem_pad((dest), sizeof(dest), (src), (pad)); \
175} while (0)
176
177/*
178 * strscpy_pad(dest, src, count) -- bounded copy, NUL-terminated, with the
179 * remainder of the destination zero-filled; returns the number of characters
180 * copied (excluding the NUL), or -E2BIG if count was 0 or the copy was
181 * truncated.
182 *
183 * All five callers (drm_property.c:131, :424, :477 and drm_modes.c:2622,
184 * :2663) copy into a fixed 32-byte name field, several of them from a
185 * userspace-provided struct that may carry no NUL at all within the field;
186 * the zero-fill is what makes the kernel-side copy well-defined for the
187 * strcmp()s and printf("%s")s that follow, and the bounded read is what
188 * keeps a missing terminator from walking off the ioctl buffer. The body
189 * lives in DCL/linux_math_shim.c beside the other real functions -- a
190 * header inline would need -E2BIG, which is not defined until kernel.h:281,
191 * after this header is parsed at kernel.h:121.
192 */
193ssize_t strscpy_pad(char *dest, const char *src, size_t count);
194
195/*
196 * scnprintf(buf, size, fmt, ...) -- vsnprintf that returns what it actually
197 * wrote, clamped to size - 1 when the output did not fit.
198 *
199 * Called from drm_dev_wedged_event() (drm_drv.c:572 and :579) to build the
200 * "WEDGED=<recovery>,..." uevent string: the second call appends at
201 * event_string + len and the return is added to len, then the trailing comma
202 * is chopped with `event_string[len - 1] = '\0'`. That arithmetic only works
203 * if len never counts characters that were not written -- snprintf's
204 * would-have-written return would push len past the buffer and the chop
205 * would land on the wrong byte. The clamped return is the entire difference
206 * between scnprintf and snprintf, and it is why this cannot just be
207 * sprintf.h's `#define snprintf _snprintf`.
208 *
209 * The body is in DCL/linux_math_shim.c: it wraps _vsnprintf
210 * (<Log/_print.h>), whose va_list plumbing a BaseHdr header should not pull
211 * into every translation unit. __printf(3, 4) is mainline's annotation and
212 * is kept so the two call sites' arguments are checked against the format.
213 */
214int scnprintf(char *buf, size_t size, const char *fmt, ...) __printf(3, 4);
215
216#endif /* __LINUX_STRING_H__ */
int scnprintf(char *buf, size_t size, const char *fmt,...) __printf(3
char * strreplace(char *str, char old, char new)
ssize_t strscpy_pad(char *dest, const char *src, size_t count)
int64_t ssize_t
Definition stddef.h:38
int strncmp(const char *String1, const char *String2, ACPI_SIZE Count)
Definition utclib.c:644
#define NULL
Definition actypes.h:561
#define __printf(fmt_idx, arg_idx)
Definition compiler.h:144
size_t size
Definition drm_bridge.h:1299
const char * fmt
Definition drm_file.h:466
size_t len
Definition initial.c:23
void strlen(void)
#define d
Definition libunicode.c:1833