OSDN Git Service

linux-pci-root: remove an unused assignment
[android-x86/external-efivar.git] / src / path-helpers.c
1 /*
2  * path-helper.c
3  * Copyright 2018 Peter Jones <pjones@redhat.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public License as
7  * published by the Free Software Foundation; either version 2.1 of the
8  * License, or (at your option) any later version.
9  *
10  * This library 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 GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, see
17  * <http://www.gnu.org/licenses/>.
18  */
19 #include "fix_coverity.h"
20
21 #include "efivar.h"
22
23 static bool
24 cinpat(const char c, const char *pat)
25 {
26         for (unsigned int i = 0; pat[i]; i++)
27                 if (pat[i] == c)
28                         return true;
29         return false;
30 }
31
32 static unsigned int
33 strxcspn(const char *s, const char *pattern)
34 {
35         unsigned int i;
36         for (i = 0; s[i]; i++) {
37                 if (!cinpat(s[i], pattern))
38                         break;
39         }
40         return i;
41 }
42
43 struct span {
44         const char *pos;
45         size_t len;
46 };
47
48 /*
49  * count how many parts of a path there are, with some caveats:
50  * a leading / is one because it's a directory, but all other slashes are
51  * treated as separators, so i.e.:
52  * 1: /
53  * 2: /foo foo/bar foo/bar/
54  * 3: /foo/bar /foo/bar/ foo/bar/baz
55  *
56  * the usage model here is 1 pass to count, one allocation, one pass to
57  * separate.
58  */
59 unsigned int HIDDEN
60 count_spans(const char *str, const char *reject, unsigned int *chars)
61 {
62         unsigned int s = 0, c = 0, pos = 0;
63
64         if (str[0] == '/') {
65                 s += 1;
66                 c += 2;
67                 pos += 1;
68         }
69
70         while (str[pos]) {
71                 unsigned int n;
72
73                 n = strcspn(str + pos, reject);
74                 if (n) {
75                         s += 1;
76                         c += n + 1;
77                         pos += n;
78                 }
79
80                 pos += strxcspn(str + pos, reject);
81         }
82
83         if (chars)
84                 *chars = c;
85         return s;
86 }
87
88 void HIDDEN
89 fill_spans(const char *str, const char *reject, void *spanbuf)
90 {
91         struct span *spans = (struct span *)spanbuf;
92         struct span *span = spans;
93         unsigned int pos = 0;
94
95         if (str[0] == '/') {
96                 span->pos = str;
97                 span->len = 1;
98                 span++;
99                 pos += 1;
100         }
101
102         while (str[pos]) {
103                 unsigned int n;
104
105                 n = strcspn(str + pos, reject);
106                 if (n) {
107                         span->pos = str + pos;
108                         span->len = n;
109                         span++;
110                         pos += n;
111                 }
112
113                 pos += strxcspn(str + pos, reject);
114         }
115         span->pos = NULL;
116         span->len = 0;
117 }
118
119 #define split_spans(str, reject)                                        \
120         ({                                                              \
121                 struct span *ret_ = NULL;                               \
122                 unsigned int s_, c_;                                    \
123                                                                         \
124                 s_ = count_spans(str, "/", &c_);                        \
125                 if (s_) {                                               \
126                         ret_ = alloca(sizeof(struct span[s_+1]));       \
127                         if (ret_)                                       \
128                                 fill_spans(str, reject, ret_);          \
129                 } else {                                                \
130                         errno = 0;                                      \
131                 }                                                       \
132                 ret_;                                                   \
133         })
134
135 int HIDDEN
136 find_path_segment(const char *path, int segment, const char **pos, size_t *len)
137 {
138         struct span *span, *last;
139         int nspans = 0;
140
141         if (!pos || !len) {
142                 errno = EINVAL;
143                 return -1;
144         }
145
146         span = split_spans(path, "/");
147         if (!span) {
148                 if (errno)
149                         return -1;
150                 *pos = NULL;
151                 *len = 0;
152                 return 0;
153         }
154
155         for (last = span; last->pos; last++)
156                 nspans += 1;
157
158         if (segment < 0)
159                 segment = nspans + segment;
160
161         if (nspans < 1 || segment < 0 || segment >= nspans) {
162                 errno = ENOENT;
163                 return -1;
164         }
165
166         for (int i = 0; i < segment; i++)
167                 span++;
168
169         *pos = span->pos;
170         *len = span->len;
171         return 0;
172 }
173
174 // vim:fenc=utf-8:tw=75:et