• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 13360111137

16 Feb 2025 05:52PM UTC coverage: 71.714% (-0.1%) from 71.81%
13360111137

push

github

DaanDeMeyer
ukify: Fix --sign-profile= check for main profile

opts.profile is always an env file so we have to parse the default
value as well to check if it's in --sign-profile= or not.

293257 of 408926 relevant lines covered (71.71%)

716517.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.41
/src/basic/escape.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <errno.h>
4
#include <stdlib.h>
5
#include <string.h>
6

7
#include "alloc-util.h"
8
#include "escape.h"
9
#include "hexdecoct.h"
10
#include "macro.h"
11
#include "strv.h"
12
#include "utf8.h"
13

14
int cescape_char(char c, char *buf) {
1,085,501✔
15
        char *buf_old = buf;
1,085,501✔
16

17
        /* Needs space for 4 characters in the buffer */
18

19
        switch (c) {
1,085,501✔
20

21
                case '\a':
7✔
22
                        *(buf++) = '\\';
7✔
23
                        *(buf++) = 'a';
7✔
24
                        break;
7✔
25
                case '\b':
9✔
26
                        *(buf++) = '\\';
9✔
27
                        *(buf++) = 'b';
9✔
28
                        break;
9✔
29
                case '\f':
7✔
30
                        *(buf++) = '\\';
7✔
31
                        *(buf++) = 'f';
7✔
32
                        break;
7✔
33
                case '\n':
261✔
34
                        *(buf++) = '\\';
261✔
35
                        *(buf++) = 'n';
261✔
36
                        break;
261✔
37
                case '\r':
4✔
38
                        *(buf++) = '\\';
4✔
39
                        *(buf++) = 'r';
4✔
40
                        break;
4✔
41
                case '\t':
45✔
42
                        *(buf++) = '\\';
45✔
43
                        *(buf++) = 't';
45✔
44
                        break;
45✔
45
                case '\v':
2✔
46
                        *(buf++) = '\\';
2✔
47
                        *(buf++) = 'v';
2✔
48
                        break;
2✔
49
                case '\\':
590✔
50
                        *(buf++) = '\\';
590✔
51
                        *(buf++) = '\\';
590✔
52
                        break;
590✔
53
                case '"':
2,398✔
54
                        *(buf++) = '\\';
2,398✔
55
                        *(buf++) = '"';
2,398✔
56
                        break;
2,398✔
57
                case '\'':
19✔
58
                        *(buf++) = '\\';
19✔
59
                        *(buf++) = '\'';
19✔
60
                        break;
19✔
61

62
                default:
1,082,159✔
63
                        /* For special chars we prefer octal over
64
                         * hexadecimal encoding, simply because glib's
65
                         * g_strescape() does the same */
66
                        if ((c < ' ') || (c >= 127)) {
1,082,159✔
67
                                *(buf++) = '\\';
13,823✔
68
                                *(buf++) = octchar((unsigned char) c >> 6);
13,823✔
69
                                *(buf++) = octchar((unsigned char) c >> 3);
13,823✔
70
                                *(buf++) = octchar((unsigned char) c);
13,823✔
71
                        } else
72
                                *(buf++) = c;
1,068,336✔
73
                        break;
74
        }
75

76
        return buf - buf_old;
1,085,501✔
77
}
78

79
char* cescape_length(const char *s, size_t n) {
20,670✔
80
        const char *f;
20,670✔
81
        char *r, *t;
20,670✔
82

83
        /* Does C style string escaping. May be reversed with cunescape(). */
84

85
        assert(s || n == 0);
20,670✔
86

87
        if (n == SIZE_MAX)
20,670✔
88
                n = strlen(s);
9,839✔
89

90
        if (n > (SIZE_MAX - 1) / 4)
20,670✔
91
                return NULL;
92

93
        r = new(char, n*4 + 1);
20,670✔
94
        if (!r)
20,670✔
95
                return NULL;
96

97
        for (f = s, t = r; f < s + n; f++)
557,456✔
98
                t += cescape_char(*f, t);
536,786✔
99

100
        *t = 0;
20,670✔
101

102
        return r;
20,670✔
103
}
104

