Imported Upstream version 1.8.0
[platform/upstream/augeas.git] / src / internal.c
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 # define MIN(a, b) ((a) < (b) ? (a) : (b))
36 #endif
37
38 /* Cap file reads somwhat arbitrarily at 32 MB */
39 #define MAX_READ_LEN (32*1024*1024)
40
41 int pathjoin(char **path, int nseg, ...) {
42     va_list ap;
43
44     va_start(ap, nseg);
45     for (int i=0; i < nseg; i++) {
46         const char *seg = va_arg(ap, const char *);
47         if (seg == NULL)
48             seg = "()";
49         int len = strlen(seg) + 1;
50
51         if (*path != NULL) {
52             len += strlen(*path) + 1;
53             if (REALLOC_N(*path, len) == -1) {
54                 FREE(*path);
55                 va_end(ap);
56                 return -1;
57             }
58             if (strlen(*path) == 0 || (*path)[strlen(*path)-1] != SEP)
59                 strcat(*path, "/");
60             if (seg[0] == SEP)
61                 seg += 1;
62             strcat(*path, seg);
63         } else {
64             if ((*path = malloc(len)) == NULL) {
65                 va_end(ap);
66                 return -1;
67             }
68             strcpy(*path, seg);
69         }
70     }
71     va_end(ap);
72     return 0;
73 }
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 {
85     char *buf = NULL;
86     size_t alloc = 0;
87     size_t size = 0;
88     int save_errno;
89
90     for (;;) {
91         size_t count;
92         size_t requested;
93
94         if (size + BUFSIZ + 1 > alloc) {
95             char *new_buf;
96
97             alloc += alloc / 2;
98             if (alloc < size + BUFSIZ + 1)
99                 alloc = size + BUFSIZ + 1;
100
101             new_buf = realloc (buf, alloc);
102             if (!new_buf) {
103                 save_errno = errno;
104                 break;
105             }
106
107             buf = new_buf;
108         }
109
110         /* Ensure that (size + requested <= max_len); */
111         requested = MIN (size < max_len ? max_len - size : 0,
112                          alloc - size - 1);
113         count = fread (buf + size, 1, requested, stream);
114         size += count;
115
116         if (count != requested || requested == 0) {
117             save_errno = errno;
118             if (ferror (stream))
119                 break;
120             buf[size] = '\0';
121             *length = size;
122             return buf;
123         }
124     }
125
126     free (buf);
127     errno = save_errno;
128     return NULL;
129 }
130
131 char* xfread_file(FILE *fp) {
132     char *result;
133     size_t len;
134
135     if (!fp)
136         return NULL;
137
138     result = fread_file_lim(fp, MAX_READ_LEN, &len);
139
140     if (result != NULL
141         && len <= MAX_READ_LEN
142         && (int) len == len)
143         return result;
144
145     free(result);
146     return NULL;
147 }
148
149 char* xread_file(const char *path) {
150     FILE *fp;
151     char *result;
152
153     fp = fopen(path, "r");
154     if (!fp)
155         return NULL;
156
157     result = xfread_file(fp);
158     fclose(fp);
159
160     return result;
161 }
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 char *unescape(const char *s, int len, const char *extra) {
170     size_t size;
171     const char *n;
172     char *result, *t;
173     int i;
174
175     if (len < 0 || len > strlen(s))
176         len = strlen(s);
177
178     size = 0;
179     for (i=0; i < len; i++, size++) {
180         if (s[i] == '\\' && strchr(escape_names, s[i+1])) {
181             i += 1;
182         } else if (s[i] == '\\' && extra && strchr(extra, s[i+1])) {
183             i += 1;
184         }
185     }
186
187     if (ALLOC_N(result, size + 1) < 0)
188         return NULL;
189
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];
193             i += 1;
194         } else if (s[i] == '\\' && extra && strchr(extra, s[i+1]) != NULL) {
195             *t++ = s[i+1];
196             i += 1;
197         } else {
198             *t++ = s[i];
199         }
200     }
201     return result;
202 }
203
204 char *escape(const char *text, int cnt, const char *extra) {
205
206     int len = 0;
207     char *esc = NULL, *e;
208
209     if (cnt < 0 || cnt > strlen(text))
210         cnt = strlen(text);
211
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' */
219         else
220             len += 1;
221     }
222     if (ALLOC_N(esc, len+1) < 0)
223         return NULL;
224     e = esc;
225     for (int i=0; i < cnt; i++) {
226         char *p;
227         if (text[i] && ((p = strchr(escape_chars, text[i])) != NULL)) {
228             *e++ = '\\';
229             *e++ = escape_names[p - escape_chars];
230         } else if (text[i] && extra && (strchr(extra, text[i]) != NULL)) {
231             *e++ = '\\';
232             *e++ = text[i];
233         } else if (! isprint(text[i])) {
234             sprintf(e, "\\%03o", (unsigned char) text[i]);
235             e += 4;
236         } else {
237             *e++ = text[i];
238         }
239     }
240     return esc;
241 }
242
243 int print_chars(FILE *out, const char *text, int cnt) {
244     int total = 0;
245     char *esc;
246
247     if (text == NULL) {
248         fprintf(out, "nil");
249         return 3;
250     }
251     if (cnt < 0)
252         cnt = strlen(text);
253
254     esc = escape(text, cnt, "\"");
255     total = strlen(esc);
256     if (out != NULL)
257         fprintf(out, "%s", esc);
258     free(esc);
259
260     return total;
261 }
262
263 char *format_pos(const char *text, int pos) {
264     static const int window = 28;
265     char *buf = NULL, *left = NULL, *right = NULL;
266     int before = pos;
267     int llen, rlen;
268     int r;
269
270     if (before > window)
271         before = window;
272     left = escape(text + pos - before, before, NULL);
273     if (left == NULL)
274         goto done;
275     right = escape(text + pos, window, NULL);
276     if (right == NULL)
277         goto done;
278
279     llen = strlen(left);
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, ">");
288     } else {
289         r = asprintf(&buf, "<%s|=|%s>\n", left, right);
290     }
291     if (r < 0) {
292         buf = NULL;
293     }
294
295  done:
296     free(left);
297     free(right);
298     return buf;
299 }
300
301 void print_pos(FILE *out, const char *text, int pos) {
302     char *format = format_pos(text, pos);
303
304     if (format != NULL) {
305         fputs(format, out);
306         FREE(format);
307     }
308 }
309
310 int __aug_init_memstream(struct memstream *ms) {
311     MEMZERO(ms, 1);
312 #if HAVE_OPEN_MEMSTREAM
313     ms->stream = open_memstream(&(ms->buf), &(ms->size));
314     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 }
323
324 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     if (fclose(ms->stream) == EOF) {
330         FREE(ms->buf);
331         ms->size = 0;
332         return -1;
333     }
334     return 0;
335 }
336
337 char *path_expand(struct tree *tree, const char *ppath) {
338     struct tree *siblings = tree->parent->children;
339
340     char *path;
341     const char *label;
342     char *escaped = NULL;
343
344     int cnt = 0, ind = 0, r;
345
346     list_for_each(t, siblings) {
347         if (streqv(t->label, tree->label)) {
348             cnt += 1;
349             if (t == tree)
350                 ind = cnt;
351         }
352     }
353
354     if (ppath == NULL)
355         ppath = "";
356
357     if (tree->label == NULL)
358         label = "(none)";
359     else
360         label = tree->label;
361
362     r = pathx_escape_name(label, &escaped);
363     if (r < 0)
364         return NULL;
365
366     if (escaped != NULL)
367         label = escaped;
368
369     if (cnt > 1) {
370         r = asprintf(&path, "%s/%s[%d]", ppath, label, ind);
371     } else {
372         r = asprintf(&path, "%s/%s", ppath, label);
373     }
374
375     free(escaped);
376
377     if (r == -1)
378         return NULL;
379     return path;
380 }
381
382 char *path_of_tree(struct tree *tree) {
383     int depth, i;
384     struct tree *t, **anc;
385     char *path = NULL;
386
387     for (t = tree, depth = 1; ! ROOT_P(t); depth++, t = t->parent);
388     if (ALLOC_N(anc, depth) < 0)
389         return NULL;
390
391     for (t = tree, i = depth - 1; i >= 0; i--, t = t->parent)
392         anc[i] = t;
393
394     for (i = 0; i < depth; i++) {
395         char *p = path_expand(anc[i], path);
396         free(path);
397         path = p;
398     }
399     FREE(anc);
400     return path;
401 }
402
403 /* User-facing path cleaning */
404 static char *cleanstr(char *path, const char sep) {
405     if (path == NULL || strlen(path) == 0)
406         return path;
407     char *e = path + strlen(path) - 1;
408     while (e >= path && (*e == sep || isspace(*e)))
409         *e-- = '\0';
410     return path;
411 }
412
413 char *cleanpath(char *path) {
414     if (path == NULL || strlen(path) == 0)
415         return path;
416     if (STREQ(path, "/"))
417         return path;
418     return cleanstr(path, SEP);
419 }
420
421 const char *xstrerror(int errnum, char *buf, size_t len) {
422 #ifdef HAVE_STRERROR_R
423 # ifdef __USE_GNU
424     /* Annoying linux specific API contract */
425     return strerror_r(errnum, buf, len);
426 # else
427     strerror_r(errnum, buf, len);
428     return buf;
429 # endif
430 #else
431     int n = snprintf(buf, len, "errno=%d", errnum);
432     return (0 < n && n < len
433             ? buf : "internal error: buffer too small in xstrerror");
434 #endif
435 }
436
437 int xasprintf(char **strp, const char *format, ...) {
438   va_list args;
439   int result;
440
441   va_start (args, format);
442   result = vasprintf (strp, format, args);
443   va_end (args);
444   if (result < 0)
445       *strp = NULL;
446   return result;
447 }
448
449 /* From libvirt's src/xen/block_stats.c */
450 int xstrtoint64(char const *s, int base, int64_t *result) {
451     long long int lli;
452     char *p;
453
454     errno = 0;
455     lli = strtoll(s, &p, base);
456     if (errno || !(*p == 0 || *p == '\n') || p == s || (int64_t) lli != lli)
457         return -1;
458     *result = lli;
459     return 0;
460 }
461
462 void calc_line_ofs(const char *text, size_t pos, size_t *line, size_t *ofs) {
463     *line = 1;
464     *ofs = 0;
465     for (const char *t = text; t < text + pos; t++) {
466         *ofs += 1;
467         if (*t == '\n') {
468             *ofs = 0;
469             *line += 1;
470         }
471     }
472 }
473
474 #if HAVE_USELOCALE
475 int regexp_c_locale(ATTRIBUTE_UNUSED char **u, ATTRIBUTE_UNUSED size_t *len) {
476     /* On systems with uselocale, we are ok, since we make sure that we
477      * switch to the "C" locale any time we enter through the public API
478      */
479     return 0;
480 }
481 #else
482 int regexp_c_locale(char **u, size_t *len) {
483     /* Without uselocale, we need to expand character ranges */
484     int r;
485     char *s = *u;
486     size_t s_len, u_len;
487     if (len == NULL) {
488         len = &u_len;
489         s_len = strlen(s);
490     } else {
491         s_len = *len;
492     }
493     r = fa_expand_char_ranges(s, s_len, u, len);
494     if (r != 0) {
495         *u = s;
496         *len = s_len;
497     }
498     if (r < 0)
499         return -1;
500     /* Syntax errors will be caught when the result is compiled */
501     if (r > 0)
502         return 0;
503     free(s);
504     return 1;
505 }
506 #endif
507
508 #if ENABLE_DEBUG
509 bool debugging(const char *category) {
510     const char *debug = getenv("AUGEAS_DEBUG");
511     const char *s;
512
513     if (debug == NULL)
514         return false;
515
516     for (s = debug; s != NULL; ) {
517         if (STREQLEN(s, category, strlen(category)))
518             return true;
519         s = strchr(s, ':');
520         if (s != NULL)
521             s+=1;
522     }
523     return false;
524 }
525
526 FILE *debug_fopen(const char *format, ...) {
527     va_list ap;
528     FILE *result = NULL;
529     const char *dir;
530     char *name = NULL, *path = NULL;
531     int r;
532
533     dir = getenv("AUGEAS_DEBUG_DIR");
534     if (dir == NULL)
535         goto error;
536
537     va_start(ap, format);
538     r = vasprintf(&name, format, ap);
539     va_end(ap);
540     if (r < 0)
541         goto error;
542
543     r = xasprintf(&path, "%s/%s", dir, name);
544     if (r < 0)
545         goto error;
546
547     result = fopen(path, "w");
548
549  error:
550     free(name);
551     free(path);
552     return result;
553 }
554 #endif
555 /*
556  * Local variables:
557  *  indent-tabs-mode: nil
558  *  c-indent-level: 4
559  *  c-basic-offset: 4
560  *  tab-width: 4
561  * End:
562  */