C 替代';realpath';解决问题/&引用;及"/&引用;在一条小路上

C 替代';realpath';解决问题/&引用;及"/&引用;在一条小路上,c,canonicalization,realpath,C,Canonicalization,Realpath,realpath执行我需要的操作,但仅当路径中的文件实际存在时才起作用 我需要一个函数,它可以从字符串(例如。/some//directory/a/b/c//d到some/directory/a/b/d)返回一个规范化路径,而不管目录/文件是否实际存在 本质上相当于windows上的路径规范化 这样的函数已经存在了吗?听起来像是在*nix(例如,Linux)上 问:你的编译器有吗 否则,如果你在C++中编程,你可能会考虑Booo: Python源代码有一个针对多个平台的实现。不幸的是,POSIX

realpath
执行我需要的操作,但仅当路径中的文件实际存在时才起作用

我需要一个函数,它可以从字符串(例如
。/some//directory/a/b/c//d
some/directory/a/b/d
)返回一个规范化路径,而不管目录/文件是否实际存在

本质上相当于windows上的路径规范化


这样的函数已经存在了吗?

听起来像是在*nix(例如,Linux)上

问:你的编译器有吗

否则,如果你在C++中编程,你可能会考虑Booo:


Python源代码有一个针对多个平台的实现。不幸的是,POSIX one(在中,对于Python 3,第318行或第308行)是Python中的,但是通用逻辑可以很容易地用C重新实现(函数非常紧凑)。经过多年使用测试

Python解释器和标准库源代码中还有其他平台normpath实现,所以可移植解决方案可以是这些的组合

可能其他用C编写的系统/库也有相同的实现,因为normpath函数在安全性方面至关重要


(使用Python代码的主要优点是能够用C语言并行测试函数,即使是随机输入,而且这种测试对于确保函数的安全性非常重要)

我假设您的主机是windows或unix(两者都支持
/
分别表示父目录、当前目录和目录分隔符)。并且您的库提供对posix指定函数
getcwd()
的访问,该函数检索程序的当前工作目录(即,如果在文件名中没有路径规范的情况下打开输出文件,将写入其中的完整路径)

首先调用
getcwd()
以检索工作目录。如果其中的最后一个字符是
'/'
,请将该工作目录预先添加到输入字符串中,而不进行修改。否则,请将其和字符
'/'
预先添加到字符串中

然后只处理字符串。找到字符串的第一个实例。//“,并删除路径的前一部分和
。//“
。例如,如果字符串是
”/a/b/c/./foo“
,则结果将是
”/a/b/foo“
。重复该操作,直到字符串中没有
。//“
的实例

唯一需要注意的是决定如何处理字符串,如
“/../”
(从技术上讲,这是一个不存在的路径)。要么将其保留为
“/”
(这样您总是可以得到一个可行的路径),要么报告错误

完成后,查找
“/。/”
的实例,并将其替换为
“/”
。这将把类似
“/a/b/c/”
的字符串转换为
“/a/b/c/”
,但将只保留类似
“/a/b/c./”
(在
“/a/b”
中指定名为
“c.
的目录)的字符串

以上所有操作都只是处理字符串。除了使用
getcwd()
,没有任何依赖于主机环境的操作。因此,无论路径是否实际存在,过程都是相同的

一些建议可能包括使其更好地与windows配合使用,例如将
'/'
'\'
视为等效项,以及处理驱动器说明符,如
“A:”

如果您不想调用
getcwd()
(例如,如果您的程序不依赖于实际有一个工作目录,或者如果它有一个不存在的目录),那么您需要指定一个启动条件。例如,像
“./x/y/z”这样的字符串将在哪里结束


我的建议允许
字符成为文件名(或目录名)的一部分您可能需要也可能不需要。根据需要进行调整。

根据您的问题陈述,以下内容完全符合您的要求。大部分代码来自注释链接中提供的路径.c
。添加了删除前面的
。/
的修改,以符合您的问题陈述:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void pathCanonicalize (char *path);

int main (int argc, char **argv)
{
    if (argc < 2) {
        fprintf (stderr, "error: insufficient input, usage: %s <path>\n",
                argv[0]);
        return 1;
    }

    char *fullpath = strdup (argv[1]);
    if (!fullpath) {
        fprintf (stderr, "error: virtual memory exhausted.\n");
        return 1;
    }

    pathCanonicalize (fullpath);

    printf ("\n original : %s\n canonical: %s\n\n", argv[1], fullpath);

    free (fullpath);

    return 0;
}