105
int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit, bool accept_nul) {
6,457✔
106
        int r = 1;
6,457✔
107

108
        assert(p);
6,457✔
109
        assert(ret);
6,457✔
110

111
        /* Unescapes C style. Returns the unescaped character in ret.
112
         * Sets *eight_bit to true if the escaped sequence either fits in
113
         * one byte in UTF-8 or is a non-unicode literal byte and should
114
         * instead be copied directly.
115
         */
116

117
        if (length != SIZE_MAX && length < 1)
6,457✔
118
                return -EINVAL;
119

120
        switch (p[0]) {
6,457✔
121

122
        case 'a':
4✔
123
                *ret = '\a';
4✔
124
                break;
4✔
125
        case 'b':
8✔
126
                *ret = '\b';
8✔
127
                break;
8✔
128
        case 'f':
3✔
129
                *ret = '\f';
3✔
130
                break;
3✔
131
        case 'n':
22✔
132
                *ret = '\n';
22✔
133
                break;
22✔
134
        case 'r':
4✔
135
                *ret = '\r';
4✔
136
                break;
4✔
137
        case 't':
6✔
138
                *ret = '\t';
6✔
139
                break;
6✔
140
        case 'v':
3✔
141
                *ret = '\v';
3✔
142
                break;
3✔
143
        case '\\':
15✔
144
                *ret = '\\';
15✔
145
                break;
15✔
146
        case '"':
5✔
147
                *ret = '"';
5✔
148
                break;
5✔
149
        case '\'':
1✔
150
                *ret = '\'';
1✔
151
                break;
1✔
152

153
        case 's':
3✔
154
                /* This is an extension of the XDG syntax files */
155
                *ret = ' ';
3✔
156
                break;
3✔
157

158
        case 'x': {
6,301✔
159
                /* hexadecimal encoding */
160
                int a, b;
6,301✔
161

162
                if (length != SIZE_MAX && length < 3)
6,301✔
163
                        return -EINVAL;
164

165
                a = unhexchar(p[1]);
6,297✔
166
                if (a < 0)
6,297✔
167
                        return -EINVAL;
168

169
                b = unhexchar(p[2]);
6,295✔
170
                if (b < 0)
6,295✔
171
                        return -EINVAL;
172

173
                /* Don't allow NUL bytes */
174
                if (a == 0 && b == 0 && !accept_nul)
6,295✔
175
                        return -EINVAL;
176

177
                *ret = (a << 4U) | b;
6,294✔
178
                *eight_bit = true;
6,294✔
179
                r = 3;
6,294✔
180
                break;
6,294✔
181
        }
182

183
        case 'u': {
19✔
184
                /* C++11 style 16-bit unicode */
185

186
                int a[4];
19✔
187
                size_t i;
19✔
188
                uint32_t c;
19✔
189

190
                if (length != SIZE_MAX && length < 5)
19✔
191
                        return -EINVAL;
1✔
192

193
                for (i = 0; i < 4; i++) {
95✔
194
                        a[i] = unhexchar(p[1 + i]);
76✔
195
                        if (a[i] < 0)
76✔
196
                                return a[i];
197
                }
198

199
                c = ((uint32_t) a[0] << 12U) | ((uint32_t) a[1] << 8U) | ((uint32_t) a[2] << 4U) | (uint32_t) a[3];
19✔
200

201
                /* Don't allow 0 chars */
202
                if (c == 0 && !accept_nul)
19✔
203
                        return -EINVAL;
204

205
                *ret = c;
18✔
206
                r = 5;
18✔
207
                break;
18✔
208
        }
209

210
        case 'U': {
10✔
211
                /* C++11 style 32-bit unicode */
212

213
                int a[8];
10✔
214
                size_t i;
10✔
215
                char32_t c;
10✔
216

217
                if (length != SIZE_MAX && length < 9)
10✔
218
                        return -EINVAL;
×
219

220
                for (i = 0; i < 8; i++) {
90✔
221
                        a[i] = unhexchar(p[1 + i]);
80✔
222
                        if (a[i] < 0)
80✔
223
                                return a[i];
224
                }
225

226
                c = ((uint32_t) a[0] << 28U) | ((uint32_t) a[1] << 24U) | ((uint32_t) a[2] << 20U) | ((uint32_t) a[3] << 16U) |
10✔
227
                    ((uint32_t) a[4] << 12U) | ((uint32_t) a[5] <<  8U) | ((uint32_t) a[6] <<  4U) |  (uint32_t) a[7];
10✔
228

229
                /* Don't allow 0 chars */
230
                if (c == 0 && !accept_nul)
10✔
231
                        return -EINVAL;
232

233
                /* Don't allow invalid code points */
234
                if (!unichar_is_valid(c))
10✔
235
                        return -EINVAL;
236

237
                *ret = c;
10✔
238
                r = 9;
10✔
239
                break;
10✔
240
        }
241

242
        case '0':
20✔
243
        case '1':
244
        case '2':
245
        case '3':
246
        case '4':
247
        case '5':
248
        case '6':
249
        case '7': {
250
                /* octal encoding */
251
                int a, b, c;
20✔
252
                char32_t m;
20✔
253

254
                if (length != SIZE_MAX && length < 3)
20✔
255
                        return -EINVAL;
256

257
                a = unoctchar(p[0]);
16✔
258
                if (a < 0)
16✔
259
                        return -EINVAL;
260

261
                b = unoctchar(p[1]);
16✔
262
                if (b < 0)
16✔
263
                        return -EINVAL;
264

265
                c = unoctchar(p[2]);
16✔
266
                if (c < 0)
16✔
267
                        return -EINVAL;
268

269
                /* don't allow NUL bytes */
270
                if (a == 0 && b == 0 && c == 0 && !accept_nul)
16✔
271
                        return -EINVAL;
272

273
                /* Don't allow bytes above 255 */
274
                m = ((uint32_t) a << 6U) | ((uint32_t) b << 3U) | (uint32_t) c;
14✔
275
                if (m > 255)
14✔
276
                        return -EINVAL;
277

278
                *ret = m;
14✔
279
                *eight_bit = true;
14✔
280
                r = 3;
14✔
281
                break;
14✔
282
        }
283

284
        default:
285
                return -EINVAL;
286
        }
287

288
        return r;
289
}
290

