]> sourceware.org Git - lvm2.git/blob - libdm/libdm-string.c
man: document allocation process in lvm.8
[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_vasprintf(char **result, const char *format, va_list aq)
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_copy(ap, aq);
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 int dm_asprintf(char **result, const char *format, ...)
172 {
173 int r;
174 va_list ap;
175 va_start(ap, format);
176 r = dm_vasprintf(result, format, ap);
177 va_end(ap);
178 return r;
179 }
180
181 /*
182 * Count occurences of 'c' in 'str' until we reach a null char.
183 *
184 * Returns:
185 * len - incremented for each char we encounter.
186 * count - number of occurrences of 'c' and 'c2'.
187 */
188 static void _count_chars(const char *str, size_t *len, int *count,
189 const int c1, const int c2)
190 {
191 const char *ptr;
192
193 for (ptr = str; *ptr; ptr++, (*len)++)
194 if (*ptr == c1 || *ptr == c2)
195 (*count)++;
196 }
197
198 /*
199 * Count occurrences of 'c' in 'str' of length 'size'.
200 *
201 * Returns:
202 * Number of occurrences of 'c'
203 */
204 unsigned dm_count_chars(const char *str, size_t len, const int c)
205 {
206 size_t i;
207 unsigned count = 0;
208
209 for (i = 0; i < len; i++)
210 if (str[i] == c)
211 count++;
212
213 return count;
214 }
215
216 /*
217 * Length of string after escaping double quotes and backslashes.
218 */
219 size_t dm_escaped_len(const char *str)
220 {
221 size_t len = 1;
222 int count = 0;
223
224 _count_chars(str, &len, &count, '\"', '\\');
225
226 return count + len;
227 }
228
229 /*
230 * Copies a string, quoting orig_char with quote_char.
231 * Optionally also quote quote_char.
232 */
233 static void _quote_characters(char **out, const char *src,
234 const int orig_char, const int quote_char,
235 int quote_quote_char)
236 {
237 while (*src) {
238 if (*src == orig_char ||
239 (*src == quote_char && quote_quote_char))
240 *(*out)++ = quote_char;
241
242 *(*out)++ = *src++;
243 }
244 }
245
246 static void _unquote_one_character(char *src, const char orig_char,
247 const char quote_char)
248 {
249 char *out;
250 char s, n;
251
252 /* Optimise for the common case where no changes are needed. */
253 while ((s = *src++)) {
254 if (s == quote_char &&
255 ((n = *src) == orig_char || n == quote_char)) {
256 out = src++;
257 *(out - 1) = n;
258
259 while ((s = *src++)) {
260 if (s == quote_char &&
261 ((n = *src) == orig_char || n == quote_char)) {
262 s = n;
263 src++;
264 }
265 *out = s;
266 out++;
267 }
268
269 *out = '\0';
270 return;
271 }
272 }
273 }
274
275 /*
276 * Unquote each character given in orig_char array and unquote quote_char
277 * as well. Also save the first occurrence of each character from orig_char
278 * that was found unquoted in arr_substr_first_unquoted array. This way we can
279 * process several characters in one go.
280 */
281 static void _unquote_characters(char *src, const char *orig_chars,
282 size_t num_orig_chars,
283 const char quote_char,
284 char *arr_substr_first_unquoted[])
285 {
286 char *out = src;
287 char c, s, n;
288 unsigned i;
289
290 while ((s = *src++)) {
291 for (i = 0; i < num_orig_chars; i++) {
292 c = orig_chars[i];
293 if (s == quote_char &&
294 ((n = *src) == c || n == quote_char)) {
295 s = n;
296 src++;
297 break;
298 }
299 if (arr_substr_first_unquoted && (s == c) &&
300 !arr_substr_first_unquoted[i])
301 arr_substr_first_unquoted[i] = out;
302 };
303 *out++ = s;
304 }
305
306 *out = '\0';
307 }
308
309 /*
310 * Copies a string, quoting hyphens with hyphens.
311 */
312 static void _quote_hyphens(char **out, const char *src)
313 {
314 _quote_characters(out, src, '-', '-', 0);
315 }
316
317 /*
318 * <vg>-<lv>-<layer> or if !layer just <vg>-<lv>.
319 */
320 char *dm_build_dm_name(struct dm_pool *mem, const char *vgname,
321 const char *lvname, const char *layer)
322 {
323 size_t len = 1;
324 int hyphens = 1;
325 char *r, *out;
326
327 _count_chars(vgname, &len, &hyphens, '-', 0);
328 _count_chars(lvname, &len, &hyphens, '-', 0);
329
330 if (layer && *layer) {
331 _count_chars(layer, &len, &hyphens, '-', 0);
332 hyphens++;
333 }
334
335 len += hyphens;
336
337 if (!(r = dm_pool_alloc(mem, len))) {
338 log_error("build_dm_name: Allocation failed for %" PRIsize_t
339 " for %s %s %s.", len, vgname, lvname, layer);
340 return NULL;
341 }
342
343 out = r;
344 _quote_hyphens(&out, vgname);
345 *out++ = '-';
346 _quote_hyphens(&out, lvname);
347
348 if (layer && *layer) {
349 /* No hyphen if the layer begins with _ e.g. _mlog */
350 if (*layer != '_')
351 *out++ = '-';
352 _quote_hyphens(&out, layer);
353 }
354 *out = '\0';
355
356 return r;
357 }
358
359 char *dm_build_dm_uuid(struct dm_pool *mem, const char *uuid_prefix, const char *lvid, const char *layer)
360 {
361 char *dmuuid;
362 size_t len;
363
364 if (!layer)
365 layer = "";
366
367 len = strlen(uuid_prefix) + strlen(lvid) + strlen(layer) + 2;
368
369 if (!(dmuuid = dm_pool_alloc(mem, len))) {
370 log_error("build_dm_name: Allocation failed for %" PRIsize_t
371 " %s %s.", len, lvid, layer);
372 return NULL;
373 }
374
375 sprintf(dmuuid, "%s%s%s%s", uuid_prefix, lvid, (*layer) ? "-" : "", layer);
376
377 return dmuuid;
378 }
379
380 /*
381 * Copies a string, quoting double quotes with backslashes.
382 */
383 char *dm_escape_double_quotes(char *out, const char *src)
384 {
385 char *buf = out;
386
387 _quote_characters(&buf, src, '\"', '\\', 1);
388 *buf = '\0';
389
390 return out;
391 }
392
393 /*
394 * Undo quoting in situ.
395 */
396 void dm_unescape_double_quotes(char *src)
397 {
398 _unquote_one_character(src, '\"', '\\');
399 }
400
401 /*
402 * Unescape colons and "at" signs in situ and save the substrings
403 * starting at the position of the first unescaped colon and the
404 * first unescaped "at" sign. This is normally used to unescape
405 * device names used as PVs.
406 */
407 void dm_unescape_colons_and_at_signs(char *src,
408 char **substr_first_unquoted_colon,
409 char **substr_first_unquoted_at_sign)
410 {
411 const char *orig_chars = ":@";
412 char *arr_substr_first_unquoted[] = {NULL, NULL, NULL};
413
414 _unquote_characters(src, orig_chars, 2, '\\', arr_substr_first_unquoted);
415
416 if (substr_first_unquoted_colon)
417 *substr_first_unquoted_colon = arr_substr_first_unquoted[0];
418
419 if (substr_first_unquoted_at_sign)
420 *substr_first_unquoted_at_sign = arr_substr_first_unquoted[1];
421 }
422
423 int dm_strncpy(char *dest, const char *src, size_t n)
424 {
425 if (memccpy(dest, src, 0, n))
426 return 1;
427
428 if (n > 0)
429 dest[n - 1] = '\0';
430
431 return 0;
432 }
This page took 0.052292 seconds and 5 git commands to generate.