2 * internal.c: internal data structures and helpers
4 * Copyright (C) 2007-2016 David Lutterkort
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.
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.
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
20 * Author: David Lutterkort <dlutter@redhat.com>
35 # define MIN(a, b) ((a) < (b) ? (a) : (b))
38 /* Cap file reads somwhat arbitrarily at 32 MB */
39 #define MAX_READ_LEN (32*1024*1024)
41 int pathjoin(char **path, int nseg, ...) {
45 for (int i=0; i < nseg; i++) {
46 const char *seg = va_arg(ap, const char *);
49 int len = strlen(seg) + 1;
52 len += strlen(*path) + 1;
53 if (REALLOC_N(*path, len) == -1) {
58 if (strlen(*path) == 0 || (*path)[strlen(*path)-1] != SEP)
64 if ((*path = malloc(len)) == NULL) {
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.
79 Taken verbatim from libvirt's util.c
83 fread_file_lim (FILE *stream, size_t max_len, size_t *length)
94 if (size + BUFSIZ + 1 > alloc) {
98 if (alloc < size + BUFSIZ + 1)
99 alloc = size + BUFSIZ + 1;
101 new_buf = realloc (buf, alloc);
110 /* Ensure that (size + requested <= max_len); */
111 requested = MIN (size < max_len ? max_len - size : 0,
113 count = fread (buf + size, 1, requested, stream);
116 if (count != requested || requested == 0) {
131 char* xfread_file(FILE *fp) {
138 result = fread_file_lim(fp, MAX_READ_LEN, &len);
141 && len <= MAX_READ_LEN
149 char* xread_file(const char *path) {
153 fp = fopen(path, "r");
157 result = xfread_file(fp);
164 * Escape/unescape of string literals
166 static const char *const escape_chars = "\a\b\t\n\v\f\r";
167 static const char *const escape_names = "abtnvfr";
169 char *unescape(const char *s, int len, const char *extra) {
175 if (len < 0 || len > strlen(s))
179 for (i=0; i < len; i++, size++) {
180 if (s[i] == '\\' && strchr(escape_names, s[i+1])) {
182 } else if (s[i] == '\\' && extra && strchr(extra, s[i+1])) {
187 if (ALLOC_N(result, size + 1) < 0)
190 for (i = 0, t = result; i < len; i++, size++) {
191 if (s[i] == '\\' && (n = strchr(escape_names, s[i+1])) != NULL) {
192 *t++ = escape_chars[n - escape_names];
194 } else if (s[i] == '\\' && extra && strchr(extra, s[i+1]) != NULL) {
204 char *escape(const char *text, int cnt, const char *extra) {
207 char *esc = NULL, *e;
209 if (cnt < 0 || cnt > strlen(text))
212 for (int i=0; i < cnt; i++) {
213 if (text[i] && (strchr(escape_chars, text[i]) != NULL))
214 len += 2; /* Escaped as '\x' */
215 else if (text[i] && extra && (strchr(extra, text[i]) != NULL))
216 len += 2; /* Escaped as '\x' */
217 else if (! isprint(text[i]))
218 len += 4; /* Escaped as '\ooo' */
222 if (ALLOC_N(esc, len+1) < 0)
225 for (int i=0; i < cnt; i++) {
227 if (text[i] && ((p = strchr(escape_chars, text[i])) != NULL)) {
229 *e++ = escape_names[p - escape_chars];
230 } else if (text[i] && extra && (strchr(extra, text[i]) != NULL)) {
233 } else if (! isprint(text[i])) {
234 sprintf(e, "\\%03o", (unsigned char) text[i]);
243 int print_chars(FILE *out, const char *text, int cnt) {
254 esc = escape(text, cnt, "\"");
257 fprintf(out, "%s", esc);
263 char *format_pos(const char *text, int pos) {
264 static const int window = 28;
265 char *buf = NULL, *left = NULL, *right = NULL;
272 left = escape(text + pos - before, before, NULL);
275 right = escape(text + pos, window, NULL);
280 rlen = strlen(right);
281 if (llen < window && rlen < window) {
282 r = asprintf(&buf, "%*s%s|=|%s%-*s\n", window - llen, "<", left,
283 right, window - rlen, ">");
284 } else if (strlen(left) < window) {
285 r = asprintf(&buf, "%*s%s|=|%s>\n", window - llen, "<", left, right);
286 } else if (strlen(right) < window) {
287 r = asprintf(&buf, "<%s|=|%s%-*s\n", left, right, window - rlen, ">");
289 r = asprintf(&buf, "<%s|=|%s>\n", left, right);
301 void print_pos(FILE *out, const char *text, int pos) {
302 char *format = format_pos(text, pos);
304 if (format != NULL) {
310 int __aug_init_memstream(struct memstream *ms) {
312 #if HAVE_OPEN_MEMSTREAM
313 ms->stream = open_memstream(&(ms->buf), &(ms->size));
314 return ms->stream == NULL ? -1 : 0;
316 ms->stream = tmpfile();
317 if (ms->stream == NULL) {
324 int __aug_close_memstream(struct memstream *ms) {
325 #if !HAVE_OPEN_MEMSTREAM
327 ms->buf = fread_file_lim(ms->stream, MAX_READ_LEN, &(ms->size));
329 if (fclose(ms->stream) == EOF) {
337 int tree_sibling_index(struct tree *tree) {
338 struct tree *siblings = tree->parent->children;
340 int cnt = 0, ind = 0;
342 list_for_each(t, siblings) {
343 if (streqv(t->label, tree->label)) {
357 char *path_expand(struct tree *tree, const char *ppath) {
360 char *escaped = NULL;
363 int ind = tree_sibling_index(tree);
368 if (tree->label == NULL)
373 r = pathx_escape_name(label, &escaped);
381 r = asprintf(&path, "%s/%s[%d]", ppath, label, ind);
383 r = asprintf(&path, "%s/%s", ppath, label);
393 char *path_of_tree(struct tree *tree) {
395 struct tree *t, **anc;
398 for (t = tree, depth = 1; ! ROOT_P(t); depth++, t = t->parent);
399 if (ALLOC_N(anc, depth) < 0)
402 for (t = tree, i = depth - 1; i >= 0; i--, t = t->parent)
405 for (i = 0; i < depth; i++) {
406 char *p = path_expand(anc[i], path);
414 /* User-facing path cleaning */
415 static char *cleanstr(char *path, const char sep) {
416 if (path == NULL || strlen(path) == 0)
418 char *e = path + strlen(path) - 1;
419 while (e >= path && (*e == sep || isspace(*e)))
424 char *cleanpath(char *path) {
425 if (path == NULL || strlen(path) == 0)
427 if (STREQ(path, "/"))
429 return cleanstr(path, SEP);
432 const char *xstrerror(int errnum, char *buf, size_t len) {
433 #ifdef HAVE_STRERROR_R
435 /* Annoying linux specific API contract */
436 return strerror_r(errnum, buf, len);
438 strerror_r(errnum, buf, len);
442 int n = snprintf(buf, len, "errno=%d", errnum);
443 return (0 < n && n < len
444 ? buf : "internal error: buffer too small in xstrerror");
448 int xasprintf(char **strp, const char *format, ...) {
452 va_start (args, format);
453 result = vasprintf (strp, format, args);
460 /* From libvirt's src/xen/block_stats.c */
461 int xstrtoint64(char const *s, int base, int64_t *result) {
466 lli = strtoll(s, &p, base);
467 if (errno || !(*p == 0 || *p == '\n') || p == s || (int64_t) lli != lli)
473 void calc_line_ofs(const char *text, size_t pos, size_t *line, size_t *ofs) {
476 for (const char *t = text; t < text + pos; t++) {
486 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
493 int regexp_c_locale(char **u, size_t *len) {
494 /* Without uselocale, we need to expand character ranges */
504 r = fa_expand_char_ranges(s, s_len, u, len);
511 /* Syntax errors will be caught when the result is compiled */
520 bool debugging(const char *category) {
521 const char *debug = getenv("AUGEAS_DEBUG");
527 for (s = debug; s != NULL; ) {
528 if (STREQLEN(s, category, strlen(category)))
537 FILE *debug_fopen(const char *format, ...) {
541 char *name = NULL, *path = NULL;
544 dir = getenv("AUGEAS_DEBUG_DIR");
548 va_start(ap, format);
549 r = vasprintf(&name, format, ap);
554 r = xasprintf(&path, "%s/%s", dir, name);
558 result = fopen(path, "w");
568 * indent-tabs-mode: nil