291
ssize_t cunescape_length_with_prefix(const char *s, size_t length, const char *prefix, UnescapeFlags flags, char **ret) {
115,898✔
292
        _cleanup_free_ char *ans = NULL;
115,898✔
293
        char *t;
115,898✔
294
        const char *f;
115,898✔
295
        size_t pl;
115,898✔
296
        int r;
115,898✔
297

298
        assert(s);
115,898✔
299
        assert(ret);
115,898✔
300

301
        /* Undoes C style string escaping, and optionally prefixes it. */
302

303
        pl = strlen_ptr(prefix);
115,898✔
304

305
        ans = new(char, pl+length+1);
115,898✔
306
        if (!ans)
115,898✔
307
                return -ENOMEM;
308

309
        if (prefix)
115,898✔
310
                memcpy(ans, prefix, pl);
4,700✔
311

312
        for (f = s, t = ans + pl; f < s + length; f++) {
3,048,703✔
313
                size_t remaining;
2,932,814✔
314
                bool eight_bit = false;
2,932,814✔
315
                char32_t u;
2,932,814✔
316

317
                remaining = s + length - f;
2,932,814✔
318
                assert(remaining > 0);
2,932,814✔
319

320
                if (*f != '\\') {
2,932,814✔
321
                        /* A literal, copy verbatim */
322
                        *(t++) = *f;
2,926,432✔
323
                        continue;
2,926,439✔
324
                }
325

326
                if (remaining == 1) {
6,382✔
327
                        if (flags & UNESCAPE_RELAX) {
3✔
328
                                /* A trailing backslash, copy verbatim */
329
                                *(t++) = *f;
1✔
330
                                continue;
1✔
331
                        }
332

333
                        return -EINVAL;
9✔
334
                }
335

336
                r = cunescape_one(f + 1, remaining - 1, &u, &eight_bit, flags & UNESCAPE_ACCEPT_NUL);
6,379✔
337
                if (r < 0) {
6,379✔
338
                        if (flags & UNESCAPE_RELAX) {
13✔
339
                                /* Invalid escape code, let's take it literal then */
340
                                *(t++) = '\\';
6✔
341
                                continue;
6✔
342
                        }
343

344
                        return r;
7✔
345
                }
346

347
                f += r;
6,366✔
348
                if (eight_bit)
6,366✔
349
                        /* One byte? Set directly as specified */
350
                        *(t++) = u;
6,295✔
351
                else
352
                        /* Otherwise encode as multi-byte UTF-8 */
353
                        t += utf8_encode_unichar(t, u);
71✔
354
        }
355

356
        *t = 0;
115,889✔
357

358
        assert(t >= ans); /* Let static analyzers know that the answer is non-negative. */
115,889✔
359
        *ret = TAKE_PTR(ans);
115,889✔
360
        return t - *ret;
115,889✔
361
}
362

