]> sourceware.org Git - lvm2.git/blob - libdm/libdm-string.c
dmsetup: allow --noflush with status/wait for thin
[lvm2.git] / libdm / libdm-string.c
1 /*
2 * Copyright (C) 2006-2012 Red Hat, Inc. All rights reserved.
3 *
4 * This file is part of the device-mapper userspace tools.
5 *
6 * This copyrighted material is made available to anyone wishing to use,
7 * modify, copy, or redistribute it subject to the terms and conditions
8 * of the GNU Lesser General Public License v.2.1.
9 *
10 * You should have received a copy of the GNU Lesser General Public License
11 * along with this program; if not, write to the Free Software Foundation,
12 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
13 */
14
15 #include "dmlib.h"
16
17 #include <ctype.h>
18
19 /*
20 * consume characters while they match the predicate function.
21 */
22 static char *_consume(char *buffer, int (*fn) (int))
23 {
24 while (*buffer && fn(*buffer))
25 buffer++;
26
27 return buffer;
28 }
29
30 static int _isword(int c)
31 {
32 return !isspace(c);
33 }
34
35 /*
36 * Split buffer into NULL-separated words in argv.
37 * Returns number of words.
38 */
39 int dm_split_words(char *buffer, unsigned max,
40 unsigned ignore_comments __attribute__((unused)),
41 char **argv)
42 {
43 unsigned arg;
44
45 for (arg = 0; arg < max; arg++) {
46 buffer = _consume(buffer, isspace);
47 if (!*buffer)
48 break;
49
50 argv[arg] = buffer;
51 buffer = _consume(buffer, _isword);
52
53 if (*buffer) {
54 *buffer = '\0';
55 buffer++;
56 }
57 }
58
59 return arg;
60 }
61
62 /*
63 * Remove hyphen quoting from a component of a name.
64 * NULL-terminates the component and returns start of next component.
65 */
66 static char *_unquote(char *component)
67 {
68 char *c = component;
69 char *o = c;
70 char *r;
71
72 while (*c) {
73 if (*(c + 1)) {
74 if (*c == '-') {
75 if (*(c + 1) == '-')
76 c++;
77 else
78 break;
79 }
80 }
81 *o = *c;
82 o++;
83 c++;
84 }
85
86 r = (*c) ? c + 1 : c;
87 *o = '\0';
88
89 return r;
90 }
91
92 int dm_split_lvm_name(struct dm_pool *mem, const char *dmname,
93 char **vgname, char **lvname, char **layer)
94 {
95 if (mem && !(*vgname = dm_pool_strdup(mem, dmname)))
96 return 0;
97
98 _unquote(*layer = _unquote(*lvname = _unquote(*vgname)));
99
100 return 1;
101 }
102
103 /*
104 * On error, up to glibc 2.0.6, snprintf returned -1 if buffer was too small;
105 * From glibc 2.1 it returns number of chars (excl. trailing null) that would
106 * have been written had there been room.
107 *
108 * dm_snprintf reverts to the old behaviour.
109 */
110 int dm_snprintf(char *buf, size_t bufsize, const char *format, ...)
111 {
112 int n;
113 va_list ap;
114
115 va_start(ap, format);
116 n = vsnprintf(buf, bufsize, format, ap);
117 va_end(ap);
118
119 if (n < 0 || ((unsigned) n + 1 > bufsize))
120 return -1;
121
122 return n;
123 }
124
125 const char *dm_basename(const char *path)
126 {
127 const char *p = strrchr(path, '/');
128
129 return p ? p + 1 : path;
130 }
131
132 int dm_asprintf(char **result, const char *format, ...)
133 {
134 int i, n, size = 16;
135 va_list ap;
136 char *buf = dm_malloc(size);
137
138 *result = 0;
139
140 if (!buf)
141 return -1;
142
143 for (i = 0;; i++) {
144 va_start(ap, format);
145 n = vsnprintf(buf, size, format, ap);
146 va_end(ap);
147
148 if (0 <= n && n < size)
149 break;
150
151 dm_free(buf);
152 /* Up to glibc 2.0.6 returns -1 */
153 size = (n < 0) ? size * 2 : n + 1;
154 if (!(buf = dm_malloc(size)))
155 return -1;
156 }
157
158 if (i > 1) {
159 /* Reallocating more then once? */
160 if (!(*result = dm_strdup(buf))) {
161 dm_free(buf);
162 return -1;
163 }
164 dm_free(buf);
165 } else
166 *result = buf;
167
168 return n + 1;
169 }
170
171 /*
172 * Count occurences of 'c' in 'str' until we reach a null char.
173 *
174 * Returns:
175 * len - incremented for each char we encounter.
176 * count - number of occurrences of 'c' and 'c2'.
177 */
178 static void _count_chars(const char *str, size_t *len, int *count,
179 const int c1, const int c2)
180 {
181 const char *ptr;
182
183 for (ptr = str; *ptr; ptr++, (*len)++)
184 if (*ptr == c1 || *ptr == c2)
185 (*count)++;
186 }
187
188 /*
189 * Count occurrences of 'c' in 'str' of length 'size'.
190 *
191 * Returns:
192 * Number of occurrences of 'c'
193 */
194 unsigned dm_count_chars(const char *str, size_t len, const int c)
195 {
196 size_t i;
197 unsigned count = 0;
198
199 for (i = 0; i < len; i++)
200 if (str[i] == c)
201 count++;
202
203 return count;
204 }
205
206 /*
207 * Length of string after escaping double quotes and backslashes.
208 */
209 size_t dm_escaped_len(const char *str)
210 {
211 size_t len = 1;
212 int count = 0;
213
214 _count_chars(str, &len, &count, '\"', '\\');
215
216 return count + len;
217 }
218
219 /*
220 * Copies a string, quoting orig_char with quote_char.
221 * Optionally also quote quote_char.
222 */
223 static void _quote_characters(char **out, const char *src,
224 const int orig_char, const int quote_char,
225 int quote_quote_char)
226 {
227 while (*src) {
228 if (*src == orig_char ||
229 (*src == quote_char && quote_quote_char))
230 *(*out)++ = quote_char;
231
232 *(*out)++ = *src++;
233 }
234 }
235
236 static void _unquote_one_character(char *src, const char orig_char,
237 const char quote_char)
238 {
239 char *out;
240 char s, n;
241
242 /* Optimise for the common case where no changes are needed. */
243 while ((s = *src++)) {
244 if (s == quote_char &&
245 ((n = *src) == orig_char || n == quote_char)) {
246 out = src++;
247 *(out - 1) = n;
248
249 while ((s = *src++)) {
250 if (s == quote_char &&
251 ((n = *src) == orig_char || n == quote_char)) {
252 s = n;
253 src++;
254 }
255 *out = s;
256 out++;
257 }
258
259 *out = '\0';
260 return;
261 }
262 }
263 }
264
265 /*
266 * Unquote each character given in orig_char array and unquote quote_char
267 * as well. Also save the first occurrence of each character from orig_char
268 * that was found unquoted in arr_substr_first_unquoted array. This way we can
269 * process several characters in one go.
270 */
271 static void _unquote_characters(char *src, const char *orig_chars,
272 size_t num_orig_chars,
273 const char quote_char,
274 char *arr_substr_first_unquoted[])
275 {
276 char *out = src;
277 char c, s, n;
278 unsigned i;
279
280 while ((s = *src++)) {
281 for (i = 0; i < num_orig_chars; i++) {
282 c = orig_chars[i];
283 if (s == quote_char &&
284 ((n = *src) == c || n == quote_char)) {
285 s = n;
286 src++;
287 break;
288 }
289 if (arr_substr_first_unquoted && (s == c) &&
290 !arr_substr_first_unquoted[i])
291 arr_substr_first_unquoted[i] = out;
292 };
293 *out++ = s;
294 }
295
296 *out = '\0';
297 }
298
299 /*
300 * Copies a string, quoting hyphens with hyphens.
301 */
302 static void _quote_hyphens(char **out, const char *src)
303 {
304 _quote_characters(out, src, '-', '-', 0);
305 }
306
307 /*
308 * <vg>-<lv>-<layer> or if !layer just <vg>-<lv>.
309 */
310 char *dm_build_dm_name(struct dm_pool *mem, const char *vgname,
311 const char *lvname, const char *layer)
312 {
313 size_t len = 1;
314 int hyphens = 1;
315 char *r, *out;
316
317 _count_chars(vgname, &len, &hyphens, '-', 0);
318 _count_chars(lvname, &len, &hyphens, '-', 0);
319
320 if (layer && *layer) {
321 _count_chars(layer, &len, &hyphens, '-', 0);
322 hyphens++;
323 }
324
325 len += hyphens;
326
327 if (!(r = dm_pool_alloc(mem, len))) {
328 log_error("build_dm_name: Allocation failed for %" PRIsize_t
329 " for %s %s %s.", len, vgname, lvname, layer);
330 return NULL;
331 }
332
333 out = r;
334 _quote_hyphens(&out, vgname);
335 *out++ = '-';
336 _quote_hyphens(&out, lvname);
337
338 if (layer && *layer) {
339 /* No hyphen if the layer begins with _ e.g. _mlog */
340 if (*layer != '_')
341 *out++ = '-';
342 _quote_hyphens(&out, layer);
343 }
344 *out = '\0';
345
346 return r;
347 }
348
349 char *dm_build_dm_uuid(struct dm_pool *mem, const char *uuid_prefix, const char *lvid, const char *layer)
350 {
351 char *dmuuid;
352 size_t len;
353
354 if (!layer)
355 layer = "";
356
357 len = strlen(uuid_prefix) + strlen(lvid) + strlen(layer) + 2;
358
359 if (!(dmuuid = dm_pool_alloc(mem, len))) {
360 log_error("build_dm_name: Allocation failed for %" PRIsize_t
361 " %s %s.", len, lvid, layer);
362 return NULL;
363 }
364
365 sprintf(dmuuid, "%s%s%s%s", uuid_prefix, lvid, (*layer) ? "-" : "", layer);
366
367 return dmuuid;
368 }
369
370 /*
371 * Copies a string, quoting double quotes with backslashes.
372 */
373 char *dm_escape_double_quotes(char *out, const char *src)
374 {
375 char *buf = out;
376
377 _quote_characters(&buf, src, '\"', '\\', 1);
378 *buf = '\0';
379
380 return out;
381 }
382
383 /*
384 * Undo quoting in situ.
385 */
386 void dm_unescape_double_quotes(char *src)
387 {
388 _unquote_one_character(src, '\"', '\\');
389 }
390
391 /*
392 * Unescape colons and "at" signs in situ and save the substrings
393 * starting at the position of the first unescaped colon and the
394 * first unescaped "at" sign. This is normally used to unescape
395 * device names used as PVs.
396 */
397 void dm_unescape_colons_and_at_signs(char *src,
398 char **substr_first_unquoted_colon,
399 char **substr_first_unquoted_at_sign)
400 {
401 const char *orig_chars = ":@";
402 char *arr_substr_first_unquoted[] = {NULL, NULL, NULL};
403
404 _unquote_characters(src, orig_chars, 2, '\\', arr_substr_first_unquoted);
405
406 if (substr_first_unquoted_colon)
407 *substr_first_unquoted_colon = arr_substr_first_unquoted[0];
408
409 if (substr_first_unquoted_at_sign)
410 *substr_first_unquoted_at_sign = arr_substr_first_unquoted[1];
411 }
412
413 int dm_strncpy(char *dest, const char *src, size_t n)
414 {
415 if (memccpy(dest, src, 0, n))
416 return 1;
417
418 if (n > 0)
419 dest[n - 1] = '\0';
420
421 return 0;
422 }
This page took 0.052699 seconds and 5 git commands to generate.