cl_error_t cli_egg_bzip2_decompress(char* compressed, size_t compressed_size, char** decompressed, size_t* decompressed_size)
{
    cl_error_t status = CL_EPARSE;

    char* decoded_tmp;
    char* decoded   = NULL;
    uint32_t declen = 0, capacity = 0;

    bz_stream stream;
    int bzstat;

    if (NULL == compressed || compressed_size == 0 || NULL == decompressed || NULL == decompressed_size) {
        cli_errmsg("cli_egg_bzip2_decompress: Invalid args!\n");
        status = CL_EARG;
        goto done;
    }

    *decompressed      = NULL;
    *decompressed_size = 0;

    if (!(decoded = (char*)cli_calloc(BUFSIZ, sizeof(Bytef)))) {
        cli_errmsg("cli_egg_bzip2_decompress: cannot allocate memory for decompressed output\n");
        status = CL_EMEM;
        goto done;
    }

    capacity = BUFSIZ;

    memset(&stream, 0, sizeof(stream));
    stream.next_in   = compressed;
    stream.avail_in  = compressed_size;
    stream.next_out  = decoded;
    stream.avail_out = BUFSIZ;

    if (BZ_OK != (bzstat = BZ2_bzDecompressInit(&stream, 0, 0))) {
        cli_warnmsg("cli_egg_bzip2_decompress: bzinit failed\n");
        status = CL_EMEM;
        goto done;
    }

    /* initial inflate */
    bzstat = BZ2_bzDecompress(&stream);

    /* check if nothing written whatsoever */
    if ((bzstat != BZ_OK) && (stream.avail_out == BUFSIZ)) {
        /* Inflation failed */
        cli_errmsg("cli_egg_bzip2_decompress: failed to decompress data\n");
        status = CL_EPARSE;
        goto done;
    }

    while (bzstat == BZ_OK && stream.avail_in) {
        /* extend output capacity if needed,*/
        if (stream.avail_out == 0) {
            if (!(decoded_tmp = cli_realloc(decoded, capacity + BUFSIZ))) {
                cli_errmsg("cli_egg_bzip2_decompress: cannot reallocate memory for decompressed output\n");
                status = CL_EMEM;
                goto done;
            }
            decoded          = decoded_tmp;
            stream.next_out  = decoded + capacity;
            stream.avail_out = BUFSIZ;
            declen += BUFSIZ;
            capacity += BUFSIZ;
        }

        /* continue inflation */
        bzstat = BZ2_bzDecompress(&stream);
    }

    /* add end fragment to decoded length */
    declen += (BUFSIZ - stream.avail_out);

    /* error handling */
    switch (bzstat) {
        case BZ_OK:
            cli_dbgmsg("cli_egg_bzip2_decompress: BZ_OK on stream decompression\n");
            /* intentional fall-through */
        case BZ_STREAM_END:
            cli_dbgmsg("cli_egg_bzip2_decompress: decompressed %lu bytes from %lu total bytes (%lu bytes remaining)\n",
                       (unsigned long)declen, (unsigned long)(compressed_size), (unsigned long)(stream.avail_in));
            break;

        /* potentially fatal */
        case BZ_DATA_ERROR:
        case BZ_MEM_ERROR:
        default:
            cli_dbgmsg("cli_egg_bzip2_decompress: after decompressing %lu bytes, got error %d\n",
                       (unsigned long)declen, bzstat);

            if (declen == 0) {
                cli_dbgmsg("cli_egg_bzip2_decompress: no bytes were decompressed.\n");

                status = CL_EPARSE;
            }
            break;
    }

    *decompressed      = (char*)decoded;
    *decompressed_size = declen;

    status = CL_SUCCESS;

done:

    (void)BZ2_bzDecompressEnd(&stream);

    if (CL_SUCCESS != status) {
        free(decoded);
    }

    return status;
}