wimlib-imagex: improve error message
[wimlib] / src / paths.c
1 /*
2  * paths.c - Path manipulation routines
3  */
4
5 /*
6  * Copyright (C) 2012, 2013 Eric Biggers
7  *
8  * This file is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU Lesser General Public License as published by the Free
10  * Software Foundation; either version 3 of the License, or (at your option) any
11  * later version.
12  *
13  * This file is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this file; if not, see http://www.gnu.org/licenses/.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 #endif
25
26 #include <string.h>
27
28 #include "wimlib.h"
29 #include "wimlib/paths.h"
30 #include "wimlib/util.h"
31
32 /* Like the basename() function, but does not modify @path; it just returns a
33  * pointer to it.  This assumes the path separator is the
34  * OS_PREFERRED_PATH_SEPARATOR.  */
35 const tchar *
36 path_basename(const tchar *path)
37 {
38         return path_basename_with_len(path, tstrlen(path));
39 }
40
41 /* Like path_basename(), but take an explicit string length.  */
42 const tchar *
43 path_basename_with_len(const tchar *path, size_t len)
44 {
45         const tchar *p = &path[len];
46
47         do {
48                 if (p == path)
49                         return &path[len];
50         } while (*--p == OS_PREFERRED_PATH_SEPARATOR);
51
52         do {
53                 if (p == path)
54                         return &path[0];
55         } while (*--p != OS_PREFERRED_PATH_SEPARATOR);
56
57         return ++p;
58 }
59
60
61 /* Returns a pointer to the part of @path following the first colon in the last
62  * path component, or NULL if the last path component does not contain a colon
63  * or has no characters following the first colon.  */
64 const tchar *
65 path_stream_name(const tchar *path)
66 {
67         const tchar *base = path_basename(path);
68         const tchar *stream_name = tstrchr(base, T(':'));
69         if (stream_name == NULL || *(stream_name + 1) == T('\0'))
70                 return NULL;
71         else
72                 return stream_name + 1;
73 }
74
75 /* Collapse and translate path separators, and strip trailing slashes.  Doesn't
76  * add or delete a leading slash.
77  *
78  * @in may alias @out.
79  */
80 void
81 do_canonicalize_path(const tchar *in, tchar *out)
82 {
83         tchar *orig_out = out;
84
85         while (*in) {
86                 if (is_any_path_separator(*in)) {
87                         /* Collapse multiple path separators into one  */
88                         *out++ = WIM_PATH_SEPARATOR;
89                         do {
90                                 in++;
91                         } while (is_any_path_separator(*in));
92                 } else {
93                         /* Copy non-path-separator character  */
94                         *out++ = *in++;
95                 }
96         }
97
98         /* Remove trailing slash if existent  */
99         if (out - orig_out > 1 && *(out - 1) == WIM_PATH_SEPARATOR)
100                 --out;
101
102         *out = T('\0');
103 }
104
105 /*
106  * canonicalize_wim_path() - Given a user-provided path to a file within a WIM
107  * image, translate it into a "canonical" path.
108  *
109  * - Translate both types of slash into a consistent type (WIM_PATH_SEPARATOR).
110  * - Collapse path separators.
111  * - Add leading slash if missing.
112  * - Strip trailing slashes.
113  *
114  * Examples (with WIM_PATH_SEPARATOR == '/'):
115  *
116  *              => /            [ either NULL or empty string ]
117  * /            => /
118  * \            => /
119  * hello        => /hello
120  * \hello       => /hello
121  * \hello       => /hello
122  * /hello/      => /hello
123  * \hello/      => /hello
124  * /hello//1    => /hello/1
125  * \\hello\\1\\ => /hello/1
126  */
127 tchar *
128 canonicalize_wim_path(const tchar *wim_path)
129 {
130         const tchar *in;
131         tchar *out;
132         tchar *result;
133
134         in = wim_path;
135         if (!in)
136                 in = T("");
137
138         result = MALLOC((1 + tstrlen(in) + 1) * sizeof(result[0]));
139         if (!result)
140                 return NULL;
141
142         out = result;
143
144         /* Add leading slash if missing  */
145         if (!is_any_path_separator(*in))
146                 *out++ = WIM_PATH_SEPARATOR;
147
148         do_canonicalize_path(in, out);
149
150         return result;
151 }