Skip to content

Instantly share code, notes, and snippets.

@meepen
Last active March 19, 2017 16:56
Show Gist options
  • Save meepen/6e1c19a193e5d57214ab0324f04f3075 to your computer and use it in GitHub Desktop.
Save meepen/6e1c19a193e5d57214ab0324f04f3075 to your computer and use it in GitHub Desktop.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <direct.h>
static bool IsPathAbsolute(const char *path)
{
size_t len = strlen(path);
if (len > 2)
{
if (path[1] == ':')
return true; // drive path
if (path[0] == '\\' && path[1] == '\\')
return true; // server path
}
return false;
}
static char ToLower(char t)
{
if (t >= 'A' && t <= 'Z')
return t - 'A' + 'a';
return t;
}
static bool CompareStringIgnoreCase(const char *str1, const char *str2, size_t len)
{
for (size_t i = 0; i < len; i++)
if (ToLower(*str1++) != ToLower(*str2++))
return false;
return true;
}
static char *RootPath(const char *fullpath, char **output)
{
size_t len = strlen(fullpath);
if (len >= 2 && fullpath[1] == ':')
{
*output = (char *)calloc(1, 3);
memcpy(*output, fullpath, 2);
return *output;
}
if (len >= 3 && fullpath[0] == '\\' && fullpath[1] == '\\')
{
const char *next = strchr(&fullpath[2], '\\');
if (!next)
return NULL;
if (next[-1] == '?' && next - fullpath == 3)
{
const char *next2 = strchr(&next[1], '\\');
if (!next2)
return NULL;
// is this ignore case?
if (next2 - next == 4 && CompareStringIgnoreCase(next, "\\UNC", 4))
{
next2 = strchr(&next2[1], '\\');
if (!next2)
return NULL;
next2 = strchr(&next2[1], '\\');
if (!next2)
return NULL;
*output = (char *)calloc(1, next2 - fullpath + 1);
memcpy(*output, fullpath, next2 - fullpath);
return *output;
}
else if (next2 - next == 3 && next2[-1] == ':')
{
// done! \\?\drive:
*output = (char *)calloc(1, next2 - fullpath + 1);
memcpy(*output, fullpath, next2 - fullpath);
return *output;
}
else
{
next2 = strchr(&next2[1], '\\');
if (!next2)
return NULL;
*output = (char *)calloc(1, next2 - fullpath + 1);
memcpy(*output, fullpath, next2 - fullpath);
return *output;
}
}
else if (next[-1] == '.' && next - fullpath == 3)
{
const char *next2 = strchr(&next[1], '\\');
if (!next2)
return NULL;
*output = (char *)calloc(1, next2 - fullpath + 1);
memcpy(*output, fullpath, next2 - fullpath);
return *output;
}
else
{
const char *next2 = strchr(&next[1], '\\');
if (!next2)
return NULL;
*output = (char *)calloc(1, next2 - fullpath + 1);
memcpy(*output, fullpath, next2 - fullpath);
return *output;
}
}
return NULL;
}
const char *CanonicalPath(const char *current_work_dir, const char *path, const char **outputnow)
{
if (path && IsPathAbsolute(path))
{
char *rootpath = RootPath(path, &rootpath);
if (rootpath)
{
free(rootpath);
return CanonicalPath(path, NULL, outputnow);
}
}
if (!IsPathAbsolute(current_work_dir))
{
_set_errno(EINVAL);
return NULL;
}
char *rootpath = RootPath(current_work_dir, &rootpath);
if (!rootpath)
{
_set_errno(EINVAL);
return NULL;
}
size_t rootlen = strlen(rootpath);
if (path && (*path == '\\' || *path == '/'))
{
size_t size = rootlen + 1 + strlen(path);
char *tmp = (char *)malloc(size);
sprintf(tmp, "%s%s", rootpath, path);
const char *ret = CanonicalPath(tmp, NULL, outputnow);
free(tmp);
free(rootpath);
return ret;
}
free(rootpath);
size_t size = strlen(current_work_dir);
char *output = (char *)calloc(1, size + 1);
strcpy(output, current_work_dir);
for (size_t i = rootlen; i < size; i++)
if (output[i] == '/')
output[i] = '\\';
// points to a \
char *updir = NULL;
size_t updir_index = 0;
for (size_t i = rootlen; i <= size; i++)
{
if (output[i] == '\r' || output[i] == '\n')
{
output[i] = 0;
size = i;
i -= 2;
continue;
}
if (size > i + 1 && output[i] == '\\' && output[i + 1] == '\\')
{
memmove(&output[i], &output[i + 1], size - i);
size -= 1;
i--;
continue;
}
if (size > i + 1 && output[i] == '\\' && output[i + 1] == '.' && (output[i + 2] == '\\' || output[i + 2] == 0))
{
memmove(&output[i], &output[i + 2], size - i);
size -= 2;
i--;
continue;
}
if (updir && size > i + 2 && output[i] == '\\' && output[i + 1] == '.' && output[i + 2] == '.' && (output[i + 3] == '\\' || output[i + 3] == 0))
{
memmove(updir, &output[i + 3], size - i);
size -= 3;
i = updir_index - 1;
continue;
}
// folders and files ending in spaces or any of these other chars ignore them
if (i > 0 && (output[i - 1] == ' ' || output[i - 1] == '.') && (output[i] == '\\' || output[i] == 0))
{
memmove(&output[i - 1], &output[i], size - i + 1);
size -= 1;
i -= 2;
continue;
}
if (output[i] == '\\')
{
updir = &output[i];
updir_index = i;
}
}
if (size > 0 && output[size - 1] == '\\')
{
output[size - 1] = 0;
size--;
}
if (size > FILENAME_MAX)
size = FILENAME_MAX - 1;
output = (char *)realloc(output, size + 1);
output[size] = 0;
const char *coutput = output;
if (path)
{
// strlen(path) + strlen(output) + 1 (null) + 1 (seperator)
size = strlen(path) + 1 + 1 + size;
output = (char *)realloc(output, size);
strcat(output, "\\");
strcat(output, path);
CanonicalPath(output, NULL, &coutput);
free((void *)output);
}
*outputnow = output;
return coutput;
}
int main(int argc, char *argv[])
{
if (argc == 2)
{
char cwd[FILENAME_MAX];
_getcwd(cwd, sizeof(cwd) - 1);
const char *output = CanonicalPath(cwd, argv[1], &output);
printf("Path normalization %s '%s'\n", output ? "succeeded" : "failed", output ? output : "");
if (output)
free((void *)output);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment