2011-01-23 9 views
10

Przeczytałem już o realpath(), ale czy istnieje funkcja, która może przekazać podstawowy katalog i nazwę pliku, który dałby mi następujący wynik bez rozwiązywania dowiązań symbolicznych lub sprawdzania, czy pliki rzeczywiście istnieją? Czy muszę użyć zmodyfikowanej wersji realpath()?realpath() bez rozwiązywania dowiązań symbolicznych?

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

jaki powinien być wynik "/dir/a_random_synlink/../hello"? pamiętaj, że może to nie być to samo, co "/ dir/hello", jeśli a_random_synlink nie wskazuje na katalog w tym samym katalogu – BatchyX

+0

@BatchyX: Wydaje się być standardowym zachowaniem: 'readlink -v -m '/ home/user/linktoslashtmp /../ '' zwraca '/ home/user' – thejh

+1

Może readlink robi to, ale podstawowy system operacyjny tego nie robi. ls/home/user/linktoslashtmp/../ wyświetla zawartość/ – BatchyX

Odpowiedz

8

Oto normalize_path ():

Jeśli dana ścieżka jest względna, funkcja rozpoczyna się od poprzedzania bieżącego katalogu roboczego.

Następnie komponenty ścieżki , takie jak .., . lub puste komponenty są traktowane, a wynik jest zwracany.

Dla .., ostatni komponent zostanie usunięty, jeśli istnieje (/.. po prostu zwróci /).
Dla . lub pustych komponentów (podwójne /), jest to po prostu pomijane.

Funkcja zapewnia, że ​​nie powróci pusta ścieżka (zamiast tego zwracana jest nazwa /).

#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: Wydaje się, że działa to dobrze w przypadku, gdy ścieżka jest oceniana względem bieżącego katalogu. Ściśle rzecz biorąc, uważam, że interpretacja pytania brzmi "oceń ścieżkę" ../etc/////passwd' względem '/ var /' ", która jest prostą odmianą twojego motywu (nie masz trzeba ustanowić bieżący katalog za pomocą 'getcwd()'; należy użyć wartości przekazanej przez użytkownika). –

+0

Dzięki, wygląda dobrze - trochę zmodyfikowałem funkcję, aby zaakceptować parametr pwd. – thejh

+1

Oczywiście, daję ci pozwolenie. – 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); 
} 

(Pytanie zostało oznaczone PHP w momencie napisałem to).

Zresztą, tutaj jest wersja regex:

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

Tak, został oznaczony bez języka, ktoś wstawił "php", a ja zmieniłem go na "c" - przepraszam, że zapomniałem tego tagu. – thejh

+0

@ user576875 @thejh Mój zły (oznaczono go jako PHP). Powinien najpierw sprawdzić twoje ostatnie pytania. Przepraszam wszystkich. –

1

użyć Hardex jest 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; 
} 

przykład:

#include <stdio.h> 

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

wyjściowa:

/usr 


Uwaga:

  1. Pierwszy argument jest ścieżkę (ścieżka bezwzględna) w stosunku, do której będą znormalizowane inne ścieżki. Jest to na ogół bezwzględna ścieżka do bieżącego katalogu.
  2. Drugi argument to ciąg normalizowany bez rozwiązywania dowiązań symbolicznych.
  3. Trzeci argument to char*, który musi mieć wymaganą pojemność pamięci zawierającą znormalizowaną ścieżkę.
Powiązane problemy