ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
mdl_sanitize.c
Go to the documentation of this file.
1
9#include "mdl_sanitize.h"
10
11#include <stdio.h>
12#include <string.h>
13
14#include "ra8_attributes.h"
15
22
24static const char* const s_fallback_name = "item";
25
27static const char* const s_reserved_exact[] = {"con", "prn", "aux", "nul"};
29static const char* const s_reserved_numbered[] = {"com", "lpt"};
30
46{
47 return (char)(((c >= 'A') && (c <= 'Z')) ? (c + ('a' - 'A')) : c);
48}
49
65{
66 return ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) || ((c >= '0') && (c <= '9')) ||
67 (c == '.') || (c == '-') || (c == '_');
68}
69
84RA8_INTERNAL static bool internal_is_dot_segment(const char* name)
85{
86 return (name[0] == '\0') || (strcmp(name, ".") == 0) || (strcmp(name, "..") == 0);
87}
88
102RA8_INTERNAL static void internal_base_of(const char* name, char* base, size_t cap)
103{
104 size_t i = 0U;
105 while ((name[i] != '\0') && (name[i] != '.') && ((i + 1U) < cap)) {
106 base[i] = internal_lower_ascii(name[i]);
107 ++i;
108 }
109 base[i] = '\0';
110}
111
126RA8_INTERNAL static bool internal_is_reserved_base(const char* name)
127{
128 char base[k_reserved_base_max];
129 internal_base_of(name, base, sizeof(base));
130 for (size_t i = 0U; i < (sizeof(s_reserved_exact) / sizeof(s_reserved_exact[0])); ++i) {
131 if (strcmp(base, s_reserved_exact[i]) == 0) {
132 return true;
133 }
134 }
135 if ((strlen(base) == (size_t)k_reserved_len) && (base[k_reserved_digit_at] >= '1') &&
136 (base[k_reserved_digit_at] <= '9')) {
137 for (size_t i = 0U; i < (sizeof(s_reserved_numbered) / sizeof(s_reserved_numbered[0])); ++i) {
138 if (strncmp(base, s_reserved_numbered[i], strlen(s_reserved_numbered[i])) == 0) {
139 return true;
140 }
141 }
142 }
143 return false;
144}
145
163RA8_INTERNAL static bool
164internal_copy_sanitised(const char* raw, char* out, size_t cap, size_t* out_len)
165{
166 bool clean = true;
167 size_t n = 0U;
168 if (raw != nullptr) {
169 size_t i = 0U;
170 while ((raw[i] != '\0') && ((n + 1U) < cap)) {
171 if (internal_is_allowed_char(raw[i])) {
172 out[n] = raw[i];
173 } else {
174 out[n] = '_';
175 clean = false;
176 }
177 ++n;
178 ++i;
179 }
180 if (raw[i] != '\0') {
181 clean = false; /* input did not fit: truncated */
182 }
183 }
184 out[n] = '\0';
185 *out_len = n;
186 return clean;
187}
188
202RA8_INTERNAL static void internal_prepend_underscore(char* out, size_t cap, size_t len)
203{
204 size_t keep = len;
205 if ((keep + 2U) > cap) {
206 keep = cap - 2U; /* drop the tail that no longer fits after the prefix */
207 }
208 memmove(out + 1, out, keep);
209 out[0] = '_';
210 out[keep + 1] = '\0';
211}
212
213bool mdl_sanitize_segment(const char* raw, char* out, size_t cap)
214{
215 if ((out == nullptr) || (cap < 2U)) {
216 return false;
217 }
218 size_t len = 0U;
219 bool clean = internal_copy_sanitised(raw, out, cap, &len);
220 if (internal_is_dot_segment(out)) {
221 (void)snprintf(out, cap, "%s", s_fallback_name);
222 return false;
223 }
224 if (internal_is_reserved_base(out)) {
225 internal_prepend_underscore(out, cap, len);
226 return false;
227 }
228 return clean;
229}
230
231bool mdl_path_contained(const char* parent, const char* candidate)
232{
233 if ((parent == nullptr) || (candidate == nullptr)) {
234 return false;
235 }
236 size_t plen = strlen(parent);
237 while ((plen > 0U) && (parent[plen - 1U] == '/')) {
238 --plen;
239 }
240 if (plen == 0U) {
241 return false;
242 }
243 if (strncmp(candidate, parent, plen) != 0) {
244 return false;
245 }
246 const char sep = candidate[plen];
247 return (sep == '/') || (sep == '\0');
248}
249
264RA8_INTERNAL static bool internal_has_separator(const char* seg)
265{
266 return strchr(seg, '/') != nullptr;
267}
268
269bool mdl_path_join(const char* parent, const char* seg, char* out, size_t cap)
270{
271 if ((out == nullptr) || (cap == 0U)) {
272 return false;
273 }
274 out[0] = '\0';
275 if ((parent == nullptr) || (seg == nullptr)) {
276 return false;
277 }
279 return false; /* empty, `.`, `..`, or a `/`-bearing/absolute segment */
280 }
281 const size_t plen = strlen(parent);
282 const size_t slen = strlen(seg);
283 const size_t need = plen + 1U + slen + 1U; /* parent + '/' + seg + NUL */
284 if (need > cap) {
285 return false; /* refuse a truncated (thus different) path */
286 }
287 memcpy(out, parent, plen);
288 out[plen] = '/';
289 memcpy(out + plen + 1U, seg, slen);
290 out[plen + 1U + slen] = '\0';
291 return true;
292}
293
295RA8_INTERNAL static const char* internal_xml_entity(char c)
296{
297 switch (c) {
298 case '&':
299 return "&amp;";
300 case '<':
301 return "&lt;";
302 case '>':
303 return "&gt;";
304 case '"':
305 return "&quot;";
306 case '\'':
307 return "&apos;";
308 default:
309 return nullptr;
310 }
311}
312
313bool mdl_xml_escape(const char* src, char* out, size_t cap)
314{
315 if ((out == nullptr) || (cap == 0U)) {
316 return false;
317 }
318 out[0] = '\0';
319 if (src == nullptr) {
320 return false;
321 }
322 size_t n = 0U;
323 for (size_t i = 0U; src[i] != '\0'; ++i) {
324 const char* ent = internal_xml_entity(src[i]);
325 const size_t need = (ent != nullptr) ? strlen(ent) : 1U;
326 if ((n + need + 1U) > cap) {
327 out[0] = '\0';
328 return false;
329 }
330 if (ent != nullptr) {
331 memcpy(out + n, ent, need);
332 } else {
333 out[n] = src[i];
334 }
335 n += need;
336 }
337 out[n] = '\0';
338 return true;
339}
static bool internal_is_dot_segment(const char *name)
True if name is empty, ".", or ".." (no useful segment).
bool mdl_xml_escape(const char *src, char *out, size_t cap)
XML-escape src into out, failing rather than truncating.
static bool internal_is_allowed_char(char c)
True if c may appear verbatim in a sanitised segment.
static const char *const s_reserved_exact[]
Exactly-reserved Windows device base names (case-folded).
bool mdl_path_join(const char *parent, const char *seg, char *out, size_t cap)
Join one safe child segment under a parent directory path.
static const char *const s_fallback_name
Fallback name substituted when a segment sanitises to nothing.
bool mdl_path_contained(const char *parent, const char *candidate)
True when candidate is lexically contained under parent.
static bool internal_has_separator(const char *seg)
True if seg embeds a path separator (would span directories).
static bool internal_is_reserved_base(const char *name)
True if name's base is a Windows reserved device name.
static void internal_prepend_underscore(char *out, size_t cap, size_t len)
Prepend _ to out in place, staying within cap.
mdl_sanitize_size_t
Small fixed sizes used while sanitising a name.
@ k_reserved_digit_at
Index of the digit in COMx / LPTx.
@ k_reserved_base_max
Buffer for a Windows reserved-name base.
@ k_reserved_len
Length of a COMx / LPTx reserved name.
static bool internal_copy_sanitised(const char *raw, char *out, size_t cap, size_t *out_len)
Copy raw into out replacing unsafe bytes; report bad/truncated.
static void internal_base_of(const char *name, char *base, size_t cap)
Case-folded base name (up to the first ".") of name into base.
static char internal_lower_ascii(char c)
ASCII lower-case of one character (locale-independent).
bool mdl_sanitize_segment(const char *raw, char *out, size_t cap)
Rewrite an untrusted path segment into a safe single filename.
static const char *const s_reserved_numbered[]
Reserved Windows device prefixes taking a 1-9 suffix.
static const char * internal_xml_entity(char c)
XML entity for a metacharacter, or NULL when c needs no escape.
Neutralise untrusted names before they reach a filesystem or XML sink.
Annotation-attribute framework macros for ra8-firmware.
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
int strncmp(const char *s1, const char *s2, size_t n)
Compare two strings up to a specified length.
int strcmp(const char *s1, const char *s2)
Compare two null-terminated strings.
void * memmove(void *dst, const void *src, size_t n)
Copy memory area between potentially overlapping regions.
size_t strlen(const char *s)
Calculate string length.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
char * strchr(const char *s, int c)
Locate first occurrence of character in string.