我正在创建一个程序,它递归地查找指定目录中的 C 文件与其子目录之间的所有#include 依赖项。依赖路径应该是绝对的,所以我使用realpath来解析相对路径和符号链接。由于可能有很多文件,我决定使用 OpenMP 或 pthreads 使程序多线程。
问题是 realpath 通过工作目录解析路径。所有线程共享同一个工作目录,所以我需要在chdir和 realpath 上放置一个互斥锁。
是否有任何替代 realpath 的标准函数,它也将目录作为参数解析路径?
我正在创建一个程序,它递归地查找指定目录中的 C 文件与其子目录之间的所有#include 依赖项。依赖路径应该是绝对的,所以我使用realpath来解析相对路径和符号链接。由于可能有很多文件,我决定使用 OpenMP 或 pthreads 使程序多线程。
问题是 realpath 通过工作目录解析路径。所有线程共享同一个工作目录,所以我需要在chdir和 realpath 上放置一个互斥锁。
是否有任何替代 realpath 的标准函数,它也将目录作为参数解析路径?
有许多带有at
后缀(例如openat()
)的 POSIX 函数可用于指定目录。但是,realpathat()
POSIX 中没有函数。也没有opendirat()
, 但有它为打开的目录文件描述符fdopendir()
创建一个流。DIR
在多线程程序中,任何使用都chdir()
令人担忧。
您应该重新考虑您的算法以使用各种*at()
功能,以避免根本需要更改目录。您可以打开目录进行读取(open()
或openat()
使用O_DIRECTORY
,也许——尽管不是 100% 必要,macOS 也不支持),这样您就可以在调用O_DIRECTORY
中使用目录文件描述符适当地访问文件。*at()
我在解决方案上做了一些工作。这绝不是最佳的,但至少它似乎有效。我创建了将相对路径转换为绝对路径的函数abspathat 。然后我使用内置的readlinkat来修复符号链接。该解决方案处理将“../code.c”“./code.c”“code.c”之类的路径转换为“/dir/code.c”。但是,它目前不修复诸如 ../dir/../code.c 之类的路径,尽管为什么有人会创建这样的路径。它也不检查文件是否确实存在。随意改进或使用此代码做任何您喜欢的事情。
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <dirent.h>
#include <stdio.h>
/*****************************************************************************/
char *abspathat(char *dirpath, int dirlen, char *path);
/*****************************************************************************/
static const int MAX_FILEPATH = 4096;
/*****************************************************************************/
char *realpathat(int dirfd, char *dirpath, int dirlen, char *path) {
char *abs = abspathat(dirpath, dirlen, path);
char *buf = malloc(sizeof(char)*MAX_FILEPATH);
ssize_t size = readlinkat(dirfd, abs, buf, MAX_FILEPATH);
char *realpath;
if(size != -1) {
realpath = malloc(sizeof(size+1));
memcpy(realpath, buf, size);
realpath[size] = '\0';
free(abs);
} else {
realpath = abs;
}
free(buf);
return realpath;
}
/*---------------------------------------------------------------------------*/
char *abspathat(char *dirpath, int dirlen, char *path) {
/* If absolute */
if(path[0] == '/') {
return path;
}
int i;
char *right;
int d = 0;
int rlen = strlen(path);
int llen = 0;
if(path[0] == '.') {
if(path[1] == '.' && path[2] == '/') {
for(i = 3, d = 1; path[i] == '.'
&& path[i+1] == '.'
&& path[i+2] == '/'
&& i < rlen; i+=3) {
d++;
}
right = &path[i];
rlen -= i;
} else if(path[1] == '/') {
right = &path[2];
rlen -= 2;
}
} else {
right = &path[0];
}
for(i = dirlen - 1 - (dirpath[dirlen-1] == '/'); d && i; i--) {
if(dirpath[i] == '/') {
d--;
}
}
llen = i+1;
char *cpy = malloc(sizeof(char)*(llen + rlen + 2));
memcpy(cpy, dirpath, llen);
cpy[llen] = '/';
memcpy(cpy+llen+1, right, rlen);
cpy[llen+rlen+1] = '\0';
return cpy;
}
/*---------------------------------------------------------------------------*/
int main(int argc, char *argv[]) {
if(argc == 3) {
char *dirpath = argv[1];
DIR *d = opendir(dirpath);
char *path = argv[2];
char *resolved = realpathat(dirfd(d), dirpath, strlen(dirpath), path);
printf("%s\n", resolved);
} else {
printf("realpathat [directory] [filepath]\n");
}
return 0;
}