363
char* xescape_full(const char *s, const char *bad, size_t console_width, XEscapeFlags flags) {
71,806✔
364
        char *ans, *t, *prev, *prev2;
71,806✔
365
        const char *f;
71,806✔
366

367
        assert(s);
71,806✔
368

369
        /* Escapes all chars in bad, in addition to \ and all special chars, in \xFF style escaping. May be
370
         * reversed with cunescape(). If XESCAPE_8_BIT is specified, characters >= 127 are let through
371
         * unchanged. This corresponds to non-ASCII printable characters in pre-unicode encodings.
372
         *
373
         * If console_width is reached, or XESCAPE_FORCE_ELLIPSIS is set, output is truncated and "..." is
374
         * appended. */
375

376
        if (console_width == 0)
71,806✔
377
                return strdup("");
4✔
378

379
        ans = new(char, MIN(strlen(s), console_width) * 4 + 1);
71,802✔
380
        if (!ans)
71,802✔
381
                return NULL;
382

383
        memset(ans, '_', MIN(strlen(s), console_width) * 4);
71,802✔
384
        ans[MIN(strlen(s), console_width) * 4] = 0;
71,802✔
385

386
        bool force_ellipsis = FLAGS_SET(flags, XESCAPE_FORCE_ELLIPSIS);
71,802✔
387

388
        for (f = s, t = prev = prev2 = ans; ; f++) {
71,802✔
389
                char *tmp_t = t;
5,296,863✔
390

391
                if (!*f) {
5,296,863✔
392
                        if (force_ellipsis)
71,598✔
393
                                break;
394

395
                        *t = 0;
71,585✔
396
                        return ans;
71,585✔
397
                }
398

399
                if ((unsigned char) *f < ' ' ||
5,225,265✔
400
                    (!FLAGS_SET(flags, XESCAPE_8_BIT) && (unsigned char) *f >= 127) ||
5,224,138✔
401
                    *f == '\\' || (bad && strchr(bad, *f))) {
5,223,793✔
402
                        if ((size_t) (t - ans) + 4 + 3 * force_ellipsis > console_width)
4,247✔
403
                                break;
404

405
                        *(t++) = '\\';
4,063✔
406
                        *(t++) = 'x';
4,063✔
407
                        *(t++) = hexchar(*f >> 4);
4,063✔
408
                        *(t++) = hexchar(*f);
4,063✔
409
                } else {
410
                        if ((size_t) (t - ans) + 1 + 3 * force_ellipsis > console_width)
5,221,018✔
411
                                break;
412

413
                        *(t++) = *f;
5,220,998✔
414
                }
415

416
                /* We might need to go back two cycles to fit three dots, so remember two positions */
417
                prev2 = prev;
5,225,061✔
418
                prev = tmp_t;
5,225,061✔
419
        }
420

421
        /* We can just write where we want, since chars are one-byte */
422
        size_t c = MIN(console_width, 3u); /* If the console is too narrow, write fewer dots */
217✔
423
        size_t off;
217✔
424
        if (console_width - c >= (size_t) (t - ans))
217✔
425
                off = (size_t) (t - ans);
426
        else if (console_width - c >= (size_t) (prev - ans))
76✔
427
                off = (size_t) (prev - ans);
428
        else if (console_width - c >= (size_t) (prev2 - ans))
10✔
429
                off = (size_t) (prev2 - ans);
430
        else
431
                off = console_width - c;
1✔
432
        assert(off <= (size_t) (t - ans));
217✔
433

434
        memcpy(ans + off, "...", c);
217✔
435
        ans[off + c] = '\0';
217✔
436
        return ans;
217✔
437
}
438

