2 * internal.c: internal data structures and helpers
4 * Copyright (C) 2007-2011 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) {
57 if (strlen(*path) == 0 || (*path)[strlen(*path)-1] != SEP)
63 if ((*path = malloc(len)) == NULL)
72 /* Like gnulib's fread_file, but read no more than the specified maximum
73 number of bytes. If the length of the input is <= max_len, and
74 upon error while reading that data, it works just like fread_file.
76 Taken verbatim from libvirt's util.c
80 fread_file_lim (FILE *stream, size_t max_len, size_t *length)
91 if (size + BUFSIZ + 1 > alloc) {
95 if (alloc < size + BUFSIZ + 1)
96 alloc = size + BUFSIZ + 1;
98 new_buf = realloc (buf, alloc);
107 /* Ensure that (size + requested <= max_len); */
108 requested = MIN (size < max_len ? max_len - size : 0,
110 count = fread (buf + size, 1, requested, stream);
113 if (count != requested || requested == 0) {
128 char* xfread_file(FILE *fp) {
135 result = fread_file_lim(fp, MAX_READ_LEN, &len);
138 && len <= MAX_READ_LEN
146 char* xread_file(const char *path) {
150 fp = fopen(path, "r");
154 result = xfread_file(fp);
161 * Escape/unescape of string literals
163 static const char *const escape_chars = "\a\b\t\n\v\f\r";
164 static const char *const escape_names = "abtnvfr";
166 char *unescape(const char *s, int len, const char *extra) {
172 if (len < 0 || len > strlen(s))
176 for (i=0; i < len; i++, size++) {
177 if (s[i] == '\\' && strchr(escape_names, s[i+1])) {
179 } else if (s[i] == '\\' && extra && strchr(extra, s[i+1])) {
184 if (ALLOC_N(result, size + 1) < 0)
187 for (i = 0, t = result; i < len; i++, size++) {
188 if (s[i] == '\\' && (n = strchr(escape_names, s[i+1])) != NULL) {
189 *t++ = escape_chars[n - escape_names];
191 } else if (s[i] == '\\' && extra && strchr(extra, s[i+1]) != NULL) {
201 char *escape(const char *text, int cnt, const char *extra) {
204 char *esc = NULL, *e;
206 if (cnt < 0 || cnt > strlen(text))
209 for (int i=0; i < cnt; i++) {
210 if (text[i] && (strchr(escape_chars, text[i]) != NULL))
211 len += 2; /* Escaped as '\x' */
212 else if (text[i] && extra && (strchr(extra, text[i]) != NULL))
213 len += 2; /* Escaped as '\x' */
214 else if (! isprint(text[i]))
215 len += 4; /* Escaped as '\ooo' */
219 if (ALLOC_N(esc, len+1) < 0)
222 for (int i=0; i < cnt; i++) {
224 if (text[i] && ((p = strchr(escape_chars, text[i])) != NULL)) {
226 *e++ = escape_names[p - escape_chars];
227 } else if (text[i] && extra && (strchr(extra, text[i]) != NULL)) {
230 } else if (! isprint(text[i])) {
231 sprintf(e, "\\%03o", (unsigned char) text[i]);
240 int print_chars(FILE *out, const char *text, int cnt) {
251 esc = escape(text, cnt, "\"");
254 fprintf(out, "%s", esc);
260 char *format_pos(const char *text, int pos) {
261 static const int window = 28;
262 char *buf = NULL, *left = NULL, *right = NULL;
269 left = escape(text + pos - before, before, NULL);
272 right = escape(text + pos, window, NULL);
277 rlen = strlen(right);
278 if (llen < window && rlen < window) {
279 r = asprintf(&buf, "%*s%s|=|%s%-*s\n", window - llen, "<", left,
280 right, window - rlen, ">");
281 } else if (strlen(left) < window) {
282 r = asprintf(&buf, "%*s%s|=|%s>\n", window - llen, "<", left, right);
283 } else if (strlen(right) < window) {
284 r = asprintf(&buf, "<%s|=|%s%-*s\n", left, right, window - rlen, ">");
286 r = asprintf(&buf, "<%s|=|%s>\n", left, right);
298 void print_pos(FILE *out, const char *text, int pos) {
299 char *format = format_pos(text, pos);
301 if (format != NULL) {
307 int __aug_init_memstream(struct memstream *ms) {
309 #if HAVE_OPEN_MEMSTREAM
310 ms->stream = open_memstream(&(ms->buf), &(ms->size));
311 return ms->stream == NULL ? -1 : 0;
313 ms->stream = tmpfile();
314 if (ms->stream == NULL) {
321 int __aug_close_memstream(struct memstream *ms) {
322 #if !HAVE_OPEN_MEMSTREAM
324 ms->buf = fread_file_lim(ms->stream, MAX_READ_LEN, &(ms->size));
326 if (fclose(ms->stream) == EOF) {
334 char *path_expand(struct tree *tree, const char *ppath) {
335 struct tree *siblings = tree->parent->children;
339 int cnt = 0, ind = 0, r;
341 list_for_each(t, siblings) {
342 if (streqv(t->label, tree->label)) {
354 else if (tree->label == NULL)
360 r = asprintf(&path, "%s/%s[%d]", ppath, label, ind);
362 r = asprintf(&path, "%s/%s", ppath, label);
369 char *path_of_tree(struct tree *tree) {
371 struct tree *t, **anc;
374 for (t = tree, depth = 1; ! ROOT_P(t); depth++, t = t->parent);
375 if (ALLOC_N(anc, depth) < 0)
378 for (t = tree, i = depth - 1; i >= 0; i--, t = t->parent)
381 for (i = 0; i < depth; i++) {
382 char *p = path_expand(anc[i], path);
390 /* User-facing path cleaning */
391 static char *cleanstr(char *path, const char sep) {
392 if (path == NULL || strlen(path) == 0)
394 char *e = path + strlen(path) - 1;
395 while (e >= path && (*e == sep || isspace(*e)))
400 char *cleanpath(char *path) {
401 if (path == NULL || strlen(path) == 0)
403 if (STREQ(path, "/"))
405 return cleanstr(path, SEP);
408 const char *xstrerror(int errnum, char *buf, size_t len) {
409 #ifdef HAVE_STRERROR_R
411 /* Annoying linux specific API contract */
412 return strerror_r(errnum, buf, len);
414 strerror_r(errnum, buf, len);
418 int n = snprintf(buf, len, "errno=%d", errnum);
419 return (0 < n && n < len
420 ? buf : "internal error: buffer too small in xstrerror");
424 int xasprintf(char **strp, const char *format, ...) {
428 va_start (args, format);
429 result = vasprintf (strp, format, args);
436 /* From libvirt's src/xen/block_stats.c */
437 int xstrtoint64(char const *s, int base, int64_t *result) {
442 lli = strtoll(s, &p, base);
443 if (errno || !(*p == 0 || *p == '\n') || p == s || (int64_t) lli != lli)
449 void calc_line_ofs(const char *text, size_t pos, size_t *line, size_t *ofs) {
452 for (const char *t = text; t < text + pos; t++) {
462 int regexp_c_locale(ATTRIBUTE_UNUSED char **u, ATTRIBUTE_UNUSED size_t *len) {
463 /* On systems with uselocale, we are ok, since we make sure that we
464 * switch to the "C" locale any time we enter through the public API
469 int regexp_c_locale(char **u, size_t *len) {
470 /* Without uselocale, we need to expand character ranges */
480 r = fa_expand_char_ranges(s, s_len, u, len);
487 /* Syntax errors will be caught when the result is compiled */
496 bool debugging(const char *category) {
497 const char *debug = getenv("AUGEAS_DEBUG");
503 for (s = debug; s != NULL; ) {
504 if (STREQLEN(s, category, strlen(category)))
513 FILE *debug_fopen(const char *format, ...) {
517 char *name = NULL, *path = NULL;
520 dir = getenv("AUGEAS_DEBUG_DIR");
524 va_start(ap, format);
525 r = vasprintf(&name, format, ap);
530 r = xasprintf(&path, "%s/%s", dir, name);
534 result = fopen(path, "w");
544 * indent-tabs-mode: nil