commit b566b9afd52b536d6d333e6df75788d766076064
parent 1b23b1eaae216f1f8c114582c9ad362df970ccf8
Author: Evgeny Grin (Karlson2k) <k2k@drgrin.dev>
Date: Tue, 18 Nov 2025 19:34:33 +0100
Further improved readability of URI normalisation function
Diffstat:
| M | src/mhd2/mhd_str.c | | | 175 | +++++++++++++++++++++++++++++++++++++++++-------------------------------------- |
1 file changed, 90 insertions(+), 85 deletions(-)
diff --git a/src/mhd2/mhd_str.c b/src/mhd2/mhd_str.c
@@ -2400,6 +2400,7 @@ mhd_str_dec_norm_uri_path (size_t str_len,
char c2;
if (str_len == r + 1u) /* overflow-safe as 'str_len > r' */
{
+ /* The complete string is "." */
++r; /* Skip "." */
break; /* At the edge, stop */
}
@@ -2407,6 +2408,7 @@ mhd_str_dec_norm_uri_path (size_t str_len,
c2 = str[r + 1u];
if ('/' == c2)
{
+ /* Found "./" at the start of the string */
r += 2u; /* Skip "./" */
continue;
}
@@ -2421,6 +2423,7 @@ mhd_str_dec_norm_uri_path (size_t str_len,
char c3;
if (str_len == r + 2u) /* overflow-safe as 'str_len > r + 1 ' */
{
+ /* The complete string is ".." */
r += 2u; /* Skip ".." */
break; /* At the edge, stop */
}
@@ -2428,6 +2431,7 @@ mhd_str_dec_norm_uri_path (size_t str_len,
c3 = str[r + 2u];
if ('/' == c3)
{
+ /* Found "../" at the start of the string */
r += 3u; /* Skip "../" */
continue;
}
@@ -2444,6 +2448,7 @@ mhd_str_dec_norm_uri_path (size_t str_len,
}
break;
}
+
mhd_ASSUME (w <= r);
/* Found first segment which is not "../" and is not "./" OR the end of the string */
for ((void) r; str_len > r && '/' != str[r]; ++r)
@@ -2460,114 +2465,114 @@ mhd_str_dec_norm_uri_path (size_t str_len,
mhd_ASSUME (w <= r);
str[w++] = c;
}
+
/* Found first '/' which is not skipped OR the end of the string */
- if (str_len > r)
+ while (str_len > r)
{
- /* Found first segment started with '/' */
- mhd_ASSUME ('/' == str[r]);
- while (str_len > r)
+ char slash_chr = str[r];
+ size_t seg_start = w;
+ mhd_ASSUME ('/' == slash_chr);
+ str[w++] = slash_chr;
+ ++r;
+ if (str_len > r)
{
- char slash_chr = str[r];
- size_t seg_start = w;
- mhd_ASSUME ('/' == slash_chr);
- str[w++] = slash_chr;
- ++r;
- if (str_len > r)
+ char c;
+ mhd_ASSUME (w <= r);
+ c = str[r];
+ if ('/' == c)
+ continue;
+ if (('%' == c) &&
+ pct_decode_no_slash (str_len,
+ str,
+ r,
+ &c))
+ r += 2u;
+ if ('.' == c)
{
- char c;
+ char c2;
+ if (str_len == r + 1u) /* overflow-safe as 'str_len > r' */
+ {
+ /* Found "/." at the end of the string */
+ ++r; /* Skip ".", leave bare '/' */
+ break; /* At the edge, stop */
+ }
mhd_ASSUME (w <= r);
- c = str[r];
- if ('/' == c)
- continue;
- if (('%' == c) &&
+ c2 = str[r + 1u];
+ if ('/' == c2)
+ {
+ /* Found "/./" */
+ w = seg_start; /* Rewind output to the '/' at the start of the segment */
+ ++r; /* Skip "." */
+ continue; /* Go to the next "/", which will be written again */
+ }
+ if (('%' == c2) &&
pct_decode_no_slash (str_len,
str,
- r,
- &c))
+ r + 1u,
+ &c2))
r += 2u;
- if ('.' == c)
+ if ('.' == c2)
{
- char c2;
- if (str_len == r + 1u) /* overflow-safe as 'str_len > r' */
+ char c3;
+ if (str_len == r + 2u) /* overflow-safe as 'str_len > r + 1 ' */
{
- ++r; /* Skip ".", leave bare '/' */
- break; /* At the edge, stop */
+ /* Found "/.." at the end of the string */
+ w = seg_start;
+ if (0 < w)
+ do
+ { /* Rewind output to the start of prev segment */
+ --w;
+ } while (0 < w && '/' != str[w]);
+ mhd_ASSUME (w < r);
+ str[w++] = '/'; /* Replace prev segment with '/' */
+ r += 2u; /* Skip ".." */
+ break; /* At the edge, stop */
}
mhd_ASSUME (w <= r);
- c2 = str[r + 1u];
- if ('/' == c2)
+ c3 = str[r + 2u];
+ if ('/' == c3)
{
+ /* Found "/../" */
w = seg_start;
- ++r; /* Skip "." */
- continue; /* Go to the next "/", which will be written again */
- }
- if (('%' == c2) &&
- pct_decode_no_slash (str_len,
- str,
- r + 1u,
- &c2))
- r += 2u;
- if ('.' == c2)
- {
- char c3;
- if (str_len == r + 2u) /* overflow-safe as 'str_len > r + 1 ' */
- {
- w = seg_start;
- if (0 < w)
- do
- { /* Rewind output to the start of prev segment */
- --w;
- } while (0 < w && '/' != str[w]);
- mhd_ASSUME (w < r);
- str[w++] = '/'; /* Replace prev segment with '/' */
- r += 2u; /* Skip ".." */
- break; /* At the edge, stop */
- }
- mhd_ASSUME (w <= r);
- c3 = str[r + 2u];
- if ('/' == c3)
- {
- w = seg_start;
- if (0 < w)
- do
- { /* Rewind output to the start of prev segment */
- --w;
- } while (0 < w && '/' != str[w]);
- r += 2u; /* Skip ".."; put next '/' to the start of prev segment */
- continue;
- }
- /* Do not write 'c3' as it has not been percent-decoded */
+ if (0 < w)
+ do
+ { /* Rewind output to the start of prev segment */
+ --w;
+ } while (0 < w && '/' != str[w]);
+ r += 2u; /* Skip ".."; put next '/' to the start of prev segment */
+ continue;
}
- str[w++] = c;
- str[w++] = c2;
- r += 2u;
- }
- else
- {
- str[w++] = c;
- r += 1u;
+ /* Do not write 'c3' as it has not been percent-decoded */
}
- mhd_assert (seg_start < w);
+ str[w++] = c;
+ str[w++] = c2;
+ r += 2u;
}
- for ((void) r; str_len > r && '/' != str[r]; ++r)
+ else
{
- /* Process the end of the segment */
- char c;
- mhd_ASSUME (w <= r);
- c = str[r];
- if (('%' == c) &&
- pct_decode_no_slash (str_len,
- str,
- r,
- &c))
- r += 2u;
- mhd_ASSUME (w <= r);
str[w++] = c;
+ r += 1u;
}
+ mhd_assert (seg_start < w);
+ }
+ for ((void) r; str_len > r && '/' != str[r]; ++r)
+ {
+ /* Process the end of the segment */
+ char c;
+ mhd_ASSUME (w <= r);
+ c = str[r];
+ if (('%' == c) &&
+ pct_decode_no_slash (str_len,
+ str,
+ r,
+ &c))
+ r += 2u;
+ mhd_ASSUME (w <= r);
+ str[w++] = c;
}
mhd_assert (0u != w);
- mhd_assert ((r == str_len) || ('/' == str[w - 1u]));
}
+ mhd_assert (r == str_len);
if (str_len > w)
str[w] = '\0';