diff options
author | Martin Baulig <martin@src.gnome.org> | 1998-09-02 07:22:53 +0000 |
---|---|---|
committer | Martin Baulig <martin@src.gnome.org> | 1998-09-02 07:22:53 +0000 |
commit | 487da1dced6b55a9334cc703375b2b3eccfe4f4c (patch) | |
tree | 4b2850ce95acf163c417cd927af272667d62e8a6 /support/canonicalize.c | |
parent | 6262c369814d8b00d1284faaf9de8087fb3c7bc5 (diff) | |
download | shared-mime-info-487da1dced6b55a9334cc703375b2b3eccfe4f4c.tar.gz |
Initial revision
svn path=/trunk/; revision=365
Diffstat (limited to 'support/canonicalize.c')
-rw-r--r-- | support/canonicalize.c | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/support/canonicalize.c b/support/canonicalize.c new file mode 100644 index 00000000..0985e250 --- /dev/null +++ b/support/canonicalize.c @@ -0,0 +1,182 @@ +/* Return the canonical absolute name of a given file. + Copyright (C) 1996 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <errno.h> + +/* Return the canonical absolute name of file NAME. A canonical name + does not contain any `.', `..' components nor any repeated path + separators ('/') or symlinks. All path components must exist. If + RESOLVED is null, the result is malloc'd; otherwise, if the + canonical name is PATH_MAX chars or more, returns null with `errno' + set to ENAMETOOLONG; if the name fits in fewer than PATH_MAX chars, + returns the name in RESOLVED. If the name cannot be resolved and + RESOLVED is non-NULL, it contains the path of the first component + that cannot be resolved. If the path can be resolved, RESOLVED + holds the same value as the value returned. */ + +static char * +canonicalize (const char *name, char *resolved) +{ + char *rpath, *dest, *extra_buf = NULL; + const char *start, *end, *rpath_limit; + long int path_max; + int num_links = 0; + +#ifdef PATH_MAX + path_max = PATH_MAX; +#else + path_max = pathconf (name, _PC_PATH_MAX); + if (path_max <= 0) + path_max = 1024; +#endif + + rpath = resolved ? __alloca (path_max) : malloc (path_max); + rpath_limit = rpath + path_max; + + if (name[0] != '/') + { + if (!getcwd (rpath, path_max)) + goto error; + dest = strchr (rpath, '\0'); + } + else + { + rpath[0] = '/'; + dest = rpath + 1; + } + + for (start = end = name; *start; start = end) + { + struct stat st; + int n; + + /* skip sequence of multiple path-separators: */ + while (*start == '/') ++start; + + /* find end of path component: */ + for (end = start; *end && *end != '/'; ++end); + + if (end - start == 0) + break; + else if (strncmp (start, ".", end - start) == 0) + /* nothing */; + else if (strncmp (start, "..", end - start) == 0) { + /* back up to previous component, ignore if at root already: */ + if (dest > rpath + 1) + while ((--dest)[-1] != '/'); + } else + { + size_t new_size; + + if (dest[-1] != '/') + *dest++ = '/'; + + if (dest + (end - start) >= rpath_limit) + { + if (resolved) + { + __set_errno (ENAMETOOLONG); + goto error; + } + new_size = rpath_limit - rpath; + if (end - start + 1 > path_max) + new_size += end - start + 1; + else + new_size += path_max; + rpath = realloc (rpath, new_size); + rpath_limit = rpath + new_size; + if (!rpath) + return NULL; + } + + memcpy (dest, start, end - start); + dest += end - start; + *dest = '\0'; + + if (__lstat (rpath, &st) < 0) + goto error; + + if (S_ISLNK (st.st_mode)) + { + char *buf = __alloca (path_max); + + if (++num_links > MAXSYMLINKS) + { + __set_errno (ELOOP); + goto error; + } + + n = readlink (rpath, buf, path_max); + if (n < 0) + goto error; + buf[n] = '\0'; + + if (!extra_buf) + extra_buf = __alloca (path_max); + + if ((long int) (n + strlen (end)) >= path_max) + { + __set_errno (ENAMETOOLONG); + goto error; + } + + /* careful here, end may be a pointer into extra_buf... */ + strcat (buf, end); + strcpy (extra_buf, buf); + name = end = extra_buf; + + if (buf[0] == '/') + dest = rpath + 1; /* it's an absolute symlink */ + else + /* back up to previous component, ignore if at root already: */ + if (dest > rpath + 1) + while ((--dest)[-1] != '/'); + } + else + num_links = 0; + } + } + if (dest > rpath + 1 && dest[-1] == '/') + --dest; + *dest = '\0'; + + return resolved ? strcpy (resolved, rpath) : rpath; + +error: + if (resolved) + strcpy (resolved, rpath); + else + free (rpath); + return NULL; +} +weak_alias (canonicalize, realpath) + + +char * +canonicalize_file_name (const char *name) +{ + return canonicalize (name, NULL); +} |