Welcome to little lamb

Code » limb » master » tree

[master] / src / liblimb / esc.h / escall_scan.c

/* This file is part of limb                           https://lila.oss/limb
 * Copyright (C) 2023 Olivier Brunel                          jjk@jjacky.com */
/* SPDX-License-Identifier: GPL-2.0-only */
#include <errno.h>
#include <skalibs/fmtscan.h>
#include <limb/bytestr.h>
#include <limb/esc.h>

int
escall_scan(char *dst, size_t dlen, const char *sce, size_t slen, size_t *w, size_t *r)
{
    if (*w > dlen || *r > slen) return (errno = EINVAL, 0);

    while ((!dst || *w < dlen) && *r < slen) {
        size_t n = byte_in(sce + *r, slen - *r, "\\\"", 2);
        if (dst) {
            if (n > dlen - *w)
                n = dlen - *w;
            memmove(dst + *w, sce + *r, n);
        }
        *w += n;
        *r += n;

        if ((dst && *w == dlen) || *r == slen)
            break;
        if (sce[*r] != '\\') return (errno = EINVAL, 0);

        if (*r + 1 == slen) return (errno = ENODATA, 0);
        ++*r;
        if (sce[*r] == '\\' || sce[*r] == '"') {
            if (dst) dst[*w] = sce[*r];
        } else if (sce[*r] == 'x') {
            if (slen - *r < 2) return (--*r, errno = ENODATA, 0);
            ++*r;
            if (dst) {
                char c = fmtscan_num(sce[*r], 16);
                if (c >= 16) return (*r -= 2, errno = EINVAL, 0);
                dst[*w] = c << 4;
                c = fmtscan_num(sce[*r + 1], 16);
                if (c >= 16) return (*r -= 2, errno = EINVAL, 0);
                dst[*w] += c;
            }
            ++*r;
        } else {
            const char esc[7] = "abtnvfr";
            size_t n = byte_chr(esc, 7, sce[*r]);
            if (n == 7) return (--*r, errno = EINVAL, 0);
            if (dst) dst[*w] = 7 + n;
        }
        ++*w;
        ++*r;
    }
    if (*r < slen) return (errno = ENOBUFS, 0);

    return 1;
}