void pathCanonicalize (char *path)
{
    size_t i;
    size_t j;
    size_t k;

    //Move to the beginning of the string
    i = 0;
    k = 0;

    //Replace backslashes with forward slashes
    while (path[i] != '\0') {
        //Forward slash or backslash separator found?
        if (path[i] == '/' || path[i] == '\\') {
            path[k++] = '/';
            while (path[i] == '/' || path[i] == '\\')
                i++;
        } else {
            path[k++] = path[i++];
        }
    }

    //Properly terminate the string with a NULL character
    path[k] = '\0';

    //Move back to the beginning of the string
    i = 0;
    j = 0;
    k = 0;

    //Parse the entire string
    do {
        //Forward slash separator found?
        if (path[i] == '/' || path[i] == '\0') {
            //"." element found?
            if ((i - j) == 1 && !strncmp (path + j, ".", 1)) {
                //Check whether the pathname is empty?
                if (k == 0) {
                    if (path[i] == '\0') {
                        path[k++] = '.';
                    } else if (path[i] == '/' && path[i + 1] == '\0') {
                        path[k++] = '.';
                        path[k++] = '/';
                    }
                } else if (k > 1) {
                    //Remove the final slash if necessary
                    if (path[i] == '\0')
                        k--;
                }
            }
            //".." element found?
            else if ((i - j) == 2 && !strncmp (path + j, "..", 2)) {
                //Check whether the pathname is empty?
                if (k == 0) {
                    path[k++] = '.';
                    path[k++] = '.';

                    //Append a slash if necessary
                    if (path[i] == '/')
                        path[k++] = '/';
                } else if (k > 1) {
                    //Search the path for the previous slash
                    for (j = 1; j < k; j++) {
                        if (path[k - j - 1] == '/')
                            break;
                    }

                    //Slash separator found?
                    if (j < k) {
                        if (!strncmp (path + k - j, "..", 2)) {
                            path[k++] = '.';
                            path[k++] = '.';
                        } else {
                            k = k - j - 1;
                        }

                        //Append a slash if necessary
                        if (k == 0 && path[0] == '/')
                            path[k++] = '/';
                        else if (path[i] == '/')
                            path[k++] = '/';
                    }
                    //No slash separator found?
                    else {
                        if (k == 3 && !strncmp (path, "..", 2)) {
                            path[k++] = '.';
                            path[k++] = '.';

                            //Append a slash if necessary
                            if (path[i] == '/')
                                path[k++] = '/';
                        } else if (path[i] == '\0') {
                            k = 0;
                            path[k++] = '.';
                        } else if (path[i] == '/' && path[i + 1] == '\0') {
                            k = 0;
                            path[k++] = '.';
                            path[k++] = '/';
                        } else {
                            k = 0;
                        }
                    }
                }
            } else {
                //Copy directory name
                memmove (path + k, path + j, i - j);
                //Advance write pointer
                k += i - j;

                //Append a slash if necessary
                if (path[i] == '/')
                    path[k++] = '/';
            }

            //Move to the next token
            while (path[i] == '/')
                i++;
            j = i;
        }
        else if (k == 0) {
            while (path[i] == '.' || path[i] == '/') {
                 j++,i++;
            }
        }
    } while (path[i++] != '\0');

    //Properly terminate the string with a NULL character
    path[k] = '\0';
}

我不认为有任何标准的库函数可用于此

您可以使用源代码文件
server/util.c
中的函数
ap\u getparents()
。我相信它正是您想要的:


(这是假设在您的项目中重新使用Apache许可代码是可以接受的。)

另一次尝试。此尝试的怪癖/功能:

  • 不规范化为源字符串;写入调用方提供的空间
  • 有绝对路径与相对路径的概念(源路径是否以“/”开头?):如果存在足够多的“..”吃掉所有源,则发出“/”表示绝对路径,发出“.”表示相对路径
  • 不知道源路径中的元素是否对应于实际的文件系统对象
  • 使用C99可变长度数组,并将其返回到调用者提供的空间中,不使用malloc,而是在引擎盖下进行一些拷贝
  • 给定这些副本,源和目标可以相同
  • 使用strtok_r(3),其关于不返回零长度标记的怪癖似乎与相邻“/”字符的期望行为相匹配
资料来源:

#include <stdlib.h>
#include <string.h>

