2011-01-23 23 views
10

Ya leí acerca de realpath(), pero ¿hay alguna función que me permita pasar un directorio base y un nombre de archivo que me daría el siguiente resultado sin resolver los enlaces simbólicos o verificar si los archivos existen realmente? ¿O tengo que usar un realpath() modificado?realpath() sin resolver enlaces simbólicos?

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

¿Cuál debería ser el resultado de "/dir/a_random_synlink/../hello"? recuerde que puede no ser lo mismo que "/ dir/hello" si a_random_synlink no apunta a un directorio en el mismo directorio – BatchyX

+0

@BatchyX: Parece ser el comportamiento estándar: 'readlink -v -m '/ home/user/linktoslashtmp /../ ''returns'/home/user' – thejh

+1

tal vez readlink hace esto, pero el sistema operativo subyacente no lo hace. ls/home/usuario/linktoslashtmp/../ enumera el contenido de/ – BatchyX

Respuesta

8

Aquí es una normalize_path () función:

Si la ruta dada es relativa, la función comienza por anteponer el directorio de trabajo actual.

Entonces los componentes especiales camino como .., . o vacíos componentes son tratados, y se devuelve el resultado.

Para .., el último componente se elimina si hay uno (/.. simplemente devolverá /).
Para . o componentes vacíos (doble /), esto simplemente se salta.

La función garantiza que no se devolverá vacía una ruta (en su lugar, se devuelve /).

#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: Parece que funciona bien para el caso donde se evalúa la ruta en relación con el directorio actual. Estrictamente, creo que la interpretación de la pregunta es "evaluar el camino' ../ etc ///././ passwd' relativo a '/ var /' ", que es una variación simple de su tema (no lo hace necesita establecer el directorio actual con 'getcwd()'; usted usa el valor pasado por el usuario). –

+0

Gracias, se ve bien - Modifiqué la función un poco para aceptar un parámetro pwd. – thejh

+1

Claro, le doy permiso – 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); 
} 

(La pregunta fue etiquetado PHP en el momento de escribir esto).

De todos modos, aquí es una versión de expresiones regulares:

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

Sí, fue etiquetado sin idioma, alguien puso "php" en él y lo cambié a "c" - perdón por olvidar esa etiqueta. – thejh

+0

@ user576875 @thejh Mi mal (lo etiqueté como PHP). Debería haber revisado sus preguntas recientes primero. Disculpas a todos. –

1

utilizo Hardex 's 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; 
} 

Ejemplo:

#include <stdio.h> 

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

de salida:

/usr 


Nota:

  1. El primer argumento es la ruta del directorio (ruta absoluta) con relación a la que se normalizarán otros caminos. En general, es la ruta absoluta del directorio actual.
  2. El segundo argumento es la cadena que se va a normalizar sin resolver los enlaces simbólicos.
  3. El tercer argumento es un char* que debe tener la memoria/capacidad requerida para contener la ruta normalizada.
Cuestiones relacionadas