X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fshared%2Futil.c;h=3561573e16da8fe190be18da21defd53e53a01f6;hb=b3aa85dc2ff9eceb3a5cf66795924caf65946d64;hp=d7e4318defa903c52864f0e62540035eaa5db969;hpb=999463d14994808ff15fc4dd66c540d8a29f8121;p=elogind.git diff --git a/src/shared/util.c b/src/shared/util.c index d7e4318de..3561573e1 100644 --- a/src/shared/util.c +++ b/src/shared/util.c @@ -1353,7 +1353,8 @@ char *cescape(const char *s) { assert(s); - /* Does C style string escaping. */ + /* Does C style string escaping. May be reversed with + * cunescape(). */ r = new(char, strlen(s)*4 + 1); if (!r) @@ -1367,13 +1368,17 @@ char *cescape(const char *s) { return r; } -static int cunescape_one(const char *p, size_t length, char *ret) { +static int cunescape_one(const char *p, size_t length, char *ret, uint32_t *ret_unicode) { int r = 1; assert(p); assert(*p); assert(ret); + /* Unescapes C style. Returns the unescaped character in ret, + * unless we encountered a \u sequence in which case the full + * unicode character is returned in ret_unicode, instead. */ + if (length != (size_t) -1 && length < 1) return -EINVAL; @@ -1430,15 +1435,92 @@ static int cunescape_one(const char *p, size_t length, char *ret) { if (b < 0) return -EINVAL; - /* don't allow NUL bytes */ + /* Don't allow NUL bytes */ if (a == 0 && b == 0) return -EINVAL; - *ret = (char) ((a << 4) | b); + *ret = (char) ((a << 4U) | b); r = 3; break; } + case 'u': { + /* C++11 style 16bit unicode */ + + int a[4]; + unsigned i; + uint32_t c; + + if (length != (size_t) -1 && length < 5) + return -EINVAL; + + for (i = 0; i < 4; i++) { + a[i] = unhexchar(p[1 + i]); + if (a[i] < 0) + return a[i]; + } + + c = ((uint32_t) a[0] << 12U) | ((uint32_t) a[1] << 8U) | ((uint32_t) a[2] << 4U) | (uint32_t) a[3]; + + /* Don't allow 0 chars */ + if (c == 0) + return -EINVAL; + + if (c < 128) + *ret = c; + else { + if (!ret_unicode) + return -EINVAL; + + *ret = 0; + *ret_unicode = c; + } + + r = 5; + break; + } + + case 'U': { + /* C++11 style 32bit unicode */ + + int a[8]; + unsigned i; + uint32_t c; + + if (length != (size_t) -1 && length < 9) + return -EINVAL; + + for (i = 0; i < 8; i++) { + a[i] = unhexchar(p[1 + i]); + if (a[i] < 0) + return a[i]; + } + + c = ((uint32_t) a[0] << 28U) | ((uint32_t) a[1] << 24U) | ((uint32_t) a[2] << 20U) | ((uint32_t) a[3] << 16U) | + ((uint32_t) a[4] << 12U) | ((uint32_t) a[5] << 8U) | ((uint32_t) a[6] << 4U) | (uint32_t) a[7]; + + /* Don't allow 0 chars */ + if (c == 0) + return -EINVAL; + + /* Don't allow invalid code points */ + if (!unichar_is_valid(c)) + return -EINVAL; + + if (c < 128) + *ret = c; + else { + if (!ret_unicode) + return -EINVAL; + + *ret = 0; + *ret_unicode = c; + } + + r = 9; + break; + } + case '0': case '1': case '2': @@ -1448,7 +1530,8 @@ static int cunescape_one(const char *p, size_t length, char *ret) { case '6': case '7': { /* octal encoding */ - int a, b, c, m; + int a, b, c; + uint32_t m; if (length != (size_t) -1 && length < 4) return -EINVAL; @@ -1470,11 +1553,11 @@ static int cunescape_one(const char *p, size_t length, char *ret) { return -EINVAL; /* Don't allow bytes above 255 */ - m = (a << 6) | (b << 3) | c; + m = ((uint32_t) a << 6U) | ((uint32_t) b << 3U) | (uint32_t) c; if (m > 255) return -EINVAL; - *ret = (char) m; + *ret = m; r = 3; break; } @@ -1507,6 +1590,8 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi for (f = s, t = r + pl; f < s + length; f++) { size_t remaining; + uint32_t u; + char c; int k; remaining = s + length - f; @@ -1525,10 +1610,11 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi continue; } + free(r); return -EINVAL; } - k = cunescape_one(f + 1, remaining - 1, t); + k = cunescape_one(f + 1, remaining - 1, &c, &u); if (k < 0) { if (flags & UNESCAPE_RELAX) { /* Invalid escape code, let's take it literal then */ @@ -1536,11 +1622,18 @@ int cunescape_length_with_prefix(const char *s, size_t length, const char *prefi continue; } + free(r); return k; } + if (c != 0) + /* Non-Unicode? Let's encode this directly */ + *(t++) = c; + else + /* Unicode? Then let's encode this in UTF-8 */ + t += utf8_encode_unichar(t, u); + f += k; - t++; } *t = 0; @@ -1563,7 +1656,7 @@ char *xescape(const char *s, const char *bad) { /* Escapes all chars in bad, in addition to \ and all special * chars, in \xFF style escaping. May be reversed with - * cunescape. */ + * cunescape(). */ r = new(char, strlen(s) * 4 + 1); if (!r) @@ -7212,20 +7305,26 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) { return -EINVAL; } - if (!GREEDY_REALLOC(s, allocated, sz+2)) + if (!GREEDY_REALLOC(s, allocated, sz+7)) return -ENOMEM; if (flags & UNQUOTE_CUNESCAPE) { - r = cunescape_one(*p, (size_t) -1, &c); + uint32_t u; + + r = cunescape_one(*p, (size_t) -1, &c, &u); if (r < 0) return -EINVAL; (*p) += r - 1; - } - s[sz++] = c; - state = VALUE; + if (c != 0) + s[sz++] = c; /* normal explicit char */ + else + sz += utf8_encode_unichar(s + sz, u); /* unicode chars we'll encode as utf8 */ + } else + s[sz++] = c; + state = VALUE; break; case SINGLE_QUOTE: @@ -7253,18 +7352,25 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) { return -EINVAL; } - if (!GREEDY_REALLOC(s, allocated, sz+2)) + if (!GREEDY_REALLOC(s, allocated, sz+7)) return -ENOMEM; if (flags & UNQUOTE_CUNESCAPE) { - r = cunescape_one(*p, (size_t) -1, &c); + uint32_t u; + + r = cunescape_one(*p, (size_t) -1, &c, &u); if (r < 0) return -EINVAL; (*p) += r - 1; - } - s[sz++] = c; + if (c != 0) + s[sz++] = c; + else + sz += utf8_encode_unichar(s + sz, u); + } else + s[sz++] = c; + state = SINGLE_QUOTE; break; @@ -7291,18 +7397,25 @@ int unquote_first_word(const char **p, char **ret, UnquoteFlags flags) { return -EINVAL; } - if (!GREEDY_REALLOC(s, allocated, sz+2)) + if (!GREEDY_REALLOC(s, allocated, sz+7)) return -ENOMEM; if (flags & UNQUOTE_CUNESCAPE) { - r = cunescape_one(*p, (size_t) -1, &c); + uint32_t u; + + r = cunescape_one(*p, (size_t) -1, &c, &u); if (r < 0) return -EINVAL; (*p) += r - 1; - } - s[sz++] = c; + if (c != 0) + s[sz++] = c; + else + sz += utf8_encode_unichar(s + sz, u); + } else + s[sz++] = c; + state = DOUBLE_QUOTE; break; @@ -7634,7 +7747,7 @@ int fd_setcrtime(int fd, usec_t usec) { return 0; } -int chattr_fd(int fd, bool b, unsigned mask) { +int chattr_fd(int fd, unsigned value, unsigned mask) { unsigned old_attr, new_attr; assert(fd >= 0); @@ -7645,21 +7758,17 @@ int chattr_fd(int fd, bool b, unsigned mask) { if (ioctl(fd, FS_IOC_GETFLAGS, &old_attr) < 0) return -errno; - if (b) - new_attr = old_attr | mask; - else - new_attr = old_attr & ~mask; - + new_attr = (old_attr & ~mask) | (value & mask); if (new_attr == old_attr) return 0; if (ioctl(fd, FS_IOC_SETFLAGS, &new_attr) < 0) return -errno; - return 0; + return 1; } -int chattr_path(const char *p, bool b, unsigned mask) { +int chattr_path(const char *p, unsigned value, unsigned mask) { _cleanup_close_ int fd = -1; assert(p); @@ -7671,29 +7780,7 @@ int chattr_path(const char *p, bool b, unsigned mask) { if (fd < 0) return -errno; - return chattr_fd(fd, b, mask); -} - -int change_attr_fd(int fd, unsigned value, unsigned mask) { - unsigned old_attr, new_attr; - - assert(fd >= 0); - - if (mask == 0) - return 0; - - if (ioctl(fd, FS_IOC_GETFLAGS, &old_attr) < 0) - return -errno; - - new_attr = (old_attr & ~mask) |(value & mask); - - if (new_attr == old_attr) - return 0; - - if (ioctl(fd, FS_IOC_SETFLAGS, &new_attr) < 0) - return -errno; - - return 0; + return chattr_fd(fd, value, mask); } int read_attr_fd(int fd, unsigned *ret) { @@ -8017,3 +8104,43 @@ int rename_noreplace(int olddirfd, const char *oldpath, int newdirfd, const char return 0; } + +char *shell_maybe_quote(const char *s) { + const char *p; + char *r, *t; + + assert(s); + + /* Encloses a string in double quotes if necessary to make it + * OK as shell string. */ + + for (p = s; *p; p++) + if (*p <= ' ' || + *p >= 127 || + strchr(SHELL_NEED_QUOTES, *p)) + break; + + if (!*p) + return strdup(s); + + r = new(char, 1+strlen(s)*2+1+1); + if (!r) + return NULL; + + t = r; + *(t++) = '"'; + t = mempcpy(t, s, p - s); + + for (; *p; p++) { + + if (strchr(SHELL_NEED_ESCAPE, *p)) + *(t++) = '\\'; + + *(t++) = *p; + } + + *(t++)= '"'; + *t = 0; + + return r; +}