Fix rpm.unregister() Lua extension
[platform/upstream/rpm.git] / misc / realpath.c
1 /*
2  * realpath.c -- canonicalize pathname by removing symlinks
3  * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU Library Public License as published by
7  * the Free Software Foundation; either version 2, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Library Public License for more details.
14  */
15
16 #include "system.h"
17
18 #ifndef STDC_HEADERS
19 extern int errno;
20 #endif
21
22 #define MAX_READLINKS 32
23
24 #ifdef __STDC__
25 char *realpath(const char *path, char resolved_path [])
26 #else
27 char *realpath(path, resolved_path)
28 const char *path;
29 char resolved_path [];
30 #endif
31 {
32         char copy_path[PATH_MAX];
33         char link_path[PATH_MAX];
34         char *new_path = resolved_path;
35         char *max_path;
36         int readlinks = 0;
37         int n;
38
39         /* Make a copy of the source path since we may need to modify it. */
40         strcpy(copy_path, path);
41         path = copy_path;
42         max_path = copy_path + PATH_MAX - 2;
43         /* If it's a relative pathname use getwd for starters. */
44         if (*path != '/') {
45 #ifdef HAVE_GETCWD
46                 getcwd(new_path, PATH_MAX - 1);
47 #else
48                 getwd(new_path);
49 #endif
50                 new_path += strlen(new_path);
51                 if (new_path[-1] != '/')
52                         *new_path++ = '/';
53         }
54         else {
55                 *new_path++ = '/';
56                 path++;
57         }
58         /* Expand each slash-separated pathname component. */
59         while (*path != '\0') {
60                 /* Ignore stray "/". */
61                 if (*path == '/') {
62                         path++;
63                         continue;
64                 }
65                 if (*path == '.') {
66                         /* Ignore ".". */
67                         if (path[1] == '\0' || path[1] == '/') {
68                                 path++;
69                                 continue;
70                         }
71                         if (path[1] == '.') {
72                                 if (path[2] == '\0' || path[2] == '/') {
73                                         path += 2;
74                                         /* Ignore ".." at root. */
75                                         if (new_path == resolved_path + 1)
76                                                 continue;
77                                         /* Handle ".." by backing up. */
78                                         while ((--new_path)[-1] != '/');
79                                         continue;
80                                 }
81                         }
82                 }
83                 /* Safely copy the next pathname component. */
84                 while (*path != '\0' && *path != '/') {
85                         if (path > max_path) {
86                                 errno = ENAMETOOLONG;
87                                 return NULL;
88                         }
89                         *new_path++ = *path++;
90                 }
91 #ifdef S_IFLNK
92                 /* Protect against infinite loops. */
93                 if (readlinks++ > MAX_READLINKS) {
94                         errno = ELOOP;
95                         return NULL;
96                 }
97                 /* See if latest pathname component is a symlink. */
98                 *new_path = '\0';
99                 n = readlink(resolved_path, link_path, PATH_MAX - 1);
100                 if (n < 0) {
101                         /* EINVAL means the file exists but isn't a symlink. */
102                         if (errno != EINVAL)
103                                 return NULL;
104                 }
105                 else {
106                         /* Note: readlink doesn't add the null byte. */
107                         link_path[n] = '\0';
108                         if (*link_path == '/')
109                                 /* Start over for an absolute symlink. */
110                                 new_path = resolved_path;
111                         else
112                                 /* Otherwise back up over this component. */
113                                 while (*(--new_path) != '/');
114                         /* Safe sex check. */
115                         if (strlen(path) + n >= PATH_MAX) {
116                                 errno = ENAMETOOLONG;
117                                 return NULL;
118                         }
119                         /* Insert symlink contents into path. */
120                         strcat(link_path, path);
121                         strcpy(copy_path, link_path);
122                         path = copy_path;
123                 }
124 #endif /* S_IFLNK */
125                 *new_path++ = '/';
126         }
127         /* Delete trailing slash but don't whomp a lone slash. */
128         if (new_path != resolved_path + 1 && new_path[-1] == '/')
129                 new_path--;
130         /* Make sure it's null terminated. */
131         *new_path = '\0';
132         return resolved_path;
133 }