Ни один из стандартов языка C не определяет функции для работы с каталогами или путями. Вы не можете выполнить свою задачу без каких-либо знаний об операционной системе и получить системные вызовы ОС или библиотечные функции.
Указывает, что ваша программа предназначена для операционной системы Unix. Есть сотни разновидностей Unix (см. http://www.levenez.com/unix/ и эта временная шкала на 34 страницы: http://www.levenez.com/unix/unix_a4.pdf). Самый портативный способ работы с каталогами для цели unix - использовать стандартизированные системные вызовы Posix.
Posix не является расширением для данного языка, это спецификация структур и интерфейсов операционной системы. Он реализован в стандартных файлах заголовков и библиотеках. Ваша программа может придерживаться стандартов c89, c90, c99 или c11 и использовать системные вызовы Posix. gcc
включит часть Posix glibc в системах Posix, если вы не поручите ей ограничить поддержку библиотеки Standard C.
Posix стандартизованный realpath
в 2001 году, он доступен практически для каждой современной унифицированной системы. если вы не можете использовать это, вы переписываете его функциональные возможности с getcwd()
, stat()
, readlink()
и другими системными вызовами, а ваш код будет менее переносимым.
Страница руководства здесь: http://man7.org/linux/man-pages/man3/realpath.3.html
EDIT: Существует искаженное решение, которое не использует явную поддержку Posix, но косвенно зависит от наличия оболочки Posix. Это не рекомендуемое решение для производственного кода, но оно может соответствовать вашим конкретным ограничениям. Вот шаги:
- с
strrchr
, извлеките начальную часть пути в имени файла.
- создайте команду для изменения текущей директории и распечатайте текущий каталог во временном файле.
- открыть и прочитать содержимое этого временного файла.
- удалите временный файл.
- конкатенации путь и имя файла с
/
сепараторе
Заметим, что изменение текущего каталога в суб-оболочки не повлияет на собственный текущий каталог вашей программы.
EDIT: Выполнение вышеизложенного несколько сложнее. Вам нужно обработать некоторые особые случаи.Вот пример:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static char *my_realpath(const char *path) {
const char *lastsep, *fname;
char *cmd, *dir, *newdir;
int pathlen, fnamelen, rc, size, pos, len;
char buf[256];
char tmpfile[L_tmpnam];
FILE *fp;
if (!tmpnam(tmpfile))
return NULL;
pathlen = 0;
lastsep = strrchr(path, '/');
if (lastsep) {
pathlen = lastsep - path;
if (pathlen == 0) {
/* special case the root directory */
pathlen = 1;
}
}
fname = path + pathlen;
fnamelen = strlen(fname);
if (!strcmp(fname, ".")
|| !strcmp(fname, "/.")
|| !strcmp(fname, "..")
|| !strcmp(fname, "/..")) {
pathlen += fnamelen;
fname += fnamelen;
fnamelen = 0;
}
if (*fname == '/') {
fname++;
fnamelen--;
}
if (pathlen > 0) {
size = strlen("cd ") + pathlen + strlen("; pwd > ") + strlen(tmpfile) + 1;
cmd = malloc(size);
if (!cmd)
return NULL;
sprintf(cmd, "cd %.*s; pwd > %s", pathlen, path, tmpfile);
} else {
size = strlen("pwd > ") + strlen(tmpfile) + 1;
cmd = malloc(size);
if (!cmd)
return NULL;
sprintf(cmd, "pwd > %s", tmpfile);
}
rc = system(cmd);
free(cmd);
if (rc != 0)
return NULL;
fp = fopen(tmpfile, "r");
size = pos = 0;
dir = NULL;
while (fgets(buf, sizeof buf, fp)) {
len = strcspn(buf, "\n");
size += len + 1;
newdir = realloc(dir, size + fnamelen + 1);
if (!newdir) {
free(dir);
dir = NULL;
break;
}
dir = newdir;
memcpy(dir + pos, buf, len);
pos += len;
dir[pos] = '\0';
if (buf[len] == '\n')
break;
}
fclose(fp);
remove(tmpfile);
if (dir != NULL) {
if (pos > 0 && dir[pos - 1] != '/' && fnamelen > 0) {
dir[pos++] = '/';
}
strcpy(dir + pos, fname);
}
return dir;
}
int main(int argc, const char *argv[]) {
int i;
char *absolute_path;
for (i = 1; i < argc; i++) {
errno = 0;
absolute_path = my_realpath(argv[i]);
if (absolute_path == NULL) {
printf("my_realpath(\"%s\") -> NULL: %s\n", argv[i], strerror(errno));
} else {
printf("my_realpath(\"%s\") -> \"%s\"\n", argv[i], absolute_path);
free(absolute_path);
}
}
return 0;
}
Проблема в том, что определение абсолютного пути зависит от ОС. Таким образом, не может быть портативное решение (по крайней мере, не без кучи #ifdefs или проверки времени выполнения на основе ОС). – user3386109
Спасибо за это. Я обновлю свой вопрос для систем UNIX – Ankush
@Ankush: Если вы используете систему Unix (как), то вы уже являетесь POSIX и должны уже иметь «realpath()». Почему вам нужно строгое соответствие ANSI/ISO, если ваша программа будет в любом случае POSIX? – 3442