int
pathcanon(const char *srcpath, char *dstpath, size_t sz)
{
    size_t plen = strlen(srcpath) + 1, chk;
    char wtmp[plen], *tokv[plen], *s, *tok, *sav;
    int i, ti, relpath;

    relpath = (*srcpath == '/') ? 0 : 1;

    /* make a local copy of srcpath so strtok(3) won't mangle it */

    ti = 0;
    (void) strcpy(wtmp, srcpath);

    tok = strtok_r(wtmp, "/", &sav);
    while (tok != NULL) {
        if (strcmp(tok, "..") == 0) {
            if (ti > 0) {
                ti--;
            }
        } else if (strcmp(tok, ".") != 0) {
            tokv[ti++] = tok;
        }
        tok = strtok_r(NULL, "/", &sav);
    }

    chk = 0;
    s = dstpath;

    /*
     * Construct canonicalized result, checking for room as we
     * go. Running out of space leaves dstpath unusable: written
     * to and *not* cleanly NUL-terminated.
     */
    for (i = 0; i < ti; i++) {
        size_t l = strlen(tokv[i]);

        if (i > 0 || !relpath) {
            if (++chk >= sz) return -1;
            *s++ = '/';
        }

        chk += l;
        if (chk >= sz) return -1;

        strcpy(s, tokv[i]);
        s += l;
    }

    if (s == dstpath) {
        if (++chk >= sz) return -1;
        *s++ = relpath ? '.' : '/';
    }
    *s = '\0';

    return 0;
}
#包括
#包括
int
pathcanon(常量字符*srcpath,字符*dstpath,大小sz)
{
尺寸=strlen(srcpath)+1,chk;
字符wtmp[plen]、*tokv[plen]、*s、*tok、*sav;
int i,ti,relpath;
relpath=(*srcpath=='/')?0:1;
/*制作srcpath的本地副本,这样strtok(3)就不会损坏它*/
ti=0;
(无效)strcpy(wtmp,srcpath);
tok=strtok_r(wtmp,“/”,&sav);
while(tok!=NULL){
如果(strcmp(tok,“…”)==0){
如果(ti>
#ifdef WIN32
#define IS_SLASH(s) ((s == '/') || (s == '\\'))
#else
#define IS_SLASH(s) (s == '/')
#endif

void ap_getparents(char *name)
{
    char *next;
    int l, w, first_dot;

    /* Four paseses, as per RFC 1808 */
    /* a) remove ./ path segments */
    for (next = name; *next && (*next != '.'); next++) {
    }

    l = w = first_dot = next - name;
    while (name[l] != '\0') {
        if (name[l] == '.' && IS_SLASH(name[l + 1])
            && (l == 0 || IS_SLASH(name[l - 1])))
            l += 2;
        else
            name[w++] = name[l++];
    }

    /* b) remove trailing . path, segment */
    if (w == 1 && name[0] == '.')
        w--;
    else if (w > 1 && name[w - 1] == '.' && IS_SLASH(name[w - 2]))
        w--;
    name[w] = '\0';

    /* c) remove all xx/../ segments. (including leading ../ and /../) */
    l = first_dot;

    while (name[l] != '\0') {
        if (name[l] == '.' && name[l + 1] == '.' && IS_SLASH(name[l + 2])
            && (l == 0 || IS_SLASH(name[l - 1]))) {
            int m = l + 3, n;

            l = l - 2;
            if (l >= 0) {
                while (l >= 0 && !IS_SLASH(name[l]))
                    l--;
                l++;
            }
            else
                l = 0;
            n = l;
            while ((name[n] = name[m]))
                (++n, ++m);
        }
        else
            ++l;
    }

    /* d) remove trailing xx/.. segment. */
    if (l == 2 && name[0] == '.' && name[1] == '.')
        name[0] = '\0';
    else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.'
             && IS_SLASH(name[l - 3])) {
        l = l - 4;
        if (l >= 0) {
            while (l >= 0 && !IS_SLASH(name[l]))
                l--;
            l++;
        }
        else
            l = 0;
        name[l] = '\0';
    }
}
#include <stdlib.h>
#include <string.h>

int
pathcanon(const char *srcpath, char *dstpath, size_t sz)
{
    size_t plen = strlen(srcpath) + 1, chk;
    char wtmp[plen], *tokv[plen], *s, *tok, *sav;
    int i, ti, relpath;

    relpath = (*srcpath == '/') ? 0 : 1;

    /* make a local copy of srcpath so strtok(3) won't mangle it */

    ti = 0;
    (void) strcpy(wtmp, srcpath);

    tok = strtok_r(wtmp, "/", &sav);
    while (tok != NULL) {
        if (strcmp(tok, "..") == 0) {
            if (ti > 0) {
                ti--;
            }
        } else if (strcmp(tok, ".") != 0) {
            tokv[ti++] = tok;
        }
        tok = strtok_r(NULL, "/", &sav);
    }

    chk = 0;
    s = dstpath;

    /*
     * Construct canonicalized result, checking for room as we
     * go. Running out of space leaves dstpath unusable: written
     * to and *not* cleanly NUL-terminated.
     */
    for (i = 0; i < ti; i++) {
        size_t l = strlen(tokv[i]);

        if (i > 0 || !relpath) {
            if (++chk >= sz) return -1;
            *s++ = '/';
        }

        chk += l;
        if (chk >= sz) return -1;

        strcpy(s, tokv[i]);
        s += l;
    }

    if (s == dstpath) {
        if (++chk >= sz) return -1;
        *s++ = relpath ? '.' : '/';
    }
    *s = '\0';

    return 0;
}