2 * Copyright (c) 2008 Jakub Jermar
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40 #include <vfs/canonify.h>
42 /** Token types used for tokenization of path. */
58 /** Fake up the TK_SLASH token. */
59 static token_t
slash_token(char *start
)
68 /** Given a token, return the next token. */
69 static token_t
next_token(token_t
*cur
)
73 if (cur
->stop
[1] == '\0') {
75 ret
.start
= cur
->stop
+ 1;
79 if (cur
->stop
[1] == '/') {
81 ret
.start
= cur
->stop
+ 1;
85 if (cur
->stop
[1] == '.' && (!cur
->stop
[2] || cur
->stop
[2] == '/')) {
87 ret
.start
= cur
->stop
+ 1;
91 if (cur
->stop
[1] == '.' && cur
->stop
[2] == '.' &&
92 (!cur
->stop
[3] || cur
->stop
[3] == '/')) {
94 ret
.start
= cur
->stop
+ 1;
95 ret
.stop
= cur
->stop
+ 2;
99 for (i
= 1; cur
->stop
[i
] && cur
->stop
[i
] != '/'; i
++)
102 ret
.start
= &cur
->stop
[1];
103 ret
.stop
= &cur
->stop
[i
- 1];
107 /** States used by canonify(). */
120 void (* f
)(token_t
*, token_t
*, token_t
*);
124 * Actions that can be performed when transitioning from one
125 * state of canonify() to another.
127 static void set_first_slash(token_t
*t
, token_t
*tfsl
, token_t
*tlcomp
)
132 static void save_component(token_t
*t
, token_t
*tfsl
, token_t
*tlcomp
)
136 static void terminate_slash(token_t
*t
, token_t
*tfsl
, token_t
*tlcomp
)
138 if (tfsl
->stop
[1]) /* avoid writing to a well-formatted path */
139 tfsl
->stop
[1] = '\0';
141 static void remove_trailing_slash(token_t
*t
, token_t
*tfsl
, token_t
*tlcomp
)
145 /** Eat the extra '/'.
147 * @param t The current TK_SLASH token.
149 static void shift_slash(token_t
*t
, token_t
*tfsl
, token_t
*tlcomp
)
152 char *q
= t
->stop
+ 1;
153 while ((*p
++ = *q
++))
156 /** Eat the extra '.'.
158 * @param t The current TK_DOT token.
160 static void shift_dot(token_t
*t
, token_t
*tfsl
, token_t
*tlcomp
)
163 char *q
= t
->stop
+ 1;
164 while ((*p
++ = *q
++))
167 /** Collapse the TK_COMP TK_SLASH TK_DOTDOT pattern.
169 * @param t The current TK_DOTDOT token.
170 * @param tlcomp The last TK_COMP token.
172 static void shift_dotdot(token_t
*t
, token_t
*tfsl
, token_t
*tlcomp
)
174 char *p
= tlcomp
->start
;
175 char *q
= t
->stop
+ 1;
176 while ((*p
++ = *q
++))
180 /** Transition function for canonify(). */
181 static change_state_t trans
[4][6] = {
185 .f
= set_first_slash
,
211 .f
= set_first_slash
,
227 .f
= terminate_slash
,
279 .f
= remove_trailing_slash
,
288 /** Canonify a file system path.
290 * A file system path is canonical, if the following holds:
292 * 1) the path is absolute
293 * (i.e. a/b/c is not canonical)
294 * 2) there is no trailing slash in the path if it has components
295 * (i.e. /a/b/c/ is not canonical)
296 * 3) there is no extra slash in the path
297 * (i.e. /a//b/c is not canonical)
298 * 4) there is no '.' component in the path
299 * (i.e. /a/./b/c is not canonical)
300 * 5) there is no '..' component in the path
301 * (i.e. /a/b/../c is not canonical)
303 * This function makes a potentially non-canonical file system path canonical.
304 * It works in-place and requires a NULL-terminated input string.
306 * @param path Path to be canonified.
307 * @param lenp Pointer where the length of the final path will be
308 * stored. Can be NULL.
310 * @return Canonified path or NULL on failure.
312 char *canonify(char *path
, size_t *lenp
)
316 token_t tfsl
; /* first slash */
317 token_t tlcomp
; /* last component */
320 tfsl
= slash_token(path
);
325 while (state
!= S_ACCEPT
&& state
!= S_RESTART
&& state
!= S_REJECT
) {
326 if (trans
[state
][t
.kind
].f
)
327 trans
[state
][t
.kind
].f(&t
, &tfsl
, &tlcomp
);
328 state
= trans
[state
][t
.kind
].s
;
339 *lenp
= (size_t)((tlcomp
.stop
- tfsl
.start
) + 1);