439
char* escape_non_printable_full(const char *str, size_t console_width, XEscapeFlags flags) {
397✔
440
        if (FLAGS_SET(flags, XESCAPE_8_BIT))
397✔
441
                return xescape_full(str, /* bad= */ NULL, console_width, flags);
×
442
        else
443
                return utf8_escape_non_printable_full(str,
397✔
444
                                                      console_width,
445
                                                      FLAGS_SET(flags, XESCAPE_FORCE_ELLIPSIS));
397✔
446
}
447

448
char* octescape(const char *s, size_t len) {
78✔
449
        char *buf, *t;
78✔
450

451
        /* Escapes \ and " chars, in \nnn style escaping. */
452

453
        assert(s || len == 0);
78✔
454

455
        if (len == SIZE_MAX)
78✔
456
                len = strlen(s);
27✔
457

458
        if (len > (SIZE_MAX - 1) / 4)
78✔
459
                return NULL;
460

461
        t = buf = new(char, len * 4 + 1);
78✔
462
        if (!buf)
78✔
463
                return NULL;
464

465
        for (size_t i = 0; i < len; i++) {
856✔
466
                uint8_t u = (uint8_t) s[i];
778✔
467

468
                if (u < ' ' || u >= 127 || IN_SET(u, '\\', '"')) {
778✔
469
                        *(t++) = '\\';
5✔
470
                        *(t++) = '0' + (u >> 6);
5✔
471
                        *(t++) = '0' + ((u >> 3) & 7);
5✔
472
                        *(t++) = '0' + (u & 7);
5✔
473
                } else
474
                        *(t++) = u;
773✔
475
        }
476

477
        *t = 0;
78✔
478
        return buf;
78✔
479
}
480

481
char* decescape(const char *s, size_t len, const char *bad) {
8✔
482
        char *buf, *t;
8✔
483

484
        /* Escapes all chars in bad, in addition to \ and " chars, in \nnn decimal style escaping. */
485

486
        assert(s || len == 0);
8✔
487

488
        if (len == SIZE_MAX)
8✔
489
                len = strlen(s);
6✔
490

491
        if (len > (SIZE_MAX - 1) / 4)
8✔
492
                return NULL;
493

494
        t = buf = new(char, len * 4 + 1);
8✔
495
        if (!buf)
8✔
496
                return NULL;
497

498
        for (size_t i = 0; i < len; i++) {
47✔
499
                uint8_t u = (uint8_t) s[i];
39✔
500

501
                if (u < ' ' || u >= 127 || IN_SET(u, '\\', '"') || strchr(bad, u)) {
39✔
502
                        *(t++) = '\\';
20✔
503
                        *(t++) = '0' + (u / 100);
20✔
504
                        *(t++) = '0' + ((u / 10) % 10);
20✔
505
                        *(t++) = '0' + (u % 10);
20✔
506
                } else
507
                        *(t++) = u;
19✔
508
        }
509

510
        *t = 0;
8✔
511
        return buf;
8✔
512
}
513

