/* * internal.c: internal data structures and helpers * * Copyright (C) 2007-2016 David Lutterkort * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author: David Lutterkort */ #include #include #include #include #include #include "internal.h" #include "memory.h" #include "fa.h" #ifndef MIN # define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif /* Cap file reads somwhat arbitrarily at 32 MB */ #define MAX_READ_LEN (32*1024*1024) int pathjoin(char **path, int nseg, ...) { va_list ap; va_start(ap, nseg); for (int i=0; i < nseg; i++) { const char *seg = va_arg(ap, const char *); if (seg == NULL) seg = "()"; int len = strlen(seg) + 1; if (*path != NULL) { len += strlen(*path) + 1; if (REALLOC_N(*path, len) == -1) { FREE(*path); va_end(ap); return -1; } if (strlen(*path) == 0 || (*path)[strlen(*path)-1] != SEP) strcat(*path, "/"); if (seg[0] == SEP) seg += 1; strcat(*path, seg); } else { if ((*path = malloc(len)) == NULL) { va_end(ap); return -1; } strcpy(*path, seg); } } va_end(ap); return 0; } /* Like gnulib's fread_file, but read no more than the specified maximum number of bytes. If the length of the input is <= max_len, and upon error while reading that data, it works just like fread_file. Taken verbatim from libvirt's util.c */ static char * fread_file_lim (FILE *stream, size_t max_len, size_t *length) { char *buf = NULL; size_t alloc = 0; size_t size = 0; int save_errno; for (;;) { size_t count; size_t requested; if (size + BUFSIZ + 1 > alloc) { char *new_buf; alloc += alloc / 2; if (alloc < size + BUFSIZ + 1) alloc = size + BUFSIZ + 1; new_buf = realloc (buf, alloc); if (!new_buf) { save_errno = errno; break; } buf = new_buf; } /* Ensure that (size + requested <= max_len); */ requested = MIN (size < max_len ? max_len - size : 0, alloc - size - 1); count = fread (buf + size, 1, requested, stream); size += count; if (count != requested || requested == 0) { save_errno = errno; if (ferror (stream)) break; buf[size] = '\0'; *length = size; return buf; } } free (buf); errno = save_errno; return NULL; } char* xfread_file(FILE *fp) { char *result; size_t len; if (!fp) return NULL; result = fread_file_lim(fp, MAX_READ_LEN, &len); if (result != NULL && len <= MAX_READ_LEN && (int) len == len) return result; free(result); return NULL; } char* xread_file(const char *path) { FILE *fp; char *result; fp = fopen(path, "r"); if (!fp) return NULL; result = xfread_file(fp); fclose(fp); return result; } /* * Escape/unescape of string literals */ static const char *const escape_chars = "\a\b\t\n\v\f\r"; static const char *const escape_names = "abtnvfr"; char *unescape(const char *s, int len, const char *extra) { size_t size; const char *n; char *result, *t; int i; if (len < 0 || len > strlen(s)) len = strlen(s); size = 0; for (i=0; i < len; i++, size++) { if (s[i] == '\\' && strchr(escape_names, s[i+1])) { i += 1; } else if (s[i] == '\\' && extra && strchr(extra, s[i+1])) { i += 1; } } if (ALLOC_N(result, size + 1) < 0) return NULL; for (i = 0, t = result; i < len; i++, size++) { if (s[i] == '\\' && (n = strchr(escape_names, s[i+1])) != NULL) { *t++ = escape_chars[n - escape_names]; i += 1; } else if (s[i] == '\\' && extra && strchr(extra, s[i+1]) != NULL) { *t++ = s[i+1]; i += 1; } else { *t++ = s[i]; } } return result; } char *escape(const char *text, int cnt, const char *extra) { int len = 0; char *esc = NULL, *e; if (cnt < 0 || cnt > strlen(text)) cnt = strlen(text); for (int i=0; i < cnt; i++) { if (text[i] && (strchr(escape_chars, text[i]) != NULL)) len += 2; /* Escaped as '\x' */ else if (text[i] && extra && (strchr(extra, text[i]) != NULL)) len += 2; /* Escaped as '\x' */ else if (! isprint(text[i])) len += 4; /* Escaped as '\ooo' */ else len += 1; } if (ALLOC_N(esc, len+1) < 0) return NULL; e = esc; for (int i=0; i < cnt; i++) { char *p; if (text[i] && ((p = strchr(escape_chars, text[i])) != NULL)) { *e++ = '\\'; *e++ = escape_names[p - escape_chars]; } else if (text[i] && extra && (strchr(extra, text[i]) != NULL)) { *e++ = '\\'; *e++ = text[i]; } else if (! isprint(text[i])) { sprintf(e, "\\%03o", (unsigned char) text[i]); e += 4; } else { *e++ = text[i]; } } return esc; } int print_chars(FILE *out, const char *text, int cnt) { int total = 0; char *esc; if (text == NULL) { fprintf(out, "nil"); return 3; } if (cnt < 0) cnt = strlen(text); esc = escape(text, cnt, "\""); total = strlen(esc); if (out != NULL) fprintf(out, "%s", esc); free(esc); return total; } char *format_pos(const char *text, int pos) { static const int window = 28; char *buf = NULL, *left = NULL, *right = NULL; int before = pos; int llen, rlen; int r; if (before > window) before = window; left = escape(text + pos - before, before, NULL); if (left == NULL) goto done; right = escape(text + pos, window, NULL); if (right == NULL) goto done; llen = strlen(left); rlen = strlen(right); if (llen < window && rlen < window) { r = asprintf(&buf, "%*s%s|=|%s%-*s\n", window - llen, "<", left, right, window - rlen, ">"); } else if (strlen(left) < window) { r = asprintf(&buf, "%*s%s|=|%s>\n", window - llen, "<", left, right); } else if (strlen(right) < window) { r = asprintf(&buf, "<%s|=|%s%-*s\n", left, right, window - rlen, ">"); } else { r = asprintf(&buf, "<%s|=|%s>\n", left, right); } if (r < 0) { buf = NULL; } done: free(left); free(right); return buf; } void print_pos(FILE *out, const char *text, int pos) { char *format = format_pos(text, pos); if (format != NULL) { fputs(format, out); FREE(format); } } int __aug_init_memstream(struct memstream *ms) { MEMZERO(ms, 1); #if HAVE_OPEN_MEMSTREAM ms->stream = open_memstream(&(ms->buf), &(ms->size)); return ms->stream == NULL ? -1 : 0; #else ms->stream = tmpfile(); if (ms->stream == NULL) { return -1; } return 0; #endif } int __aug_close_memstream(struct memstream *ms) { #if !HAVE_OPEN_MEMSTREAM rewind(ms->stream); ms->buf = fread_file_lim(ms->stream, MAX_READ_LEN, &(ms->size)); #endif if (fclose(ms->stream) == EOF) { FREE(ms->buf); ms->size = 0; return -1; } return 0; } int tree_sibling_index(struct tree *tree) { struct tree *siblings = tree->parent->children; int cnt = 0, ind = 0; list_for_each(t, siblings) { if (streqv(t->label, tree->label)) { cnt += 1; if (t == tree) ind = cnt; } } if (cnt > 1) { return ind; } else { return 0; } } char *path_expand(struct tree *tree, const char *ppath) { char *path; const char *label; char *escaped = NULL; int r; int ind = tree_sibling_index(tree); if (ppath == NULL) ppath = ""; if (tree->label == NULL) label = "(none)"; else label = tree->label; r = pathx_escape_name(label, &escaped); if (r < 0) return NULL; if (escaped != NULL) label = escaped; if (ind > 0) { r = asprintf(&path, "%s/%s[%d]", ppath, label, ind); } else { r = asprintf(&path, "%s/%s", ppath, label); } free(escaped); if (r == -1) return NULL; return path; } char *path_of_tree(struct tree *tree) { int depth, i; struct tree *t, **anc; char *path = NULL; for (t = tree, depth = 1; ! ROOT_P(t); depth++, t = t->parent); if (ALLOC_N(anc, depth) < 0) return NULL; for (t = tree, i = depth - 1; i >= 0; i--, t = t->parent) anc[i] = t; for (i = 0; i < depth; i++) { char *p = path_expand(anc[i], path); free(path); path = p; } FREE(anc); return path; } /* User-facing path cleaning */ static char *cleanstr(char *path, const char sep) { if (path == NULL || strlen(path) == 0) return path; char *e = path + strlen(path) - 1; while (e >= path && (*e == sep || isspace(*e))) *e-- = '\0'; return path; } char *cleanpath(char *path) { if (path == NULL || strlen(path) == 0) return path; if (STREQ(path, "/")) return path; return cleanstr(path, SEP); } const char *xstrerror(int errnum, char *buf, size_t len) { #ifdef HAVE_STRERROR_R # ifdef __USE_GNU /* Annoying linux specific API contract */ return strerror_r(errnum, buf, len); # else strerror_r(errnum, buf, len); return buf; # endif #else int n = snprintf(buf, len, "errno=%d", errnum); return (0 < n && n < len ? buf : "internal error: buffer too small in xstrerror"); #endif } int xasprintf(char **strp, const char *format, ...) { va_list args; int result; va_start (args, format); result = vasprintf (strp, format, args); va_end (args); if (result < 0) *strp = NULL; return result; } /* From libvirt's src/xen/block_stats.c */ int xstrtoint64(char const *s, int base, int64_t *result) { long long int lli; char *p; errno = 0; lli = strtoll(s, &p, base); if (errno || !(*p == 0 || *p == '\n') || p == s || (int64_t) lli != lli) return -1; *result = lli; return 0; } void calc_line_ofs(const char *text, size_t pos, size_t *line, size_t *ofs) { *line = 1; *ofs = 0; for (const char *t = text; t < text + pos; t++) { *ofs += 1; if (*t == '\n') { *ofs = 0; *line += 1; } } } #if HAVE_USELOCALE int regexp_c_locale(ATTRIBUTE_UNUSED char **u, ATTRIBUTE_UNUSED size_t *len) { /* On systems with uselocale, we are ok, since we make sure that we * switch to the "C" locale any time we enter through the public API */ return 0; } #else int regexp_c_locale(char **u, size_t *len) { /* Without uselocale, we need to expand character ranges */ int r; char *s = *u; size_t s_len, u_len; if (len == NULL) { len = &u_len; s_len = strlen(s); } else { s_len = *len; } r = fa_expand_char_ranges(s, s_len, u, len); if (r != 0) { *u = s; *len = s_len; } if (r < 0) return -1; /* Syntax errors will be caught when the result is compiled */ if (r > 0) return 0; free(s); return 1; } #endif #if ENABLE_DEBUG bool debugging(const char *category) { const char *debug = getenv("AUGEAS_DEBUG"); const char *s; if (debug == NULL) return false; for (s = debug; s != NULL; ) { if (STREQLEN(s, category, strlen(category))) return true; s = strchr(s, ':'); if (s != NULL) s+=1; } return false; } FILE *debug_fopen(const char *format, ...) { va_list ap; FILE *result = NULL; const char *dir; char *name = NULL, *path = NULL; int r; dir = getenv("AUGEAS_DEBUG_DIR"); if (dir == NULL) goto error; va_start(ap, format); r = vasprintf(&name, format, ap); va_end(ap); if (r < 0) goto error; r = xasprintf(&path, "%s/%s", dir, name); if (r < 0) goto error; result = fopen(path, "w"); error: free(name); free(path); return result; } #endif /* * Local variables: * indent-tabs-mode: nil * c-indent-level: 4 * c-basic-offset: 4 * tab-width: 4 * End: */