/src/augeas/src/internal.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * internal.c: internal data structures and helpers |
3 | | * |
4 | | * Copyright (C) 2007-2016 David Lutterkort |
5 | | * |
6 | | * This library is free software; you can redistribute it and/or |
7 | | * modify it under the terms of the GNU Lesser General Public |
8 | | * License as published by the Free Software Foundation; either |
9 | | * version 2.1 of the License, or (at your option) any later version. |
10 | | * |
11 | | * This library is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | | * Lesser General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU Lesser General Public |
17 | | * License along with this library; if not, write to the Free Software |
18 | | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
19 | | * |
20 | | * Author: David Lutterkort <dlutter@redhat.com> |
21 | | */ |
22 | | |
23 | | #include <config.h> |
24 | | |
25 | | #include <ctype.h> |
26 | | #include <stdio.h> |
27 | | #include <stdarg.h> |
28 | | #include <locale.h> |
29 | | |
30 | | #include "internal.h" |
31 | | #include "memory.h" |
32 | | #include "fa.h" |
33 | | |
34 | | #ifndef MIN |
35 | 0 | # define MIN(a, b) ((a) < (b) ? (a) : (b)) |
36 | | #endif |
37 | | |
38 | | /* Cap file reads somwhat arbitrarily at 32 MB */ |
39 | 0 | #define MAX_READ_LEN (32*1024*1024) |
40 | | |
41 | 1 | int pathjoin(char **path, int nseg, ...) { |
42 | 1 | va_list ap; |
43 | | |
44 | 1 | va_start(ap, nseg); |
45 | 3 | for (int i=0; i < nseg; i++) { |
46 | 2 | const char *seg = va_arg(ap, const char *); |
47 | 2 | if (seg == NULL) |
48 | 0 | seg = "()"; |
49 | 2 | int len = strlen(seg) + 1; |
50 | | |
51 | 2 | if (*path != NULL) { |
52 | 1 | len += strlen(*path) + 1; |
53 | 1 | if (REALLOC_N(*path, len) == -1) { |
54 | 0 | FREE(*path); |
55 | 0 | va_end(ap); |
56 | 0 | return -1; |
57 | 0 | } |
58 | 1 | if (strlen(*path) == 0 || (*path)[strlen(*path)-1] != SEP) |
59 | 1 | strcat(*path, "/"); |
60 | 1 | if (seg[0] == SEP) |
61 | 0 | seg += 1; |
62 | 1 | strcat(*path, seg); |
63 | 1 | } else { |
64 | 1 | if ((*path = malloc(len)) == NULL) { |
65 | 0 | va_end(ap); |
66 | 0 | return -1; |
67 | 0 | } |
68 | 1 | strcpy(*path, seg); |
69 | 1 | } |
70 | 2 | } |
71 | 1 | va_end(ap); |
72 | 1 | return 0; |
73 | 1 | } |
74 | | |
75 | | /* Like gnulib's fread_file, but read no more than the specified maximum |
76 | | number of bytes. If the length of the input is <= max_len, and |
77 | | upon error while reading that data, it works just like fread_file. |
78 | | |
79 | | Taken verbatim from libvirt's util.c |
80 | | */ |
81 | | |
82 | | static char * |
83 | | fread_file_lim (FILE *stream, size_t max_len, size_t *length) |
84 | 0 | { |
85 | 0 | char *buf = NULL; |
86 | 0 | size_t alloc = 0; |
87 | 0 | size_t size = 0; |
88 | 0 | int save_errno; |
89 | |
|
90 | 0 | for (;;) { |
91 | 0 | size_t count; |
92 | 0 | size_t requested; |
93 | |
|
94 | 0 | if (size + BUFSIZ + 1 > alloc) { |
95 | 0 | char *new_buf; |
96 | |
|
97 | 0 | alloc += alloc / 2; |
98 | 0 | if (alloc < size + BUFSIZ + 1) |
99 | 0 | alloc = size + BUFSIZ + 1; |
100 | |
|
101 | 0 | new_buf = realloc (buf, alloc); |
102 | 0 | if (!new_buf) { |
103 | 0 | save_errno = errno; |
104 | 0 | break; |
105 | 0 | } |
106 | | |
107 | 0 | buf = new_buf; |
108 | 0 | } |
109 | | |
110 | | /* Ensure that (size + requested <= max_len); */ |
111 | 0 | requested = MIN (size < max_len ? max_len - size : 0, |
112 | 0 | alloc - size - 1); |
113 | 0 | count = fread (buf + size, 1, requested, stream); |
114 | 0 | size += count; |
115 | |
|
116 | 0 | if (count != requested || requested == 0) { |
117 | 0 | save_errno = errno; |
118 | 0 | if (ferror (stream)) |
119 | 0 | break; |
120 | 0 | buf[size] = '\0'; |
121 | 0 | *length = size; |
122 | 0 | return buf; |
123 | 0 | } |
124 | 0 | } |
125 | | |
126 | 0 | free (buf); |
127 | 0 | errno = save_errno; |
128 | 0 | return NULL; |
129 | 0 | } |
130 | | |
131 | 0 | char* xfread_file(FILE *fp) { |
132 | 0 | char *result; |
133 | 0 | size_t len; |
134 | |
|
135 | 0 | if (!fp) |
136 | 0 | return NULL; |
137 | | |
138 | 0 | result = fread_file_lim(fp, MAX_READ_LEN, &len); |
139 | |
|
140 | 0 | if (result != NULL |
141 | 0 | && len <= MAX_READ_LEN |
142 | 0 | && (int) len == len) |
143 | 0 | return result; |
144 | | |
145 | 0 | free(result); |
146 | 0 | return NULL; |
147 | 0 | } |
148 | | |
149 | 0 | char* xread_file(const char *path) { |
150 | 0 | FILE *fp; |
151 | 0 | char *result; |
152 | |
|
153 | 0 | fp = fopen(path, "r"); |
154 | 0 | if (!fp) |
155 | 0 | return NULL; |
156 | | |
157 | 0 | result = xfread_file(fp); |
158 | 0 | fclose(fp); |
159 | |
|
160 | 0 | return result; |
161 | 0 | } |
162 | | |
163 | | /* |
164 | | * Escape/unescape of string literals |
165 | | */ |
166 | | static const char *const escape_chars = "\a\b\t\n\v\f\r"; |
167 | | static const char *const escape_names = "abtnvfr"; |
168 | | |
169 | 22.0k | char *unescape(const char *s, int len, const char *extra) { |
170 | 22.0k | size_t size; |
171 | 22.0k | const char *n; |
172 | 22.0k | char *result, *t; |
173 | 22.0k | int i; |
174 | | |
175 | 22.0k | if (len < 0 || len > strlen(s)) |
176 | 0 | len = strlen(s); |
177 | | |
178 | 22.0k | size = 0; |
179 | 12.5M | for (i=0; i < len; i++, size++) { |
180 | 12.5M | if (s[i] == '\\' && strchr(escape_names, s[i+1])) { |
181 | 12 | i += 1; |
182 | 12.5M | } else if (s[i] == '\\' && extra && strchr(extra, s[i+1])) { |
183 | 0 | i += 1; |
184 | 0 | } |
185 | 12.5M | } |
186 | | |
187 | 22.0k | if (ALLOC_N(result, size + 1) < 0) |
188 | 0 | return NULL; |
189 | | |
190 | 12.5M | for (i = 0, t = result; i < len; i++, size++) { |
191 | 12.5M | if (s[i] == '\\' && (n = strchr(escape_names, s[i+1])) != NULL) { |
192 | 12 | *t++ = escape_chars[n - escape_names]; |
193 | 12 | i += 1; |
194 | 12.5M | } else if (s[i] == '\\' && extra && strchr(extra, s[i+1]) != NULL) { |
195 | 0 | *t++ = s[i+1]; |
196 | 0 | i += 1; |
197 | 12.5M | } else { |
198 | 12.5M | *t++ = s[i]; |
199 | 12.5M | } |
200 | 12.5M | } |
201 | 22.0k | return result; |
202 | 22.0k | } |
203 | | |
204 | 9.06k | char *escape(const char *text, int cnt, const char *extra) { |
205 | | |
206 | 9.06k | int len = 0; |
207 | 9.06k | char *esc = NULL, *e; |
208 | | |
209 | 9.06k | if (cnt < 0 || cnt > strlen(text)) |
210 | 9.06k | cnt = strlen(text); |
211 | | |
212 | 123M | for (int i=0; i < cnt; i++) { |
213 | 123M | if (text[i] && (strchr(escape_chars, text[i]) != NULL)) |
214 | 69.6M | len += 2; /* Escaped as '\x' */ |
215 | 53.9M | else if (text[i] && extra && (strchr(extra, text[i]) != NULL)) |
216 | 23.4k | len += 2; /* Escaped as '\x' */ |
217 | 53.8M | else if (! isprint(text[i])) |
218 | 835k | len += 4; /* Escaped as '\ooo' */ |
219 | 53.0M | else |
220 | 53.0M | len += 1; |
221 | 123M | } |
222 | 9.06k | if (ALLOC_N(esc, len+1) < 0) |
223 | 0 | return NULL; |
224 | 9.06k | e = esc; |
225 | 123M | for (int i=0; i < cnt; i++) { |
226 | 123M | char *p; |
227 | 123M | if (text[i] && ((p = strchr(escape_chars, text[i])) != NULL)) { |
228 | 69.6M | *e++ = '\\'; |
229 | 69.6M | *e++ = escape_names[p - escape_chars]; |
230 | 69.6M | } else if (text[i] && extra && (strchr(extra, text[i]) != NULL)) { |
231 | 23.4k | *e++ = '\\'; |
232 | 23.4k | *e++ = text[i]; |
233 | 53.8M | } else if (! isprint(text[i])) { |
234 | 835k | sprintf(e, "\\%03o", (unsigned char) text[i]); |
235 | 835k | e += 4; |
236 | 53.0M | } else { |
237 | 53.0M | *e++ = text[i]; |
238 | 53.0M | } |
239 | 123M | } |
240 | 9.06k | return esc; |
241 | 9.06k | } |
242 | | |
243 | 0 | int print_chars(FILE *out, const char *text, int cnt) { |
244 | 0 | int total = 0; |
245 | 0 | char *esc; |
246 | |
|
247 | 0 | if (text == NULL) { |
248 | 0 | fprintf(out, "nil"); |
249 | 0 | return 3; |
250 | 0 | } |
251 | 0 | if (cnt < 0) |
252 | 0 | cnt = strlen(text); |
253 | |
|
254 | 0 | esc = escape(text, cnt, "\""); |
255 | 0 | total = strlen(esc); |
256 | 0 | if (out != NULL) |
257 | 0 | fprintf(out, "%s", esc); |
258 | 0 | free(esc); |
259 | |
|
260 | 0 | return total; |
261 | 0 | } |
262 | | |
263 | 0 | char *format_pos(const char *text, int pos) { |
264 | 0 | static const int window = 28; |
265 | 0 | char *buf = NULL, *left = NULL, *right = NULL; |
266 | 0 | int before = pos; |
267 | 0 | int llen, rlen; |
268 | 0 | int r; |
269 | |
|
270 | 0 | if (before > window) |
271 | 0 | before = window; |
272 | 0 | left = escape(text + pos - before, before, NULL); |
273 | 0 | if (left == NULL) |
274 | 0 | goto done; |
275 | 0 | right = escape(text + pos, window, NULL); |
276 | 0 | if (right == NULL) |
277 | 0 | goto done; |
278 | | |
279 | 0 | llen = strlen(left); |
280 | 0 | rlen = strlen(right); |
281 | 0 | if (llen < window && rlen < window) { |
282 | 0 | r = asprintf(&buf, "%*s%s|=|%s%-*s\n", window - llen, "<", left, |
283 | 0 | right, window - rlen, ">"); |
284 | 0 | } else if (strlen(left) < window) { |
285 | 0 | r = asprintf(&buf, "%*s%s|=|%s>\n", window - llen, "<", left, right); |
286 | 0 | } else if (strlen(right) < window) { |
287 | 0 | r = asprintf(&buf, "<%s|=|%s%-*s\n", left, right, window - rlen, ">"); |
288 | 0 | } else { |
289 | 0 | r = asprintf(&buf, "<%s|=|%s>\n", left, right); |
290 | 0 | } |
291 | 0 | if (r < 0) { |
292 | 0 | buf = NULL; |
293 | 0 | } |
294 | |
|
295 | 0 | done: |
296 | 0 | free(left); |
297 | 0 | free(right); |
298 | 0 | return buf; |
299 | 0 | } |
300 | | |
301 | 0 | void print_pos(FILE *out, const char *text, int pos) { |
302 | 0 | char *format = format_pos(text, pos); |
303 | |
|
304 | 0 | if (format != NULL) { |
305 | 0 | fputs(format, out); |
306 | 0 | FREE(format); |
307 | 0 | } |
308 | 0 | } |
309 | | |
310 | 0 | int __aug_init_memstream(struct memstream *ms) { |
311 | 0 | MEMZERO(ms, 1); |
312 | 0 | #if HAVE_OPEN_MEMSTREAM |
313 | 0 | ms->stream = open_memstream(&(ms->buf), &(ms->size)); |
314 | 0 | return ms->stream == NULL ? -1 : 0; |
315 | | #else |
316 | | ms->stream = tmpfile(); |
317 | | if (ms->stream == NULL) { |
318 | | return -1; |
319 | | } |
320 | | return 0; |
321 | | #endif |
322 | 0 | } |
323 | | |
324 | 0 | int __aug_close_memstream(struct memstream *ms) { |
325 | | #if !HAVE_OPEN_MEMSTREAM |
326 | | rewind(ms->stream); |
327 | | ms->buf = fread_file_lim(ms->stream, MAX_READ_LEN, &(ms->size)); |
328 | | #endif |
329 | 0 | if (fclose(ms->stream) == EOF) { |
330 | 0 | FREE(ms->buf); |
331 | 0 | ms->size = 0; |
332 | 0 | return -1; |
333 | 0 | } |
334 | 0 | return 0; |
335 | 0 | } |
336 | | |
337 | 44.4k | int tree_sibling_index(struct tree *tree) { |
338 | 44.4k | struct tree *siblings = tree->parent->children; |
339 | | |
340 | 44.4k | int cnt = 0, ind = 0; |
341 | | |
342 | 190k | list_for_each(t, siblings) { |
343 | 190k | if (streqv(t->label, tree->label)) { |
344 | 58.4k | cnt += 1; |
345 | 58.4k | if (t == tree) |
346 | 44.4k | ind = cnt; |
347 | 58.4k | } |
348 | 190k | } |
349 | | |
350 | 44.4k | if (cnt > 1) { |
351 | 4.66k | return ind; |
352 | 39.7k | } else { |
353 | 39.7k | return 0; |
354 | 39.7k | } |
355 | 44.4k | } |
356 | | |
357 | 44.4k | char *path_expand(struct tree *tree, const char *ppath) { |
358 | 44.4k | char *path; |
359 | 44.4k | const char *label; |
360 | 44.4k | char *escaped = NULL; |
361 | 44.4k | int r; |
362 | | |
363 | 44.4k | int ind = tree_sibling_index(tree); |
364 | | |
365 | 44.4k | if (ppath == NULL) |
366 | 7.93k | ppath = ""; |
367 | | |
368 | 44.4k | if (tree->label == NULL) |
369 | 0 | label = "(none)"; |
370 | 44.4k | else |
371 | 44.4k | label = tree->label; |
372 | | |
373 | 44.4k | r = pathx_escape_name(label, &escaped); |
374 | 44.4k | if (r < 0) |
375 | 0 | return NULL; |
376 | | |
377 | 44.4k | if (escaped != NULL) |
378 | 510 | label = escaped; |
379 | | |
380 | 44.4k | if (ind > 0) { |
381 | 4.66k | r = asprintf(&path, "%s/%s[%d]", ppath, label, ind); |
382 | 39.7k | } else { |
383 | 39.7k | r = asprintf(&path, "%s/%s", ppath, label); |
384 | 39.7k | } |
385 | | |
386 | 44.4k | free(escaped); |
387 | | |
388 | 44.4k | if (r == -1) |
389 | 0 | return NULL; |
390 | 44.4k | return path; |
391 | 44.4k | } |
392 | | |
393 | 7.93k | char *path_of_tree(struct tree *tree) { |
394 | 7.93k | int depth, i; |
395 | 7.93k | struct tree *t, **anc; |
396 | 7.93k | char *path = NULL; |
397 | | |
398 | 26.7k | for (t = tree, depth = 1; ! ROOT_P(t); depth++, t = t->parent); |
399 | 7.93k | if (ALLOC_N(anc, depth) < 0) |
400 | 0 | return NULL; |
401 | | |
402 | 34.6k | for (t = tree, i = depth - 1; i >= 0; i--, t = t->parent) |
403 | 26.7k | anc[i] = t; |
404 | | |
405 | 34.6k | for (i = 0; i < depth; i++) { |
406 | 26.7k | char *p = path_expand(anc[i], path); |
407 | 26.7k | free(path); |
408 | 26.7k | path = p; |
409 | 26.7k | } |
410 | 7.93k | FREE(anc); |
411 | 7.93k | return path; |
412 | 7.93k | } |
413 | | |
414 | | /* User-facing path cleaning */ |
415 | 29.7k | static char *cleanstr(char *path, const char sep) { |
416 | 29.7k | if (path == NULL || strlen(path) == 0) |
417 | 0 | return path; |
418 | 29.7k | char *e = path + strlen(path) - 1; |
419 | 29.7k | while (e >= path && (*e == sep || isspace(*e))) |
420 | 0 | *e-- = '\0'; |
421 | 29.7k | return path; |
422 | 29.7k | } |
423 | | |
424 | 29.7k | char *cleanpath(char *path) { |
425 | 29.7k | if (path == NULL || strlen(path) == 0) |
426 | 0 | return path; |
427 | 29.7k | if (STREQ(path, "/")) |
428 | 0 | return path; |
429 | 29.7k | return cleanstr(path, SEP); |
430 | 29.7k | } |
431 | | |
432 | 0 | const char *xstrerror(int errnum, char *buf, size_t len) { |
433 | 0 | #ifdef HAVE_STRERROR_R |
434 | 0 | # if defined(__USE_GNU) && defined(__GLIBC__) |
435 | | /* Annoying GNU specific API contract */ |
436 | 0 | return strerror_r(errnum, buf, len); |
437 | | # else |
438 | | strerror_r(errnum, buf, len); |
439 | | return buf; |
440 | | # endif |
441 | | #else |
442 | | int n = snprintf(buf, len, "errno=%d", errnum); |
443 | | return (0 < n && n < len |
444 | | ? buf : "internal error: buffer too small in xstrerror"); |
445 | | #endif |
446 | 0 | } |
447 | | |
448 | 0 | int xasprintf(char **strp, const char *format, ...) { |
449 | 0 | va_list args; |
450 | 0 | int result; |
451 | |
|
452 | 0 | va_start (args, format); |
453 | 0 | result = vasprintf (strp, format, args); |
454 | 0 | va_end (args); |
455 | 0 | if (result < 0) |
456 | 0 | *strp = NULL; |
457 | 0 | return result; |
458 | 0 | } |
459 | | |
460 | | /* From libvirt's src/xen/block_stats.c */ |
461 | 0 | int xstrtoint64(char const *s, int base, int64_t *result) { |
462 | 0 | long long int lli; |
463 | 0 | char *p; |
464 | |
|
465 | 0 | errno = 0; |
466 | 0 | lli = strtoll(s, &p, base); |
467 | 0 | if (errno || !(*p == 0 || *p == '\n') || p == s || (int64_t) lli != lli) |
468 | 0 | return -1; |
469 | 0 | *result = lli; |
470 | 0 | return 0; |
471 | 0 | } |
472 | | |
473 | 0 | void calc_line_ofs(const char *text, size_t pos, size_t *line, size_t *ofs) { |
474 | 0 | *line = 1; |
475 | 0 | *ofs = 0; |
476 | 0 | for (const char *t = text; t < text + pos; t++) { |
477 | 0 | *ofs += 1; |
478 | 0 | if (*t == '\n') { |
479 | 0 | *ofs = 0; |
480 | 0 | *line += 1; |
481 | 0 | } |
482 | 0 | } |
483 | 0 | } |
484 | | |
485 | | #if HAVE_USELOCALE |
486 | 0 | int regexp_c_locale(ATTRIBUTE_UNUSED char **u, ATTRIBUTE_UNUSED size_t *len) { |
487 | | /* On systems with uselocale, we are ok, since we make sure that we |
488 | | * switch to the "C" locale any time we enter through the public API |
489 | | */ |
490 | 0 | return 0; |
491 | 0 | } |
492 | | #else |
493 | | int regexp_c_locale(char **u, size_t *len) { |
494 | | /* Without uselocale, we need to expand character ranges */ |
495 | | int r; |
496 | | char *s = *u; |
497 | | size_t s_len, u_len; |
498 | | if (len == NULL) { |
499 | | len = &u_len; |
500 | | s_len = strlen(s); |
501 | | } else { |
502 | | s_len = *len; |
503 | | } |
504 | | r = fa_expand_char_ranges(s, s_len, u, len); |
505 | | if (r != 0) { |
506 | | *u = s; |
507 | | *len = s_len; |
508 | | } |
509 | | if (r < 0) |
510 | | return -1; |
511 | | /* Syntax errors will be caught when the result is compiled */ |
512 | | if (r > 0) |
513 | | return 0; |
514 | | free(s); |
515 | | return 1; |
516 | | } |
517 | | #endif |
518 | | |
519 | | #if ENABLE_DEBUG |
520 | 0 | bool debugging(const char *category) { |
521 | 0 | const char *debug = getenv("AUGEAS_DEBUG"); |
522 | 0 | const char *s; |
523 | |
|
524 | 0 | if (debug == NULL) |
525 | 0 | return false; |
526 | | |
527 | 0 | for (s = debug; s != NULL; ) { |
528 | 0 | if (STREQLEN(s, category, strlen(category))) |
529 | 0 | return true; |
530 | 0 | s = strchr(s, ':'); |
531 | 0 | if (s != NULL) |
532 | 0 | s+=1; |
533 | 0 | } |
534 | 0 | return false; |
535 | 0 | } |
536 | | |
537 | 0 | FILE *debug_fopen(const char *format, ...) { |
538 | 0 | va_list ap; |
539 | 0 | FILE *result = NULL; |
540 | 0 | const char *dir; |
541 | 0 | char *name = NULL, *path = NULL; |
542 | 0 | int r; |
543 | |
|
544 | 0 | dir = getenv("AUGEAS_DEBUG_DIR"); |
545 | 0 | if (dir == NULL) |
546 | 0 | goto error; |
547 | | |
548 | 0 | va_start(ap, format); |
549 | 0 | r = vasprintf(&name, format, ap); |
550 | 0 | va_end(ap); |
551 | 0 | if (r < 0) |
552 | 0 | goto error; |
553 | | |
554 | 0 | r = xasprintf(&path, "%s/%s", dir, name); |
555 | 0 | if (r < 0) |
556 | 0 | goto error; |
557 | | |
558 | 0 | result = fopen(path, "w"); |
559 | |
|
560 | 0 | error: |
561 | 0 | free(name); |
562 | 0 | free(path); |
563 | 0 | return result; |
564 | 0 | } |
565 | | #endif |
566 | | /* |
567 | | * Local variables: |
568 | | * indent-tabs-mode: nil |
569 | | * c-indent-level: 4 |
570 | | * c-basic-offset: 4 |
571 | | * tab-width: 4 |
572 | | * End: |
573 | | */ |