Skip to content

Instantly share code, notes, and snippets.

@starwing
Last active July 7, 2024 13:05
Show Gist options
  • Save starwing/2761647 to your computer and use it in GitHub Desktop.
Save starwing/2761647 to your computer and use it in GitHub Desktop.
a path normalize algorithm
#include <stdio.h>
#include <string.h>
#define COMP_MAX 50
#define ispathsep(ch) ((ch) == '/' || (ch) == '\\')
#define iseos(ch) ((ch) == '\0')
#define ispathend(ch) (ispathsep(ch) || iseos(ch))
char *normpath(char *out, const char *in) {
char *pos[COMP_MAX], **top = pos, *head = out;
int isabs = ispathsep(*in);
if (isabs) *out++ = '/';
*top++ = out;
while (!iseos(*in)) {
while (ispathsep(*in)) ++in;
if (iseos(*in))
break;
if (memcmp(in, ".", 1) == 0 && ispathend(in[1])) {
++in;
continue;
}
if (memcmp(in, "..", 2) == 0 && ispathend(in[2])) {
in += 2;
if (top != pos + 1)
out = *--top;
else if (isabs)
out = top[-1];
else {
strcpy(out, "../");
out += 3;
}
continue;
}
if (top - pos >= COMP_MAX)
return NULL; /* path to complicate */
*top++ = out;
while (!ispathend(*in))
*out++ = *in++;
if (ispathsep(*in))
*out++ = '/';
}
*out = '\0';
if (*head == '\0')
strcpy(head, "./");
return head;
}
int main(void) {
char a[][2][260] = {
{ "", "./" },
{ "/..", "/" },
{ "/../", "/" },
{ ".", "./" },
{ "./", "./" },
{ "..", "../" },
{ "../", "../" },
{ "../abc/def", "../abc/def" },
{ "../abc/def/..", "../abc/" },
{ "../abc/././././def/..", "../abc/" },
{ "////../abc/def", "/abc/def" },
{ "/../def", "/def" },
{ "../def", "../def" },
{ "/abc////../def", "/def" },
{ "abc/../def/ghi", "def/ghi" },
{ "/abc/def/../ghi", "/abc/ghi" },
{ "/abc/..abc////../def", "/abc/def" },
{ "/abc/..abc/../def", "/abc/def" },
{ "abc/../def", "def" },
{ "abc/../../def", "../def" },
{ "././", "./" },
{ "abc/..", "./" },
{ "abc/../", "./" },
{ "abc/../..", "../" },
{ "abc/../../", "../" },
{ "a/..", "./" },
{ "a/../", "./" },
{ "a/../..", "../" },
{ "a/../../", "../" },
{ "../../../a", "../../../a" },
{ "../a../../a", "../a" },
{ "cccc/abc////..////.//../", "./" },
{ "aaaa/cccc/abc////..////.//../", "aaaa/" },
{ "..//////.///..////..////.//////abc////.////..////def//abc/..", "../../../def/" },
{ "////////////..//////.///..////..////.//////abc////.////..////def//abc/..", "/def/" },
};
int i;
int pass = 0;
for (i = 0; i < sizeof(a) / sizeof(a[0]); ++i) {
char buff[260];
printf("[%d] in> %s\n", i+1, a[i][0]);
normpath(buff, a[i][0]);
if (strcmp(buff, a[i][1])) {
printf("--> %s\n", buff);
printf("expected: %s\n", a[i][1]);
}
else ++pass;
}
printf("-----------\nPASS: %d/%d\n", pass, sizeof(a) / sizeof(a[0]));
return 0;
}
/* cc: flags+='-O2 -Wall -pedantic -ansi' */
#include <stdio.h>
#include <assert.h>
#include <stddef.h>
#define NORMPATH_METHOD 1
#define ALT_SEP '/'
int debug = 0;
static size_t trimpath(char *s, int *isabs, int pathsep, int altsep) {
char *wp = s, *rp = s;
for (; *rp != '\0'; ++rp) {
if (*rp == altsep || *rp == pathsep) {
while (rp[1] == altsep || rp[1] == pathsep) ++rp;
if (rp[1] == '.'
&& (rp[2] == altsep || rp[2] == pathsep || rp[2] == '\0'))
++rp;
else *wp++ = pathsep;
}
else *wp++ = *rp;
}
if (s < wp && wp[-1] == '.' && wp[-2] == '.' &&
(wp - 2 == s || wp[-3] == pathsep))
*wp++ = pathsep;
*wp = '\0';
if (isabs != NULL) *isabs = *s == pathsep;
return wp - s;
}
#if NORMPATH_METHOD == 1
static size_t normpath_inplace(char *s, int pathsep) {
int isabs;
char *wp = s, *rp = s;
trimpath(s, &isabs, pathsep, ALT_SEP);
for (; *rp != '\0'; ++rp) {
/*while (*rp != '\0' && *rp != pathsep) *wp++ = *rp++;*/
/*if (*rp == '\0') break;*/
if (rp[0] == pathsep && rp[1] == '.' && rp[2] == '.'
&& (rp[3] == pathsep || rp[3] == '\0')) {
char *lastwp = wp;
while (s < lastwp && *--lastwp != pathsep)
;
if (lastwp != wp && (wp[-1] != '.' || wp[-2] != '.' ||
(s < wp - 3 && wp[-3] != pathsep))) {
wp = lastwp;
rp += 2;
continue;
}
else if (lastwp == s && isabs) {
rp += 2;
continue;
}
}
if (rp[0] != pathsep || wp != s || isabs)
*wp++ = *rp;
}
if (wp == s)
*wp++ = isabs ? pathsep : '.';
if (wp == s + 1 && s[0] == '.')
*wp++ = pathsep;
*wp = '\0';
return wp - s;
}
#endif
#if NORMPATH_METHOD == 2
static size_t normpath_inplace(char *s, int pathsep) {
int isabs;
char *lastwp = s, *wp = s, *rp = s;
trimpath(s, &isabs, pathsep, ALT_SEP);
for (; *rp != '\0'; ++rp) {
if (rp[0] == pathsep) {
if (rp[1] == '.' && rp[2] == '.'
&& (rp[3] == pathsep || rp[3] == '\0')) {
if (lastwp != wp && (wp[-1] != '.' || wp[-2] != '.' ||
(s < wp - 3 && wp[-3] != pathsep))) {
wp = lastwp;
while (s < lastwp && *--lastwp != pathsep)
;
rp += 2;
continue;
}
else if (isabs && lastwp == s) {
rp += 2;
continue;
}
}
if (wp != s || isabs) {
lastwp = wp;
*wp++ = *rp;
}
}
else *wp++ = *rp;
}
if (wp == s)
*wp++ = isabs ? pathsep : '.';
if (wp == s + 1 && s[0] == '.')
*wp++ = pathsep;
*wp = '\0';
return wp - s;
}
#endif
#if NORMPATH_METHOD == 3
static size_t normpath_inplace(char *s, int pathsep) {
int isabs;
char *lastp = s, *p = s;
trimpath(s, &isabs, pathsep, ALT_SEP);
while (*p != '\0') {
if (*p++ != pathsep) continue;
if (p[0] == '.' && p[1] == '.' &&
(p[2] == pathsep || p[2] == '\0')) {
if (*lastp != pathsep && (lastp[0] != '.' || lastp[1] != '.' ||
lastp[2] != pathsep)) {
char *pi = lastp;
while (pi < p + 2) *pi++ = pathsep;
while (s < lastp && *lastp == pathsep) --lastp;
while (s < lastp && lastp[-1] != pathsep) --lastp;
p += 2;
}
else if (lastp == s && isabs) {
p[0] = p[1] = pathsep;
p += 2;
}
}
else lastp = p;
}
p = s + trimpath(s, pathsep, ALT_SEP);
if (!isabs && *s == pathsep) {
char *pi = s;
while ((pi[0] = pi[1])) ++pi;
--p;
}
if (p == s)
*p++ = isabs ? pathsep : '.';
if (p == s + 1 && s[0] == '.')
*p++ = pathsep;
*p = '\0';
return p - s;
}
#endif
#include <stdio.h>
#include <string.h>
int main(void) {
char a[][2][260] = {
{ "", "./" },
{ "/..", "/" },
{ "/../", "/" },
{ ".", "./" },
{ "./", "./" },
{ "..", "../" },
{ "../", "../" },
{ "../abc/def", "../abc/def" },
{ "../abc/def/..", "../abc/" },
{ "../abc/././././def/..", "../abc/" },
{ "////../abc/def", "/abc/def" },
{ "/../def", "/def" },
{ "../def", "../def" },
{ "/abc////../def", "/def" },
{ "abc/../def/ghi", "def/ghi" },
{ "/abc/def/../ghi", "/abc/ghi" },
{ "/abc/..abc////../def", "/abc/def" },
{ "/abc/..abc/../def", "/abc/def" },
{ "abc/../def", "def" },
{ "abc/../../def", "../def" },
{ "././", "./" },
{ "abc/..", "./" },
{ "abc/../", "./" },
{ "abc/../..", "../" },
{ "abc/../../", "../" },
{ "a/..", "./" },
{ "a/../", "./" },
{ "a/../..", "../" },
{ "a/../../", "../" },
{ "../../../a", "../../../a" },
{ "../a../../a", "../a" },
{ "cccc/abc////..////.//../", "./" },
{ "aaaa/cccc/abc////..////.//../", "aaaa/" },
{ "..//////.///..////..////.//////abc////.////..////def//abc/..", "../../../def/" },
{ "////////////..//////.///..////..////.//////abc////.////..////def//abc/..", "/def/" },
};
int i;
int pass = 0;
for (i = 0; i < sizeof(a) / sizeof(a[0]); ++i) {
char buff[260];
strcpy(buff, a[i][0]);
normpath_inplace(buff, '\\');
trimpath(a[i][1], NULL, '\\', '/');
if (strcmp(buff, a[i][1])) {
printf("in> %s\n", a[i][0]);
printf("--> %s\n", buff);
printf("expected: %s\n", a[i][1]);
printf("trace:\n");
debug = 1;
strcpy(buff, a[i][0]);
normpath_inplace(buff, '\\');
debug = 0;
printf("\n");
}
else ++pass;
}
printf("-----------\nPASS: %d/%d\n", pass, sizeof(a) / sizeof(a[0]));
return 0;
}
/* cc: flags+='-Wall -pedantic -ansi' */
@xtrm-en
Copy link

xtrm-en commented Jul 7, 2024

Thank you kind person 🤝

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment