The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include "b_string.h"
#include "b_stack.h"
#include "b_util.h"

b_string *b_string_join(char *sep, b_stack *items) {
    b_string *ret;
    size_t i = 0, count = 0, off = 0, len, seplen;

    if ((ret = malloc(sizeof(*ret))) == NULL) {
        goto error_malloc;
    }

    ret->str = NULL;
    ret->len = 0;

    /*
     * First, calculate the length of the string buffer to be returned, even
     * if there are zero items on the input stack.
     */
    count  = b_stack_count(items);
    seplen = strlen(sep);
    len    = count? seplen * (count - 1): 0;

    for (i=0; i<count; i++) {
        b_string *item;

        if ((item = b_stack_item_at(items, i)) == NULL) {
            goto error_item_at_len;
        }

        len += b_string_len(item);
    }

    if ((ret->str = malloc(len + 1)) == NULL) {
        goto error_malloc_str;
    }

    ret->len = len;

    /*
     * Next, copy each item into the buffer.
     */
    for (i=0; i<count; i++) {
        b_string *item;
        size_t itemlen;

        if ((item = b_stack_item_at(items, i)) == NULL) {
            goto error_item_at;
        }

        itemlen = b_string_len(item);

        if (seplen > 0 && i) {
            if (memcpy(ret->str + off, sep, seplen) == NULL) {
                goto error_memcpy_sep;
            }

            off += seplen;
        }

        if (itemlen > 0) {
            if (memcpy(ret->str + off, item->str, itemlen) == NULL) {
                goto error_memcpy_item;
            }

            off += itemlen;
        }
    }

    /*
     * Finally, add the nul terminator.
     */
    ret->str[len] = '\0';

    return ret;

error_memcpy_item:
error_memcpy_sep:
error_item_at:
    free(ret->str);

error_malloc_str:
error_item_at_len:
    free(ret);

error_malloc:
    return NULL;
}

b_string *b_readlink(b_string *path, struct stat *st) {
    b_string *ret;
    char *buf = NULL;

    if ((buf = malloc(st->st_size + 1)) == NULL) {
        goto error_malloc_buf;
    }

    if (readlink(path->str, buf, st->st_size) < 0) {
        goto error_readlink;
    }

    buf[st->st_size] = '\x00';

    if ((ret = malloc(sizeof(*ret))) == NULL) {
        goto error_malloc_ret;
    }

    ret->str = buf;
    ret->len = st->st_size;

    return ret;

error_malloc_ret:
error_readlink:
    free(buf);

error_malloc_buf:
    return NULL;
}