514
static char* strcpy_backslash_escaped(char *t, const char *s, const char *bad) {
4,748✔
515
        assert(bad);
4,748✔
516
        assert(t);
4,748✔
517
        assert(s);
4,748✔
518

519
        while (*s) {
192,523✔
520
                int l = utf8_encoded_valid_unichar(s, SIZE_MAX);
187,775✔
521

522
                if (char_is_cc(*s) || l < 0)
187,775✔
523
                        t += cescape_char(*(s++), t);
258✔
524
                else if (l == 1) {
187,517✔
525
                        if (*s == '\\' || strchr(bad, *s))
187,515✔
526
                                *(t++) = '\\';
2,112✔
527
                        *(t++) = *(s++);
187,515✔
528
                } else {
529
                        t = mempcpy(t, s, l);
2✔
530
                        s += l;
2✔
531
                }
532
        }
533

534
        return t;
4,748✔
535
}
536

537
char* shell_escape(const char *s, const char *bad) {
1,560✔
538
        char *buf, *t;
1,560✔
539

540
        buf = new(char, strlen(s)*4+1);
1,560✔
541
        if (!buf)
1,560✔
542
                return NULL;
543

544
        t = strcpy_backslash_escaped(buf, s, bad);
1,560✔
545
        *t = 0;
1,560✔
546

547
        return buf;
1,560✔
548
}
549

550
char* shell_maybe_quote(const char *s, ShellEscapeFlags flags) {
74,908✔
551
        const char *p;
74,908✔
552
        char *buf, *t;
74,908✔
553

554
        assert(s);
74,908✔
555

556
        /* Encloses a string in quotes if necessary to make it OK as a shell string. */
557

558
        if (FLAGS_SET(flags, SHELL_ESCAPE_EMPTY) && isempty(s))
74,908✔
559
                return strdup("\"\""); /* We don't use $'' here in the POSIX mode. "" is fine too. */
153✔
560

561
        for (p = s; *p; ) {
1,582,254✔
562
                int l = utf8_encoded_valid_unichar(p, SIZE_MAX);
1,510,687✔
563

564
                if (char_is_cc(*p) || l < 0 ||
1,510,687✔
565
                    strchr(WHITESPACE SHELL_NEED_QUOTES, *p))
1,510,660✔
566
                        break;
567

568
                p += l;
1,507,499✔
569
        }
570

571
        if (!*p)
74,755✔
572
                return strdup(s);
71,567✔
573

574
        buf = new(char, FLAGS_SET(flags, SHELL_ESCAPE_POSIX) + 1 + strlen(s)*4 + 1 + 1);
6,307✔
575
        if (!buf)
3,188✔
576
                return NULL;
577

578
        t = buf;
3,188✔
579
        if (FLAGS_SET(flags, SHELL_ESCAPE_POSIX)) {
3,188✔
580
                *(t++) = '$';
69✔
581
                *(t++) = '\'';
69✔
582
        } else
583
                *(t++) = '"';
3,119✔
584

585
        t = mempcpy(t, s, p - s);
3,188✔
586

587
        t = strcpy_backslash_escaped(t, p,
6,307✔
588
                                     FLAGS_SET(flags, SHELL_ESCAPE_POSIX) ? SHELL_NEED_ESCAPE_POSIX : SHELL_NEED_ESCAPE);
589

590
        if (FLAGS_SET(flags, SHELL_ESCAPE_POSIX))
3,188✔
591
                *(t++) = '\'';
69✔
592
        else
593
                *(t++) = '"';
3,119✔
594
        *t = 0;
3,188✔
595

596
        return str_realloc(buf);
3,188✔
597
}
598

599
char* quote_command_line(char **argv, ShellEscapeFlags flags) {
24,450✔
600
        _cleanup_free_ char *result = NULL;
24,450✔
601

602
        assert(argv);
24,450✔
603

604
        STRV_FOREACH(a, argv) {
98,620✔
605
                _cleanup_free_ char *t = NULL;
74,170✔
606

607
                t = shell_maybe_quote(*a, flags);
74,170✔
608
                if (!t)
74,170✔
609
                        return NULL;
610

611
                if (!strextend_with_separator(&result, " ", t))
74,170✔
612
                        return NULL;
613
        }
614

615
        return str_realloc(TAKE_PTR(result));
24,450✔
616
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc