Coverage Report

Created: 2023-09-23 07:09

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