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