2011-01-23 3 views
10

Я уже читал о realpath(), но есть ли функция, с которой я могу передать базовый каталог и имя файла, которое даст мне следующий результат без разрешения символических ссылок или проверки того, существуют ли файлы на самом деле? Или мне нужно использовать модифицированный realpath()?realpath() без разрешения символических ссылок?

"/var/", "../etc///././/passwd" => "/etc/passwd" 
+3

что должно быть результатом "/dir/a_random_synlink/../hello"? помните, что это может быть не то же самое, что «/ dir/hello», если a_random_synlink не указывает на каталог в том же каталоге – BatchyX

+0

@BatchyX: Кажется, это стандартное поведение: 'readlink -v -m '/ home/user/linktoslashtmp /../ ''возвращает'/home/user' – thejh

+1

, возможно, readlink делает это, но базовая ОС этого не делает. ls/home/user/linktoslashtmp/../ перечисляет содержимое/ – BatchyX

ответ

8

Вот normalize_path ():

Если данный путь относительный, функция начинается с добавления к нему текущего рабочего каталога.

Тогда специальные компоненты пути, как .., . или пустые компоненты обрабатываются, и результат возвращается.

Для .. последний компонент удален, если он есть (/.. только что вернет /).
Для . или пустых компонентов (double /), это просто пропущено.

Функция гарантирует возврат пустого пути (вместо этого возвращается /).

#define _GNU_SOURCE /* memrchr() */ 

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

char * normalize_path(const char * src, size_t src_len) { 

     char * res; 
     size_t res_len; 

     const char * ptr = src; 
     const char * end = &src[src_len]; 
     const char * next; 

     if (src_len == 0 || src[0] != '/') { 

       // relative path 

       char pwd[PATH_MAX]; 
       size_t pwd_len; 

       if (getcwd(pwd, sizeof(pwd)) == NULL) { 
         return NULL; 
       } 

       pwd_len = strlen(pwd); 
       res = malloc(pwd_len + 1 + src_len + 1); 
       memcpy(res, pwd, pwd_len); 
       res_len = pwd_len; 
     } else { 
       res = malloc((src_len > 0 ? src_len : 1) + 1); 
       res_len = 0; 
     } 

     for (ptr = src; ptr < end; ptr=next+1) { 
       size_t len; 
       next = memchr(ptr, '/', end-ptr); 
       if (next == NULL) { 
         next = end; 
       } 
       len = next-ptr; 
       switch(len) { 
       case 2: 
         if (ptr[0] == '.' && ptr[1] == '.') { 
           const char * slash = memrchr(res, '/', res_len); 
           if (slash != NULL) { 
             res_len = slash - res; 
           } 
           continue; 
         } 
         break; 
       case 1: 
         if (ptr[0] == '.') { 
           continue; 

         } 
         break; 
       case 0: 
         continue; 
       } 
       res[res_len++] = '/'; 
       memcpy(&res[res_len], ptr, len); 
       res_len += len; 
     } 

     if (res_len == 0) { 
       res[res_len++] = '/'; 
     } 
     res[res_len] = '\0'; 
     return res; 
} 
+0

+1: Кажется, что это хорошо работает для случая, когда путь оценивается относительно текущего каталога. Строго говоря, я считаю, что интерпретация вопроса - это «оценить путь» ../ etc ///././ passwd' относительно '/ var /', что является простым вариантом вашей темы (вы не нужно установить текущий каталог с 'getcwd()'; вы используете значение, переданное пользователем). –

+0

Спасибо, хорошо выглядит - я немного изменил функцию, чтобы принять параметр pwd. – thejh

+1

Уверен, я даю вам разрешение – arnaud576875

1
function normalize_path($path, $pwd = '/') { 
     if (!isset($path[0]) || $path[0] !== '/') { 
       $result = explode('/', getcwd()); 
     } else { 
       $result = array(''); 
     } 
     $parts = explode('/', $path); 
     foreach($parts as $part) { 
      if ($part === '' || $part == '.') { 
        continue; 
      } if ($part == '..') { 
        array_pop($result); 
      } else { 
        $result[] = $part; 
      } 
     } 
     return implode('/', $result); 
} 

(вопрос был помечен PHP в то время я написал это.)

Во всяком случае, вот версия регулярное выражение:

function normalize_path($path, $pwd = '/') { 
     if (!isset($path[0]) || $path[0] !== '/') { 
       $path = "$pwd/$path"; 
     } 
     return preg_replace('~ 
       ^(?P>sdotdot)?(?:(?P>sdot)*/\.\.)* 
       |(?<sdotdot>(?:(?P>sdot)*/(?!\.\.)(?:[^/]+)(?P>sdotdot)?(?P>sdot)*/\.\.)+) 
       |(?<sdot>/\.?(?=/|$))+ 
     ~sx', '', $path); 
} 
+0

Да, он был помечен без языка, кто-то положил на него «php», и я изменил его на «c» - извините за то, что забыл этот тег. – thejh

+0

@ user576875 @thejh Мой плохой (я отметил его как PHP). Должен был сначала проверить ваши последние вопросы. Извиняюсь перед всеми. –

1

Hardex Я использую «ы solution:

#include <string.h> 

char * normalizePath(char* pwd, const char * src, char* res) { 
    size_t res_len; 
    size_t src_len = strlen(src); 

    const char * ptr = src; 
    const char * end = &src[src_len]; 
    const char * next; 

    if (src_len == 0 || src[0] != '/') { 
     // relative path 
     size_t pwd_len; 

     pwd_len = strlen(pwd); 
     memcpy(res, pwd, pwd_len); 
     res_len = pwd_len; 
    } else { 
     res_len = 0; 
    } 

    for (ptr = src; ptr < end; ptr=next+1) { 
     size_t len; 
     next = (char*)memchr(ptr, '/', end-ptr); 
     if (next == NULL) { 
      next = end; 
     } 
     len = next-ptr; 
     switch(len) { 
     case 2: 
      if (ptr[0] == '.' && ptr[1] == '.') { 
       const char * slash = (char*)memrchr(res, '/', res_len); 
       if (slash != NULL) { 
        res_len = slash - res; 
       } 
       continue; 
      } 
      break; 
     case 1: 
      if (ptr[0] == '.') { 
       continue; 
      } 
      break; 
     case 0: 
      continue; 
     } 

     if (res_len != 1) 
      res[res_len++] = '/'; 

     memcpy(&res[res_len], ptr, len); 
     res_len += len; 
    } 

    if (res_len == 0) { 
     res[res_len++] = '/'; 
    } 
    res[res_len] = '\0'; 
    return res; 
} 

Пример:

#include <stdio.h> 

int main(){ 
    char path[FILENAME_MAX+1]; 
    printf("\n%s\n",normalizePath((char*)"/usr/share/local/apps",(char*)"./../../../",path)); 
    return 0; 
} 

Выход:

/usr 


Примечание:

  1. Первый аргумент это путь каталога (абсолютный путь) по отношению к которому будут нормализованы другие пути. Это, как правило, абсолютный путь к текущему каталогу.
  2. Второй аргумент - это строка, которая должна быть нормализована без разрешения символических ссылок.
  3. Третий аргумент - char*, который должен иметь требуемую память/емкость, чтобы содержать нормализованный путь.
Смежные вопросы