From 41a1e473accda76726d8e2768aa750d1fe5a112c Mon Sep 17 00:00:00 2001 From: ketmar Date: Wed, 2 Oct 2013 11:40:08 +0300 Subject: [PATCH] normalize_path() rewritten --- src/pathsys.c | 177 +++++++++++++++++++++++++++++++++++----------------------- src/pathsys.h | 3 +- 2 files changed, 110 insertions(+), 70 deletions(-) diff --git a/src/pathsys.c b/src/pathsys.c index c39386a..7c440d5 100644 --- a/src/pathsys.c +++ b/src/pathsys.c @@ -166,79 +166,118 @@ void path_parent (PATHNAME *f) { * but at least reduces the number of categories that represent the same * entry. On error, or if the supplied buffer is too small, NULL is returned. */ -char *normalize_path (const char *path, char *buffer, size_t bufferSize) { - // init cwd - static char _cwd[PATH_MAX]; - static char *cwd = 0; - static size_t cwdLen = 0; - int pathLen = path?strlen(path):0; - int resultLen = 0; - int resolveDotDot = !0; - // init cwd - if (!cwd) { - cwd = getcwd(_cwd, PATH_MAX); - if (!cwd) return 0; - cwdLen = strlen(cwd); - } - // check length - if (cwdLen+pathLen+2 > bufferSize) return 0; - // construct result - if (pathLen > 0 && path[0] == PATH_DELIM) { - // absolute path: ignore cwd - buffer[0] = PATH_DELIM; - buffer[1] = '\0'; - resultLen = 1; - ++path; - --pathLen; +char *normalize_path (const char *path, char *buffer, size_t buf_size, const char *pwd) { +#if PATH_DELIM == '\\' + /* stupid windoze; convert all idiotic backslashes to normal slashes */ + static char w2upath[PATH_MAX]; +#endif + char *res = buffer; + static char cwd_buf[PATH_MAX]; + char *cwd = NULL; + static size_t cwd_len = 0; + int res_len = 0; + int ends_with_slash = 0; + /* init cwd */ + if (pwd == NULL) { + if ((cwd = getcwd(cwd_buf, PATH_MAX)) == NULL) return NULL; } else { - // relative path: copy cwd into result - memcpy(buffer, cwd, cwdLen+1); - resultLen = cwdLen; - } - // append path componentwise to the result, skipping "." and empty - // components, and chopping off a component per ".." - while (pathLen > 0) { - // find component - char *separator = strchr(path, PATH_DELIM); - const char *component = path; - int componentLen = 0; - if (separator) { - componentLen = separator-path; - pathLen -= componentLen+1; - path = separator+1; - } else { - componentLen = pathLen; - path += componentLen; - pathLen = 0; + /*FIXME: possible overflow*/ + snprintf(cwd_buf, sizeof(cwd_buf), "%s", pwd); + cwd = cwd_buf; + } + cwd_len = strlen(cwd); + if (path == NULL) path = ""; + /* start reconstructing path */ +#if PATH_DELIM == '\\' + snprintf(w2upath, sizeof(w2upath), "%s", path); + for (char *p = w2upath; *p; ++p) if (*p == '\\') *p = '/'; + path = w2upath; + /* check if this path is absolute */ + /* windoze: check if we have disk letter here */ + if (path[0] && path[1] == ':') { + /* copy disk letter */ + if (buf_size < 3) return NULL; + buf_size -= 2; + *buffer++ = (cwd[0] && cwd[1] == ':' ? cwd[0] : path[0]); + *buffer++ = ':'; + path += 2; + } + /* convert cwd */ + if (cwd[0] && cwd[1] == ':') { cwd += 2; cwd_len -= 2; } + for (char *p = cwd; *p; ++p) if (*p == '\\') *p = '/'; +#endif + /* check if this path is absolute */ + if (path[0] != '/') { + /* prepend pwd */ + if (cwd_len > 0) { + if (buf_size < cwd_len+1) return NULL; + memmove(buffer, cwd, cwd_len); + res_len += cwd_len; } - // handle found component - if (componentLen > 0) { - if (componentLen == 1 && component[0] == '.') { - // component is ".": skip - } else if (resolveDotDot && componentLen == 2 && component[0] == '.' && component[1] == '.') { - // component is "..": eat the last component of the result - char *lastSeparator = strrchr(buffer, PATH_DELIM); - if (lastSeparator) { - resultLen = lastSeparator-buffer; - if (resultLen == 0) { - // always leave at least the root - buffer[0] = PATH_DELIM; - resultLen = 1; + } else { + /* put slash in output; excess slashes will be eaten by the main loop */ + if (buf_size < 2) return NULL; + buffer[res_len++] = '/'; + } + ends_with_slash = (path[0] && path[strlen(path)-1] == '/'); + /* main loop */ + while (*path) { + const char *e; + /* skip leading slashes */ + while (*path && path[0] == '/') ++path; + if (!path[0]) break; /* no more */ + if ((e = strchr(path, '/')) == NULL) e = path+strlen(path); + if (e-path == 1 && path[0] == '.') { + /* this is unnecessary dot, skip it */ + path = e; + continue; + } + if (e-path == 2 && path[0] == '.' && path[1] == '.') { + /* dotdot; go one dir up if we can */ + /* we can't go up if we are at root or if previous dir is "." or ".." */ + if (res_len > 0 && !(res_len == 1 && buffer[0] == '/')) { + char *pd = buffer+res_len; + int sz = res_len; + int do_remove = 0; + if (pd[-1] == '/') { --pd; --sz; } + if (sz > 0) { + if (pd[-1] != '.') { + do_remove = 1; + } else if (sz > 1) { + /* last char is '.' and we have more chars */ + if (pd[-2] != '.') { + do_remove = 1; + } else if (sz > 2) { + /* last two chars is ".." and we have more chars */ + do_remove = (pd[-3] != '/'); + } } - buffer[resultLen] = '\0'; - } // else: not good - } else { - // normal component: append - if (resultLen < 1 || buffer[resultLen-1] != PATH_DELIM) buffer[resultLen++] = PATH_DELIM; - memcpy(buffer+resultLen, component, componentLen); - resultLen += componentLen; - buffer[resultLen] = '\0'; - // After we found the first real path component, we don't - // resolve ".." anymore, as it could be a (sym)link, which - // could break the algorithm. - resolveDotDot = 0; + } + if (do_remove) { + if (res_len > 0 && buffer[res_len-1] == '/') --res_len; /* remove trailing slash if any */ + while (res_len > 0 && buffer[res_len-1] != '/') --res_len; /* remove last dir */ + path = e; + continue; + } } } + /* append this dir */ + if (res_len > 0 && buffer[res_len-1] != '/') { + if (res_len+2 > buf_size) return NULL; + buffer[res_len++] = '/'; + } + if (res_len+(e-path)+1 > buf_size) return NULL; + memmove(buffer+res_len, path, (e-path)); + res_len += e-path; + path = e; + } + /* add trailing slash if we need it */ + if (ends_with_slash && res_len > 0 && buffer[res_len-1] != '/') { + if (res_len+2 > buf_size) return NULL; + buffer[res_len++] = '/'; } - return buffer; + /* add terminator */ + if (res_len+1 > buf_size) return NULL; + buffer[res_len] = 0; + return res; } diff --git a/src/pathsys.h b/src/pathsys.h index 0ed28da..ce46362 100644 --- a/src/pathsys.h +++ b/src/pathsys.h @@ -48,7 +48,8 @@ extern void path_build (char *file, const PATHNAME *f); extern void path_parse (const char *file, PATHNAME *f); extern void path_parent (PATHNAME *f); -extern char *normalize_path (const char *path, char *buffer, size_t bufferSize); +/* if 'pwd'is NULL, use getcwd() */ +extern char *normalize_path (const char *path, char *buffer, size_t bufferSize, const char *pwd); #endif -- 2.11.4.GIT