1 #include <stdio.h>
2 #include <string.h>
3 #include <stdlib.h>
4 #include <unistd.h>
5 #include <path_helper.h>
6 
up_one_level(char * s)7 static int up_one_level(char *s)
8 {
9     char *tail;
10 
11     if (!s)
12         return -1;
13 
14     tail = s + strlen(s) - 1;
15     if (*tail == '/')
16         tail--;
17 
18     while (*tail != '\0' && *tail != '/')
19         tail--;
20 
21     if (*tail == '\0') {
22         return -1;
23     } else {
24         *(tail + 1) = '\0';
25         return 0;
26     }
27 }
28 
get_realpath(const char * path,char * resolved_path,unsigned int len)29 char *get_realpath(const char *path, char *resolved_path, unsigned int len)
30 {
31     char *ret, *p = (char *)path, *r = resolved_path;
32 
33     if (!path || !r || len < 1)
34         return NULL;
35 
36     memset(r, 0, len);
37 
38     // deal with heading char
39     if (p[0] != '/') {
40         // relative path
41         ret = getcwd(r, len);
42         if (!ret)
43             return NULL;
44 
45         // add tailing '/' if no
46         if (r[strlen(r) - 1] != '/') {
47             r[strlen(r)] = '/';
48         }
49 
50         r += strlen(r);
51     } else {
52         // absolute path
53         r[0] = '/';
54         r++;
55     }
56 
57     // iterate to exclude '.', '..'. '/'
58     while (*p != '\0') {
59         while (*p == '/')
60             p++;
61         if (*p == '\0')
62             break;
63 
64         if (*p == '.') {
65             p++;
66             // end with '.'
67             if (*p == '\0')
68                 break;
69 
70             if (*p == '.') {
71                 // '..' or '../'
72                 if ((*(p + 1) != '/') && (*(p + 1) != '\0')) {
73                     printf("Invalid path %s\r\n", path);
74                     return NULL;
75                 } else {
76                     // '..' case
77                     p++;
78                     // if (*p == '/') {
79                     if (up_one_level(resolved_path) != 0) {
80                         printf("Failed to go up now. Invalid path %s\r\n", path);
81                         return NULL;
82                     }
83 
84                     r = resolved_path + strlen(resolved_path);
85                     // }
86 
87                     // end with '.'
88                     if (*p == '\0') {
89                         break;
90                     }
91                 }
92             } else {
93                 if (*p == '/' || *p == '\0') {
94                     p++;
95                 } else {
96                     // '.xxx' might be hidden file or dir
97                     p--;
98                     goto copy_valid;
99                 }
100             }
101         }
102 
103         while (*p == '/')
104             p++;
105         if (*p == '\0')
106             break;
107 
108         // if another round of ./.., just continue
109         if (*p == '.')
110             continue;
111 
112 copy_valid:
113         // path string may be found now, save to r
114         while ((*p != '/') && (*p != '\0'))
115             *r++ = *p++;
116 
117         // add taling '/' if necessary
118         if (*(r - 1) != '/') {
119             *r++ = '/';
120         }
121     }
122 
123     /**
124      * considering "cd ../config" for tab key case,
125      * we need set string EOF avoid out of control.
126      */
127     *r = '\0';
128 
129     // exclude the tailing '/', just in case it is a file
130     if ((resolved_path[strlen(resolved_path) - 1] == '/') &&
131         (strlen(resolved_path) != 1)) {
132         resolved_path[strlen(resolved_path) - 1] = '\0';
133     }
134 
135     return resolved_path;
136 }
137