pdf14_cmykspot_put_image(gx_device * dev, gs_gstate * pgs, gx_device * target)
{
    pdf14_device * pdev = (pdf14_device *)dev;
    pdf14_buf *buf = pdev->ctx->stack;
    gs_int_rect rect = buf->rect;
    int x1, y1, width, height;
    gs_devn_params * pdevn_params = &pdev->devn_params;
    gs_separations * pseparations = &pdevn_params->separations;
    int planestride = buf->planestride;
    int rowstride = buf->rowstride;
    const byte bg = pdev->ctx->additive ? 0xff : 0;
    int num_comp = buf->n_chan - 1;
    byte *buf_ptr;

    if_debug0m('v', dev->memory, "[v]pdf14_cmykspot_put_image\n");
    rect_intersect(rect, buf->dirty);
    x1 = min(pdev->width, rect.q.x);
    y1 = min(pdev->height, rect.q.y);
    width = x1 - rect.p.x;
    height = y1 - rect.p.y;
    if (width <= 0 || height <= 0 || buf->data == NULL)
        return 0;
    buf_ptr = buf->data + rect.p.y * buf->rowstride + rect.p.x;
#if RAW_DUMP
    /* Dump the current buffer to see what we have. */
    dump_raw_buffer(pdev->ctx->stack->rect.q.y-pdev->ctx->stack->rect.p.y,
                pdev->ctx->stack->rect.q.x-pdev->ctx->stack->rect.p.x,
                                pdev->ctx->stack->n_planes,
                pdev->ctx->stack->planestride, pdev->ctx->stack->rowstride,
                "CMYK_SPOT_PUTIMAGE",pdev->ctx->stack->data);

    global_index++;
    clist_band_count++;
#endif
    return gx_put_blended_image_cmykspot(target, buf_ptr, planestride, rowstride,
                      rect.p.x, rect.p.y, width, height, num_comp, bg,
                      buf->has_tags, rect, pseparations);
}


pdf14_get_cmap_procs(const gs_gstate *pgs, const gx_device * dev)
{
    /* The pdf14 marking device itself is always continuous tone. */
    return &pdf14_cmap_many;
}


pdf14_output_page(gx_device * dev, int num_copies, int flush)
{
    pdf14_device * pdev = (pdf14_device *)dev;

    if (pdev->target != NULL)
        return (*dev_proc(pdev->target, output_page)) (pdev->target, num_copies, flush);
    return 0;
}


pdf14_copy_alpha_color(gx_device * dev, const byte * data, int data_x,
           int aa_raster, gx_bitmap_id id, int x, int y, int w, int h,
                      gx_color_index color, const gx_device_color *pdc,
                      int depth, bool devn)
{
    const byte *aa_row;
    pdf14_device *pdev = (pdf14_device *)dev;
    pdf14_buf *buf = pdev->ctx->stack;
    int i, j, k;
    byte *line, *dst_ptr;
    byte src[PDF14_MAX_PLANES];
    byte dst[PDF14_MAX_PLANES] = { 0 };
    gs_blend_mode_t blend_mode = pdev->blend_mode;
    bool additive = pdev->ctx->additive;
    int rowstride = buf->rowstride;
    int planestride = buf->planestride;
    gs_graphics_type_tag_t curr_tag = GS_UNKNOWN_TAG;  /* Quiet compiler */
    bool has_alpha_g = buf->has_alpha_g;
    bool has_shape = buf->has_shape;
    bool has_tags = buf->has_tags;
    bool knockout = buf->knockout;
    int num_chan = buf->n_chan;
    int num_comp = num_chan - 1;
    int shape_off = num_chan * planestride;
    int alpha_g_off = shape_off + (has_shape ? planestride : 0);
    int tag_off = alpha_g_off + (has_alpha_g ? planestride : 0);
    bool overprint = pdev->overprint;
    gx_color_index drawn_comps = pdev->drawn_comps;
    gx_color_index comps;
    byte shape = 0; /* Quiet compiler. */
    byte src_alpha;
    int alpha2_aa, alpha_aa, sx;
    int alpha_aa_act;
    int xoff;
    gx_color_index mask = ((gx_color_index)1 << 8) - 1;
    int shift = 8;

    if (buf->data == NULL)
        return 0;
    aa_row = data;
    if (has_tags) {
        curr_tag = (color >> (num_comp*8)) & 0xff;
    }

    if (devn) {
        if (additive) {
            for (j = 0; j < num_comp; j++) {
                src[j] = ((pdc->colors.devn.values[j]) >> shift & mask);
            }
        } else {
            for (j = 0; j < num_comp; j++) {
                src[j] = 255 - ((pdc->colors.devn.values[j]) >> shift & mask);
            }
        }
    } else
        pdev->pdf14_procs->unpack_color(num_comp, color, pdev, src);
    src_alpha = src[num_comp] = (byte)floor (255 * pdev->alpha + 0.5);
    if (has_shape)
        shape = (byte)floor (255 * pdev->shape + 0.5);
    /* Limit the area we write to the bounding rectangle for this buffer */
    if (x < buf->rect.p.x) {
        xoff = data_x + buf->rect.p.x - x;
        w += x - buf->rect.p.x;
        x = buf->rect.p.x;
    } else {
        xoff = data_x;
    }
    if (y < buf->rect.p.y) {
      h += y - buf->rect.p.y;
      aa_row -= (y - buf->rect.p.y) * aa_raster;
      y = buf->rect.p.y;
    }
    if (x + w > buf->rect.q.x) w = buf->rect.q.x - x;
    if (y + h > buf->rect.q.y) h = buf->rect.q.y - y;
    /* Update the dirty rectangle. */
    if (x < buf->dirty.p.x) buf->dirty.p.x = x;
    if (y < buf->dirty.p.y) buf->dirty.p.y = y;
    if (x + w > buf->dirty.q.x) buf->dirty.q.x = x + w;
    if (y + h > buf->dirty.q.y) buf->dirty.q.y = y + h;
    line = buf->data + (x - buf->rect.p.x) + (y - buf->rect.p.y) * rowstride;

    for (j = 0; j < h; ++j, aa_row += aa_raster) {
        dst_ptr = line;
        sx = xoff;
        for (i = 0; i < w; ++i, ++sx) {
            /* Complement the components for subtractive color spaces */
            if (additive) {
                for (k = 0; k < num_chan; ++k)		/* num_chan includes alpha */
                    dst[k] = dst_ptr[k * planestride];
            } else { /* Complement the components for subtractive color spaces */
                for (k = 0; k < num_comp; ++k)
                    dst[k] = 255 - dst_ptr[k * planestride];
                dst[num_comp] = dst_ptr[num_comp * planestride];	/* alpha */
            }
            /* Get the aa alpha from the buffer */
            switch(depth)
            {
            case 2:  /* map 0 - 3 to 0 - 255 */
                alpha_aa = ((aa_row[sx >> 2] >> ((3 - (sx & 3)) << 1)) & 3) * 85;
                break;
            case 4:
                alpha2_aa = aa_row[sx >> 1];
                alpha_aa = (sx & 1 ? alpha2_aa & 0xf : alpha2_aa >> 4) * 17;
                break;
            case 8:
                alpha_aa = aa_row[sx];
                break;
            default:
                return_error(gs_error_rangecheck);
            }
            if (alpha_aa != 0) {  /* This does happen */
                if (alpha_aa != 255) {
                    /* We have an alpha value from aa */
                    alpha_aa_act = alpha_aa;
                    if (src_alpha != 255) {
                        /* Need to combine it with the existing alpha */
                        int tmp = src_alpha * alpha_aa_act + 0x80;
                        alpha_aa_act = (tmp + (tmp >> 8)) >> 8;
                    }
                    /* Set our source alpha value appropriately */
                    src[num_comp] = alpha_aa_act;
                } else {
                    /* We may have to reset this is it was changed as we
                       moved across the row */
                    src[num_comp] = src_alpha;
                }
                if (knockout) {
                    if (has_shape) {
                        art_pdf_composite_knockout_8(dst, src, num_comp, blend_mode,
                                            pdev->blend_procs, pdev);
                    } else {
                        art_pdf_knockoutisolated_group_aa_8(dst, src, src_alpha,
                                            alpha_aa, num_comp, pdev);
                    }
                } else {
                    art_pdf_composite_pixel_alpha_8(dst, src, num_comp, blend_mode,
                                                    pdev->blend_procs, pdev);
                }
                /* Complement the results for subtractive color spaces */
                if (additive) {
                    for (k = 0; k < num_chan; ++k)
                        dst_ptr[k * planestride] = dst[k];
                } else {
                    if (overprint && dst_ptr[num_comp * planestride] != 0) {
                        for (k = 0, comps = drawn_comps; comps != 0;
                                ++k, comps >>= 1) {
                            if ((comps & 0x1) != 0) {
                                dst_ptr[k * planestride] = 255 - dst[k];
                            }
                        }
                        /* The alpha channel */
                        dst_ptr[num_comp * planestride] = dst[num_comp];
                    } else {
                        for (k = 0; k < num_comp; ++k)
                            dst_ptr[k * planestride] = 255 - dst[k];
                        /* The alpha channel */
                        dst_ptr[num_comp * planestride] = dst[num_comp];
                    }
                }
                if (has_alpha_g) {
                    int tmp = (255 - dst_ptr[alpha_g_off]) * (255 - src[num_comp]) + 0x80;
                    dst_ptr[alpha_g_off] = 255 - ((tmp + (tmp >> 8)) >> 8);
                }
                if (has_shape) {
                    int tmp = (255 - dst_ptr[shape_off]) * (255 - shape) + 0x80;
                    dst_ptr[shape_off] = 255 - ((tmp + (tmp >> 8)) >> 8);
                }
                if (has_tags) {
                    /* If alpha is 100% then set to pure path, else or */
                    if (src[num_comp] == 255) {
                        dst_ptr[tag_off] = curr_tag;
                    } else {
                        dst_ptr[tag_off] = ( dst_ptr[tag_off] |curr_tag ) & ~GS_UNTOUCHED_TAG;
                    }
                }
            }
            ++dst_ptr;
        }
        line += rowstride;
    }
    return 0;
}


update_lop_for_pdf14(gs_gstate *pgs, const gx_drawing_color *pdcolor)
{
    bool hastrans = false;

    /* We'd really rather not have to set the pdf14 bit in the lop, as this
     * makes other operations much slower. We have no option however, if the
     * current colour involves transparency, or if it's anything other than
     * a completely solid (or transparent) operation in the normal blend mode.
     */
    if (pdcolor != NULL)
    {
        if (gx_dc_is_pattern1_color(pdcolor) &&
            gx_pattern1_get_transptr(pdcolor) != NULL) {
            hastrans = true;
        } else if (gx_dc_is_pattern2_color(pdcolor)) {
            /* FIXME: Here we assume that ALL type 2 patterns are
             * transparent - this test could be better. */
            hastrans = true;
        }
    }
    /* The only idempotent blend modes are Normal, Darken and Lighten */
    if ((pgs->alpha != 0xFFFF) ||
        (pgs->blend_mode != BLEND_MODE_Normal && pgs->blend_mode != BLEND_MODE_Darken && pgs->blend_mode != BLEND_MODE_Lighten) ||
        (pgs->opacity.alpha != 1.0) ||
        (pgs->shape.alpha != 1.0) ||
        (hastrans))
    {
        /*
         * The blend operations are not idempotent.  Force non-idempotent
         * filling and stroking operations.
         */
        pgs->log_op |= lop_pdf14;
    }
}


pdf14_text_begin(gx_device * dev, gs_gstate * pgs,
                 const gs_text_params_t * text, gs_font * font,
                 gx_path * path, const gx_device_color * pdcolor,
                 const gx_clip_path * pcpath, gs_memory_t * memory,
                 gs_text_enum_t ** ppenum)
{
    int code;
    gs_text_enum_t *penum;

    if_debug0m('v', memory, "[v]pdf14_text_begin\n");
    pdf14_set_marking_params(dev, pgs);
    code = gx_default_text_begin(dev, pgs, text, font, path, pdcolor, pcpath,
                                 memory, &penum);
    if (code < 0)
        return code;
    *ppenum = (gs_text_enum_t *)penum;
    return code;
}


c_pdf14trans_write_ctm(byte **ppbuf, const gs_pdf14trans_params_t *pparams)
{
    /* Note: We can't skip writing CTM if it is equal to pgs->ctm,
       because clist writer may skip this command for some bands.
       For a better result we need individual CTM for each band.
     */
    byte *pbuf = *ppbuf;
    int len, code;

    len = cmd_write_ctm_return_length_nodevice(&pparams->ctm);
    pbuf--; /* For cmd_write_ctm. */
    code = cmd_write_ctm(&pparams->ctm, pbuf, len);
    if (code < 0)
        return code;
    pbuf += len + 1;
    *ppbuf = pbuf;
    return 0;
}


pdf14_clist_fill_path(gx_device	*dev, const gs_gstate *pgs,
                           gx_path *ppath, const gx_fill_params *params,
                           const gx_drawing_color *pdcolor,
                           const gx_clip_path *pcpath)
{
    pdf14_clist_device * pdev = (pdf14_clist_device *)dev;
    gs_gstate new_pgs = *pgs;
    int code;
    gs_pattern2_instance_t *pinst = NULL;
    gx_device_forward * fdev = (gx_device_forward *)dev;
    cmm_dev_profile_t *dev_profile, *fwd_profile;
    gsicc_rendering_param_t render_cond;
    cmm_profile_t *icc_profile_fwd, *icc_profile_dev;

    code = dev_proc(dev, get_profile)(dev,  &dev_profile);
    if (code < 0)
        return code;
    code = dev_proc(fdev->target, get_profile)(fdev->target,  &fwd_profile);
    if (code < 0)
        return code;

    gsicc_extract_profile(GS_UNKNOWN_TAG, fwd_profile, &icc_profile_fwd,
                          &render_cond);
    gsicc_extract_profile(GS_UNKNOWN_TAG, dev_profile, &icc_profile_dev,
                          &render_cond);

    /*
     * Ensure that that the PDF 1.4 reading compositor will have the current
     * blending parameters.  This is needed since the fill_rectangle routines
     * do not have access to the gs_gstate.  Thus we have to pass any
     * changes explictly.
     */
    code = pdf14_clist_update_params(pdev, pgs, false, NULL);
    if (code < 0)
        return code;
    /* If we are doing a shading fill and we are in a transparency group of a
       different color space, then we do not want to do the shading in the
       device color space. It must occur in the source space.  To handle it in
       the device space would require knowing all the nested transparency group
       color space as well as the transparency.  Some of the shading code ignores
       this, so we have to pass on the clist_writer device to enable proper
       mapping to the transparency group color space. */

    if (pdcolor != NULL && gx_dc_is_pattern2_color(pdcolor)) {
        pinst =
            (gs_pattern2_instance_t *)pdcolor->ccolor.pattern;
           pinst->saved->has_transparency = true;
           /* The transparency color space operations are driven by the pdf14
              clist writer device.  */
           pinst->saved->trans_device = dev;
    }
    update_lop_for_pdf14(&new_pgs, pdcolor);
    new_pgs.trans_device = dev;
    new_pgs.has_transparency = true;
    code = gx_forward_fill_path(dev, &new_pgs, ppath, params, pdcolor, pcpath);
    new_pgs.trans_device = NULL;
    new_pgs.has_transparency = false;
    if (pinst != NULL){
        pinst->saved->trans_device = NULL;
    }
    return code;
}


ref_param_write(iparam_list * plist, gs_param_name pkey, const ref * pvalue)
{
    ref kref;
    int code;

    if (!ref_param_requested((gs_param_list *) plist, pkey))
        return 0;
    code = ref_param_key(plist, pkey, &kref);
    if (code < 0)
        return code;
    return (*plist->u.w.write) (plist, &kref, pvalue);
}


ref_param_read_get_policy(gs_param_list * plist, gs_param_name pkey)
{
    iparam_list *const iplist = (iparam_list *) plist;
    ref *pvalue;

    if (!(r_has_type(&iplist->u.r.policies, t_dictionary) &&
          dict_find_string(&iplist->u.r.policies, pkey, &pvalue) > 0 &&
          r_has_type(pvalue, t_integer))
        )
        return gs_param_policy_ignore;
    return (int)pvalue->value.intval;
}


dict_param_list_read(dict_param_list * plist, const ref * pdict,
                     const ref * ppolicies, bool require_all,
                     gs_ref_memory_t *imem)
{
    iparam_list *const iplist = (iparam_list *) plist;
    uint count;

    if (pdict == 0) {
        plist->u.r.read = empty_param_read;
        count = 0;
    } else {
        check_dict_read(*pdict);
        plist->u.r.read = dict_param_read;
        plist->dict = *pdict;
        count = dict_max_index(pdict) + 1;
    }
    plist->enumerate = dict_param_enumerate;
    return ref_param_read_init(iplist, count, ppolicies, require_all, imem);
}


ref_param_begin_write_collection(gs_param_list * plist, gs_param_name pkey,
                                 gs_param_dict * pvalue,
                                 gs_param_collection_type_t coll_type)
{
    iparam_list *const iplist = (iparam_list *) plist;
    gs_ref_memory_t *imem = iplist->ref_memory;
    dict_param_list *dlist = (dict_param_list *)
        gs_alloc_bytes(plist->memory, size_of(dict_param_list),
                       "ref_param_begin_write_collection");
    int code;

    if (dlist == 0)
        return_error(gs_error_VMerror);
    if (coll_type != gs_param_collection_array) {
        ref dref;

        code = dict_alloc(imem, pvalue->size, &dref);
        if (code >= 0) {
            code = dict_param_list_write(dlist, &dref, NULL, imem);
            dlist->int_keys = coll_type == gs_param_collection_dict_int_keys;
        }
    } else {
        ref aref;

        code = gs_alloc_ref_array(imem, &aref, a_all, pvalue->size,
                                  "ref_param_begin_write_collection");
        if (code >= 0)
            code = array_new_indexed_plist_write(dlist, &aref, NULL, imem);
    }
    if (code < 0)
        gs_free_object(plist->memory, dlist, "ref_param_begin_write_collection");
    else
        pvalue->list = (gs_param_list *) dlist;
    return code;
}


array_param_list_read(array_param_list * plist, ref * bot, uint count,
                      const ref * ppolicies, bool require_all,
                      gs_ref_memory_t *imem)
{
    iparam_list *const iplist = (iparam_list *) plist;

    if (count & 1)
        return_error(gs_error_rangecheck);
    plist->u.r.read = array_param_read;
    plist->enumerate = array_param_enumerate;
    plist->bot = bot;
    plist->top = bot + count;
    return ref_param_read_init(iplist, count, ppolicies, require_all, imem);
}


gx_image_scale_mask_colors(gx_image_enum *penum, int component_index)
{
    uint scale = 255 / ((1 << penum->bps) - 1);
    uint *values = &penum->mask_color.values[component_index * 2];
    uint v0 = values[0] *= scale;
    uint v1 = values[1] *= scale;

    if (penum->map[component_index].decoding == sd_none &&
        penum->map[component_index].inverted
        ) {
        values[0] = 255 - v1;
        values[1] = 255 - v0;
    }
}


gx_image_enum_begin(gx_device * dev, const gs_gstate * pgs,
                    const gs_matrix *pmat, const gs_image_common_t * pic,
                const gx_drawing_color * pdcolor, const gx_clip_path * pcpath,
                gs_memory_t * mem, gx_image_enum *penum)
{
    const gs_pixel_image_t *pim = (const gs_pixel_image_t *)pic;
    gs_image_format_t format = pim->format;
    const int width = pim->Width;
    const int height = pim->Height;
    const int bps = pim->BitsPerComponent;
    bool masked = penum->masked;
    const float *decode = pim->Decode;
    gs_matrix_double mat;
    int index_bps;
    const gs_color_space *pcs = pim->ColorSpace;
    gs_logical_operation_t lop = (pgs ? pgs->log_op : lop_default);
    int code;
    int log2_xbytes = (bps <= 8 ? 0 : arch_log2_sizeof_frac);
    int spp, nplanes, spread;
    uint bsize;
    byte *buffer;
    fixed mtx, mty;
    gs_fixed_point row_extent, col_extent, x_extent, y_extent;
    bool device_color = true;
    gs_fixed_rect obox, cbox;
    bool gridfitimages = 0;
    bool in_pattern_accumulator = 0;
    int orthogonal;
    int force_interpolation = 0;

    penum->clues = NULL;
    penum->icc_setup.has_transfer = false;
    penum->icc_setup.is_lab = false;
    penum->icc_setup.must_halftone = false;
    penum->icc_setup.need_decode = false;
    penum->Width = width;
    penum->Height = height;

    if ((code = gx_image_compute_mat(pgs, pmat, &(pim->ImageMatrix), &mat)) < 0) {
        return code;
    }
    /* Grid fit: A common construction in postscript/PDF files is for images
     * to be constructed as a series of 'stacked' 1 pixel high images.
     * Furthermore, many of these are implemented as an imagemask plotted on
     * top of thin rectangles. The different fill rules for images and line
     * art produces problems; line art fills a pixel if any part of it is
     * touched - images only fill a pixel if the centre of the pixel is
     * covered. Bug 692666 is such a problem.
     *
     * As a workaround for this problem, the code below was introduced. The
     * concept is that orthogonal images can be 'grid fitted' (or 'stretch')
     * to entirely cover pixels that they touch. Initially I had this working
     * for all images regardless of type, but as testing has proceeded, this
     * showed more and more regressions, so I've cut the cases back in which
     * this code is used until it now only triggers on imagemasks that are
     * either 1 pixel high, or wide, and then not if we are rendering a
     * glyph (such as from a type3 font).
     */

    /* Ask the device if we are in a pattern accumulator */
    in_pattern_accumulator = (dev_proc(dev, dev_spec_op)(dev, gxdso_in_pattern_accumulator, NULL, 0));
    if (in_pattern_accumulator < 0)
        in_pattern_accumulator = 0;

    /* Figure out if we are orthogonal */
    if (mat.xy == 0 && mat.yx == 0)
        orthogonal = 1;
    else if (mat.xx == 0 && mat.yy == 0)
        orthogonal = 2;
    else
        orthogonal = 0;

    /* If we are in a pattern accumulator, we choose to always grid fit
     * orthogonal images. We do this by asking the device whether we
     * should grid fit. This allows us to avoid nasty blank lines around
     * the edges of cells.
     */
    gridfitimages = in_pattern_accumulator && orthogonal;

    if (pgs != NULL && pgs->show_gstate != NULL) {
        /* If we're a graphics state, and we're in a text object, then we
         * must be in a type3 font. Don't fiddle with it. */
    } else if (!gridfitimages &&
               (!penum->masked || penum->image_parent_type != 0)) {
        /* Other than for images we are specifically looking to grid fit (such as
         * ones in a pattern device), we only grid fit imagemasks */
    } else if (gridfitimages && (penum->masked && penum->image_parent_type == 0)) {
        /* We don't gridfit imagemasks in a pattern accumulator */
    } else if (pgs != NULL && pgs->fill_adjust.x == 0 && pgs->fill_adjust.y == 0) {
        /* If fill adjust is disabled, so is grid fitting */
    } else if (orthogonal == 1) {
        if (width == 1 || gridfitimages) {
            if (mat.xx > 0) {
                fixed ix0 = int2fixed(fixed2int(float2fixed(mat.tx)));
                double x1 = mat.tx + mat.xx * width;
                fixed ix1 = int2fixed(fixed2int_ceiling(float2fixed(x1)));
                mat.tx = (double)fixed2float(ix0);
                mat.xx = (double)(fixed2float(ix1 - ix0)/width);
            } else if (mat.xx < 0) {
                fixed ix0 = int2fixed(fixed2int_ceiling(float2fixed(mat.tx)));
                double x1 = mat.tx + mat.xx * width;
                fixed ix1 = int2fixed(fixed2int(float2fixed(x1)));
                mat.tx = (double)fixed2float(ix0);
                mat.xx = (double)(fixed2float(ix1 - ix0)/width);
            }
        }
        if (height == 1 || gridfitimages) {
            if (mat.yy > 0) {
                fixed iy0 = int2fixed(fixed2int(float2fixed(mat.ty)));
                double y1 = mat.ty + mat.yy * height;
                fixed iy1 = int2fixed(fixed2int_ceiling(float2fixed(y1)));
                mat.ty = (double)fixed2float(iy0);
                mat.yy = (double)(fixed2float(iy1 - iy0)/height);
            } else if (mat.yy < 0) {
                fixed iy0 = int2fixed(fixed2int_ceiling(float2fixed(mat.ty)));
                double y1 = mat.ty + mat.yy * height;
                fixed iy1 = int2fixed(fixed2int(float2fixed(y1)));
                mat.ty = (double)fixed2float(iy0);
                mat.yy = ((double)fixed2float(iy1 - iy0)/height);
            }
        }
    } else if (orthogonal == 2) {
        if (height == 1 || gridfitimages) {
            if (mat.yx > 0) {
                fixed ix0 = int2fixed(fixed2int(float2fixed(mat.tx)));
                double x1 = mat.tx + mat.yx * height;
                fixed ix1 = int2fixed(fixed2int_ceiling(float2fixed(x1)));
                mat.tx = (double)fixed2float(ix0);
                mat.yx = (double)(fixed2float(ix1 - ix0)/height);
            } else if (mat.yx < 0) {
                fixed ix0 = int2fixed(fixed2int_ceiling(float2fixed(mat.tx)));
                double x1 = mat.tx + mat.yx * height;
                fixed ix1 = int2fixed(fixed2int(float2fixed(x1)));
                mat.tx = (double)fixed2float(ix0);
                mat.yx = (double)(fixed2float(ix1 - ix0)/height);
            }
        }
        if (width == 1 || gridfitimages) {
            if (mat.xy > 0) {
                fixed iy0 = int2fixed(fixed2int(float2fixed(mat.ty)));
                double y1 = mat.ty + mat.xy * width;
                fixed iy1 = int2fixed(fixed2int_ceiling(float2fixed(y1)));
                mat.ty = (double)fixed2float(iy0);
                mat.xy = (double)(fixed2float(iy1 - iy0)/width);
            } else if (mat.xy < 0) {
                fixed iy0 = int2fixed(fixed2int_ceiling(float2fixed(mat.ty)));
                double y1 = mat.ty + mat.xy * width;
                fixed iy1 = int2fixed(fixed2int(float2fixed(y1)));
                mat.ty = (double)fixed2float(iy0);
                mat.xy = ((double)fixed2float(iy1 - iy0)/width);
            }
        }
    }

    /* When rendering to a pattern accumulator, if we are downscaling
     * then enable interpolation, as otherwise dropouts can cause
     * serious problems. */
    if (in_pattern_accumulator) {
        double ome = ((double)(fixed_1 - fixed_epsilon)) / (double)fixed_1; /* One Minus Epsilon */

        if (orthogonal == 1) {
            if ((mat.xx > -ome && mat.xx < ome) || (mat.yy > -ome && mat.yy < ome)) {
                force_interpolation = true;
            }
        } else if (orthogonal == 2) {
            if ((mat.xy > -ome && mat.xy < ome) || (mat.yx > -ome && mat.yx < ome)) {
                force_interpolation = true;
            }
        }
    }

    /* Can we restrict the amount of image we need? */
    while (pcpath) /* So we can break out of it */
    {
        gs_rect rect, rect_out;
        gs_matrix mi;
        const gs_matrix *m = pgs != NULL ? &ctm_only(pgs) : NULL;
        gs_fixed_rect obox;
        gs_int_rect irect;
        if (m == NULL || (code = gs_matrix_invert(m, &mi)) < 0 ||
            (code = gs_matrix_multiply(&mi, &pic->ImageMatrix, &mi)) < 0) {
            /* Give up trying to shrink the render box, but continue processing */
            break;
        }
        gx_cpath_outer_box(pcpath, &obox);
        rect.p.x = fixed2float(obox.p.x);
        rect.p.y = fixed2float(obox.p.y);
        rect.q.x = fixed2float(obox.q.x);
        rect.q.y = fixed2float(obox.q.y);
        code = gs_bbox_transform(&rect, &mi, &rect_out);
        if (code < 0) {
            /* Give up trying to shrink the render box, but continue processing */
            break;
        }
        irect.p.x = (int)(rect_out.p.x-1.0);
        irect.p.y = (int)(rect_out.p.y-1.0);
        irect.q.x = (int)(rect_out.q.x+1.0);
        irect.q.y = (int)(rect_out.q.y+1.0);
        /* Need to expand the region to allow for the fact that the mitchell
         * scaler reads multiple pixels in. This calculation can probably be
         * improved. */
        /* If mi.{xx,yy} > 1 then we are downscaling. During downscaling,
         * the support increases to ensure that we don't lose pixels contributions
         * entirely. */
        /* I do not understand the need for the +/- 1 fudge factors,
         * but they seem to be required. Increasing the render rectangle can
         * never be bad at least... RJW */
        {
            float support = any_abs(mi.xx);
            int isupport;
            if (any_abs(mi.yy) > support)
                support = any_abs(mi.yy);
            if (any_abs(mi.xy) > support)
                support = any_abs(mi.xy);
            if (any_abs(mi.yx) > support)
                support = any_abs(mi.yx);
            isupport = (int)(MAX_ISCALE_SUPPORT * (support+1)) + 1;
            irect.p.x -= isupport;
            irect.p.y -= isupport;
            irect.q.x += isupport;
            irect.q.y += isupport;
        }
        if (penum->rrect.x < irect.p.x) {
            penum->rrect.w -= irect.p.x - penum->rrect.x;
            if (penum->rrect.w < 0)
               penum->rrect.w = 0;
            penum->rrect.x = irect.p.x;
        }
        if (penum->rrect.x + penum->rrect.w > irect.q.x) {
            penum->rrect.w = irect.q.x - penum->rrect.x;
            if (penum->rrect.w < 0)
                penum->rrect.w = 0;
        }
        if (penum->rrect.y < irect.p.y) {
            penum->rrect.h -= irect.p.y - penum->rrect.y;
            if (penum->rrect.h < 0)
                penum->rrect.h = 0;
            penum->rrect.y = irect.p.y;
        }
        if (penum->rrect.y + penum->rrect.h > irect.q.y) {
            penum->rrect.h = irect.q.y - penum->rrect.y;
            if (penum->rrect.h < 0)
                penum->rrect.h = 0;
        }
        break; /* Out of the while */
    }
    /* Check for the intersection being null */
    if (penum->rrect.x + penum->rrect.w <= penum->rect.x  ||
        penum->rect.x  + penum->rect.w  <= penum->rrect.x ||
        penum->rrect.y + penum->rrect.h <= penum->rect.y  ||
        penum->rect.y  + penum->rect.h  <= penum->rrect.y)
    {
          /* Something may have gone wrong with the floating point above.
           * set the region to something sane. */
        penum->rrect.x = penum->rect.x;
        penum->rrect.y = penum->rect.y;
        penum->rrect.w = 0;
        penum->rrect.h = 0;
    }

    /*penum->matrix = mat;*/
    penum->matrix.xx = mat.xx;
    penum->matrix.xy = mat.xy;
    penum->matrix.yx = mat.yx;
    penum->matrix.yy = mat.yy;
    penum->matrix.tx = mat.tx;
    penum->matrix.ty = mat.ty;
    if_debug6m('b', mem, " [%g %g %g %g %g %g]\n",
              mat.xx, mat.xy, mat.yx, mat.yy, mat.tx, mat.ty);
    /* following works for 1, 2, 4, 8, 12, 16 */
    index_bps = (bps < 8 ? bps >> 1 : (bps >> 2) + 1);
    /*
     * Compute extents with distance transformation.
     */
    if (mat.tx > 0)
        mtx = float2fixed(mat.tx);
    else { /* Use positive values to ensure round down. */
        int f = (int)-mat.tx + 1;

        mtx = float2fixed(mat.tx + f) - int2fixed(f);
    }
    if (mat.ty > 0)
        mty = float2fixed(mat.ty);
    else {  /* Use positive values to ensure round down. */
        int f = (int)-mat.ty + 1;

        mty = float2fixed(mat.ty + f) - int2fixed(f);
    }

    row_extent.x = float2fixed_rounded_boxed(width * mat.xx);
    row_extent.y =
        (is_fzero(mat.xy) ? fixed_0 :
         float2fixed_rounded_boxed(width * mat.xy));
    col_extent.x =
        (is_fzero(mat.yx) ? fixed_0 :
         float2fixed_rounded_boxed(height * mat.yx));
    col_extent.y = float2fixed_rounded_boxed(height * mat.yy);
    gx_image_enum_common_init((gx_image_enum_common_t *)penum,
                              (const gs_data_image_t *)pim,
                              &image1_enum_procs, dev,
                              (masked ? 1 : (penum->alpha ? cs_num_components(pcs)+1 : cs_num_components(pcs))),
                              format);
    if (penum->rect.w == width && penum->rect.h == height) {
        x_extent = row_extent;
        y_extent = col_extent;
    } else {
        int rw = penum->rect.w, rh = penum->rect.h;

        x_extent.x = float2fixed_rounded_boxed(rw * mat.xx);
        x_extent.y =
            (is_fzero(mat.xy) ? fixed_0 :
             float2fixed_rounded_boxed(rw * mat.xy));
        y_extent.x =
            (is_fzero(mat.yx) ? fixed_0 :
             float2fixed_rounded_boxed(rh * mat.yx));
        y_extent.y = float2fixed_rounded_boxed(rh * mat.yy);
    }
    /* Set icolor0 and icolor1 to point to image clues locations if we have
       1spp or an imagemask, otherwise image clues is not used and
       we have these values point to other member variables */
    if (masked || cs_num_components(pcs) == 1) {
        /* Go ahead and allocate now if not already done.  For a mask
           we really should only do 2 values. For now, the goal is to
           eliminate the 256 bytes for the >8bpp image enumerator */
        penum->clues = (gx_image_clue*) gs_alloc_bytes(mem, sizeof(gx_image_clue)*256,
                             "gx_image_enum_begin");
        if (penum->clues == NULL)
            return_error(gs_error_VMerror);
        penum->icolor0 = &(penum->clues[0].dev_color);
        penum->icolor1 = &(penum->clues[255].dev_color);
    } else {
        penum->icolor0 = &(penum->icolor0_val);
        penum->icolor1 = &(penum->icolor1_val);
    }
    if (masked) {       /* This is imagemask. */
        if (bps != 1 || pcs != NULL || penum->alpha || decode[0] == decode[1]) {
            return_error(gs_error_rangecheck);
        }
        /* Initialize color entries 0 and 255. */
        set_nonclient_dev_color(penum->icolor0, gx_no_color_index);
        set_nonclient_dev_color(penum->icolor1, gx_no_color_index);
        *(penum->icolor1) = *pdcolor;
        memcpy(&penum->map[0].table.lookup4x1to32[0],
               (decode[0] < decode[1] ? lookup4x1to32_inverted :
                lookup4x1to32_identity),
               16 * 4);
        penum->map[0].decoding = sd_none;
        spp = 1;
        lop = rop3_know_S_0(lop);
    } else {                    /* This is image, not imagemask. */
        const gs_color_space_type *pcst = pcs->type;
        int b_w_color;

        spp = cs_num_components(pcs);
        if (spp < 0) {          /* Pattern not allowed */
            return_error(gs_error_rangecheck);
        }
        if (penum->alpha)
            ++spp;
        /* Use a less expensive format if possible. */
        switch (format) {
        case gs_image_format_bit_planar:
            if (bps > 1)
                break;
            format = gs_image_format_component_planar;
        case gs_image_format_component_planar:
            if (spp == 1)
                format = gs_image_format_chunky;
        default:                /* chunky */
            break;
        }

        if (pcs->cmm_icc_profile_data != NULL) {
            device_color = false;
        } else {
            device_color = (*pcst->concrete_space) (pcs, pgs) == pcs;
        }

        code = image_init_colors(penum, bps, spp, format, decode, pgs, dev,
                          pcs, &device_color);
        if (code < 0)
            return gs_throw(code, "Image colors initialization failed");
        /* If we have a CIE based color space and the icc equivalent profile
           is not yet set, go ahead and handle that now.  It may already
           be done due to the above init_colors which may go through remap. */
        if (gs_color_space_is_PSCIE(pcs) && pcs->icc_equivalent == NULL) {
            code = gs_colorspace_set_icc_equivalent((gs_color_space *)pcs, &(penum->icc_setup.is_lab),
                                                pgs->memory);
            if (code < 0)
                return code;
            if (penum->icc_setup.is_lab) {
                /* Free what ever profile was created and use the icc manager's
                   cielab profile */
                gs_color_space *curr_pcs = (gs_color_space *)pcs;
                rc_decrement(curr_pcs->icc_equivalent,"gx_image_enum_begin");
                rc_decrement(curr_pcs->cmm_icc_profile_data,"gx_image_enum_begin");
                curr_pcs->cmm_icc_profile_data = pgs->icc_manager->lab_profile;
                rc_increment(curr_pcs->cmm_icc_profile_data);
            }
        }
        /* Try to transform non-default RasterOps to something */
        /* that we implement less expensively. */
        if (!pim->CombineWithColor)
            lop = rop3_know_T_0(lop) & ~lop_T_transparent;
        else if ((rop3_uses_T(lop) && color_draws_b_w(dev, pdcolor) == 0))
            lop = rop3_know_T_0(lop);

        if (lop != rop3_S &&    /* if best case, no more work needed */
            !rop3_uses_T(lop) && bps == 1 && spp == 1 &&
            (b_w_color =
             color_draws_b_w(dev, penum->icolor0)) >= 0 &&
            color_draws_b_w(dev, penum->icolor1) == (b_w_color ^ 1)
            ) {
            if (b_w_color) {    /* Swap the colors and invert the RasterOp source. */
                gx_device_color dcolor;

                dcolor = *(penum->icolor0);
                *(penum->icolor0) = *(penum->icolor1);
                *(penum->icolor1) = dcolor;
                lop = rop3_invert_S(lop);
            }
            /*
             * At this point, we know that the source pixels
             * correspond directly to the S input for the raster op,
             * i.e., icolor0 is black and icolor1 is white.
             */
            switch (lop) {
                case rop3_D & rop3_S:
                    /* Implement this as an inverted mask writing 0s. */
                    *(penum->icolor1) = *(penum->icolor0);
                    /* (falls through) */
                case rop3_D | rop3_not(rop3_S):
                    /* Implement this as an inverted mask writing 1s. */
                    memcpy(&penum->map[0].table.lookup4x1to32[0],
                           lookup4x1to32_inverted, 16 * 4);
                  rmask:        /* Fill in the remaining parameters for a mask. */
                    penum->masked = masked = true;
                    set_nonclient_dev_color(penum->icolor0, gx_no_color_index);
                    penum->map[0].decoding = sd_none;
                    lop = rop3_T;
                    break;
                case rop3_D & rop3_not(rop3_S):
                    /* Implement this as a mask writing 0s. */
                    *(penum->icolor1) = *(penum->icolor0);
                    /* (falls through) */
                case rop3_D | rop3_S:
                    /* Implement this as a mask writing 1s. */
                    memcpy(&penum->map[0].table.lookup4x1to32[0],
                           lookup4x1to32_identity, 16 * 4);
                    goto rmask;
                default:
                    ;
            }
        }
    }
    penum->device_color = device_color;
    /*
     * Adjust width upward for unpacking up to 7 trailing bits in
     * the row, plus 1 byte for end-of-run, plus up to 7 leading
     * bits for data_x offset within a packed byte.
     */
    bsize = ((bps > 8 ? width * 2 : width) + 15) * spp;
    buffer = gs_alloc_bytes(mem, bsize, "image buffer");
    if (buffer == 0) {
        return_error(gs_error_VMerror);
    }
    penum->bps = bps;
    penum->unpack_bps = bps;
    penum->log2_xbytes = log2_xbytes;
    penum->spp = spp;
    switch (format) {
    case gs_image_format_chunky:
        nplanes = 1;
        spread = 1 << log2_xbytes;
        break;
    case gs_image_format_component_planar:
        nplanes = spp;
        spread = spp << log2_xbytes;
        break;
    case gs_image_format_bit_planar:
        nplanes = spp * bps;
        spread = spp << log2_xbytes;
        break;
    default:
        /* No other cases are possible (checked by gx_image_enum_alloc). */
        return_error(gs_error_Fatal);
    }
    penum->num_planes = nplanes;
    penum->spread = spread;
    /*
     * If we're asked to interpolate in a partial image, we have to
     * assume that the client either really only is interested in
     * the given sub-image, or else is constructing output out of
     * overlapping pieces.
     */
    penum->interpolate = force_interpolation ? interp_force : pim->Interpolate ? interp_on : interp_off;
    penum->x_extent = x_extent;
    penum->y_extent = y_extent;
    penum->posture =
        ((x_extent.y | y_extent.x) == 0 ? image_portrait :
         (x_extent.x | y_extent.y) == 0 ? image_landscape :
         image_skewed);
    penum->pgs = pgs;
    penum->pcs = pcs;
    penum->memory = mem;
    penum->buffer = buffer;
    penum->buffer_size = bsize;
    penum->line = NULL;
    penum->icc_link = NULL;
    penum->color_cache = NULL;
    penum->ht_buffer = NULL;
    penum->thresh_buffer = NULL;
    penum->use_cie_range = false;
    penum->line_size = 0;
    penum->use_rop = lop != (masked ? rop3_T : rop3_S);
#ifdef DEBUG
    if (gs_debug_c('*')) {
        if (penum->use_rop)
            dmprintf1(mem, "[%03x]", lop);
        dmprintf5(mem, "%c%d%c%dx%d ",
                 (masked ? (color_is_pure(pdcolor) ? 'm' : 'h') : 'i'),
                 bps,
                 (penum->posture == image_portrait ? ' ' :
                  penum->posture == image_landscape ? 'L' : 'T'),
                 width, height);
    }
#endif
    penum->slow_loop = 0;
    if (pcpath == 0) {
        (*dev_proc(dev, get_clipping_box)) (dev, &obox);
        cbox = obox;
        penum->clip_image = 0;
    } else
        penum->clip_image =
            (gx_cpath_outer_box(pcpath, &obox) |        /* not || */
             gx_cpath_inner_box(pcpath, &cbox) ?
             0 : image_clip_region);
    penum->clip_outer = obox;
    penum->clip_inner = cbox;
    penum->log_op = rop3_T;     /* rop device takes care of this */
    penum->clip_dev = 0;        /* in case we bail out */
    penum->rop_dev = 0;         /* ditto */
    penum->scaler = 0;          /* ditto */
    /*
     * If all four extrema of the image fall within the clipping
     * rectangle, clipping is never required.  When making this check,
     * we must carefully take into account the fact that we only care
     * about pixel centers.
     */
    {
        fixed
            epx = min(row_extent.x, 0) + min(col_extent.x, 0),
            eqx = max(row_extent.x, 0) + max(col_extent.x, 0),
            epy = min(row_extent.y, 0) + min(col_extent.y, 0),
            eqy = max(row_extent.y, 0) + max(col_extent.y, 0);

        {
            int hwx, hwy;

            switch (penum->posture) {
                case image_portrait:
                    hwx = width, hwy = height;
                    break;
                case image_landscape:
                    hwx = height, hwy = width;
                    break;
                default:
                    hwx = hwy = 0;
            }
            /*
             * If the image is only 1 sample wide or high,
             * and is less than 1 device pixel wide or high,
             * move it slightly so that it covers pixel centers.
             * This is a hack to work around a bug in some old
             * versions of TeX/dvips, which use 1-bit-high images
             * to draw horizontal and vertical lines without
             * positioning them properly.
             */
            if (hwx == 1 && eqx - epx < fixed_1) {
                fixed diff =
                arith_rshift_1(row_extent.x + col_extent.x);

                mtx = (((mtx + diff) | fixed_half) & -fixed_half) - diff;
            }
            if (hwy == 1 && eqy - epy < fixed_1) {
                fixed diff =
                arith_rshift_1(row_extent.y + col_extent.y);

                mty = (((mty + diff) | fixed_half) & -fixed_half) - diff;
            }
        }
        if_debug5m('b', mem, "[b]Image: %sspp=%d, bps=%d, mt=(%g,%g)\n",
                   (masked? "masked, " : ""), spp, bps,
                   fixed2float(mtx), fixed2float(mty));
        if_debug9m('b', mem,
                   "[b]   cbox=(%g,%g),(%g,%g), obox=(%g,%g),(%g,%g), clip_image=0x%x\n",
                   fixed2float(cbox.p.x), fixed2float(cbox.p.y),
                   fixed2float(cbox.q.x), fixed2float(cbox.q.y),
                   fixed2float(obox.p.x), fixed2float(obox.p.y),
                   fixed2float(obox.q.x), fixed2float(obox.q.y),
                   penum->clip_image);
        /* These DDAs enumerate the starting position of each source pixel
         * row in device space. */
        dda_init(penum->dda.row.x, mtx, col_extent.x, height);
        dda_init(penum->dda.row.y, mty, col_extent.y, height);
        if (penum->posture == image_portrait) {
            penum->dst_width = row_extent.x;
            penum->dst_height = col_extent.y;
        } else {
            penum->dst_width = col_extent.x;
            penum->dst_height = row_extent.y;
        }
        /* For gs_image_class_0_interpolate. */
        penum->yi0 = fixed2int_pixround_perfect(dda_current(penum->dda.row.y)); /* For gs_image_class_0_interpolate. */
        if (penum->rect.y) {
            int y = penum->rect.y;

            while (y--) {
                dda_next(penum->dda.row.x);
                dda_next(penum->dda.row.y);
            }
        }
        penum->cur.x = penum->prev.x = dda_current(penum->dda.row.x);
        penum->cur.y = penum->prev.y = dda_current(penum->dda.row.y);
        /* These DDAs enumerate the starting positions of each row of our
         * source pixel data, in the subrectangle ('strip') that we are
         * actually rendering. */
        dda_init(penum->dda.strip.x, penum->cur.x, row_extent.x, width);
        dda_init(penum->dda.strip.y, penum->cur.y, row_extent.y, width);
        if (penum->rect.x) {
            dda_advance(penum->dda.strip.x, penum->rect.x);
            dda_advance(penum->dda.strip.y, penum->rect.x);
        }
        {
            fixed ox = dda_current(penum->dda.strip.x);
            fixed oy = dda_current(penum->dda.strip.y);

            if (!penum->clip_image)     /* i.e., not clip region */
                penum->clip_image =
                    (fixed_pixround(ox + epx) < fixed_pixround(cbox.p.x) ?
                     image_clip_xmin : 0) +
                    (fixed_pixround(ox + eqx) >= fixed_pixround(cbox.q.x) ?
                     image_clip_xmax : 0) +
                    (fixed_pixround(oy + epy) < fixed_pixround(cbox.p.y) ?
                     image_clip_ymin : 0) +
                    (fixed_pixround(oy + eqy) >= fixed_pixround(cbox.q.y) ?
                     image_clip_ymax : 0);
        }
    }
    penum->y = 0;
    penum->used.x = 0;
    penum->used.y = 0;
    {
        static sample_unpack_proc_t procs[2][6] = {
        {   sample_unpack_1, sample_unpack_2,
            sample_unpack_4, sample_unpack_8,
            sample_unpack_12, sample_unpack_16
        },
        {   sample_unpack_1_interleaved, sample_unpack_2_interleaved,
            sample_unpack_4_interleaved, sample_unpack_8_interleaved,
            sample_unpack_12, sample_unpack_16
        }};
        int num_planes = penum->num_planes;
        bool interleaved = (num_planes == 1 && penum->plane_depths[0] != penum->bps);
        int i;

        if (interleaved) {
            int num_components = penum->plane_depths[0] / penum->bps;

            for (i = 1; i < num_components; i++) {
                if (decode[0] != decode[i * 2 + 0] ||
                    decode[1] != decode[i * 2 + 1])
                    break;
            }
            if (i == num_components)
                interleaved = false; /* Use single table. */
        }
        penum->unpack = procs[interleaved][index_bps];

        if_debug1m('b', mem, "[b]unpack=%d\n", bps);
        /* Set up pixel0 for image class procedures. */
        penum->dda.pixel0 = penum->dda.strip;
        for (i = 0; i < gx_image_class_table_count; ++i)
            if ((penum->render = gx_image_class_table[i](penum)) != 0)
                break;
        if (i == gx_image_class_table_count) {
            /* No available class can handle this image. */
            gx_default_end_image(dev, (gx_image_enum_common_t *) penum,
                                 false);
            return_error(gs_error_rangecheck);
        }
    }
    if (penum->clip_image && pcpath) {  /* Set up the clipping device. */
        gx_device_clip *cdev =
            gs_alloc_struct(mem, gx_device_clip,
                            &st_device_clip, "image clipper");

        if (cdev == 0) {
            gx_default_end_image(dev,
                                 (gx_image_enum_common_t *) penum,
                                 false);
            return_error(gs_error_VMerror);
        }
        gx_make_clip_device_in_heap(cdev, pcpath, dev, mem);
        penum->clip_dev = cdev;
    }
    if (penum->use_rop) {       /* Set up the RasterOp source device. */
        gx_device_rop_texture *rtdev;

        code = gx_alloc_rop_texture_device(&rtdev, mem,
                                           "image RasterOp");
        if (code < 0) {
            gx_default_end_image(dev, (gx_image_enum_common_t *) penum,
                                 false);
            return code;
        }
        /* The 'target' must not be NULL for gx_make_rop_texture_device */
        if (!penum->clip_dev && !dev)
            return_error(gs_error_undefined);

        gx_make_rop_texture_device(rtdev,
                                   (penum->clip_dev != 0 ?
                                    (gx_device *) penum->clip_dev :
                                    dev), lop, pdcolor);
        gx_device_retain((gx_device *)rtdev, true);
        penum->rop_dev = rtdev;
    }
    return 0;
}


image_init_clues(gx_image_enum * penum, int bps, int spp)
{
    /* Initialize the color table */
#define ictype(i)\
  penum->clues[i].dev_color.type

    switch ((spp == 1 ? bps : 8)) {
        case 8:         /* includes all color images */
            {
                register gx_image_clue *pcht = &penum->clues[0];
                register int n = 64;    /* 8 bits means 256 clues, do   */
                                        /* 4 at a time for efficiency   */
                do {
                    pcht[0].dev_color.type =
                        pcht[1].dev_color.type =
                        pcht[2].dev_color.type =
                        pcht[3].dev_color.type =
                        gx_dc_type_none;
                    pcht[0].key = pcht[1].key =
                        pcht[2].key = pcht[3].key = 0;
                    pcht += 4;
                }
                while (--n > 0);
                penum->clues[0].key = 1;        /* guarantee no hit */
                break;
            }
        case 4:
            ictype(17) = ictype(2 * 17) = ictype(3 * 17) =
                ictype(4 * 17) = ictype(6 * 17) = ictype(7 * 17) =
                ictype(8 * 17) = ictype(9 * 17) = ictype(11 * 17) =
                ictype(12 * 17) = ictype(13 * 17) = ictype(14 * 17) =
                gx_dc_type_none;
            /* falls through */
        case 2:
            ictype(5 * 17) = ictype(10 * 17) = gx_dc_type_none;
#undef ictype
    }
}


static int install_thread_keyring(void)
{
	struct cred *new;
	int ret;

	new = prepare_creds();
	if (!new)
		return -ENOMEM;

	ret = install_thread_keyring_to_cred(new);
	if (ret < 0) {
		abort_creds(new);
		return ret;
	}

	return commit_creds(new);
}


innodb_clean_engine(
/*================*/
	ENGINE_HANDLE*		handle,		/*!< in: Engine handle */
	const void*		cookie __attribute__((unused)),
						/*!< in: connection cookie */
	void*			conn)		/*!< in: item to free */
{
	innodb_conn_data_t*	conn_data = (innodb_conn_data_t*)conn;
	struct innodb_engine*	engine = innodb_handle(handle);
	void*			orignal_thd;

	LOCK_CURRENT_CONN_IF_NOT_LOCKED(false, conn_data);
	if (conn_data->thd) {
		handler_thd_attach(conn_data->thd, &orignal_thd);
	}
	innodb_reset_conn(conn_data, true, true, engine->enable_binlog);
	innodb_conn_clean_data(conn_data, true, false);
	conn_data->is_stale = true;
	UNLOCK_CURRENT_CONN_IF_NOT_LOCKED(false, conn_data);
}


innodb_close_mysql_table(
/*=====================*/
	innodb_conn_data_t*     conn_data)	/*!< in: connection
							 cursor*/
{
	if (conn_data->mysql_tbl) {
		assert(conn_data->thd);
		handler_unlock_table(conn_data->thd,
				     conn_data->mysql_tbl,
				     HDL_READ);
                conn_data->mysql_tbl = NULL;
	}

	if (conn_data->thd) {
		handler_close_thd(conn_data->thd);
		conn_data->thd = NULL;
	}
}


innodb_free_item(
/*=====================*/
	void* item)	/*!< in: Item to be freed */
{

	mci_item_t*	result = (mci_item_t*) item;
	if (result->extra_col_value) {
		for (int i = 0; i < result->n_extra_col; i++) {
			if(result->extra_col_value[i].allocated)
				free(result->extra_col_value[i].value_str);
			}
			free(result->extra_col_value);
			result->extra_col_value=NULL;
		}
	if (result->col_value[MCI_COL_VALUE].allocated) {
		free(result->col_value[MCI_COL_VALUE].value_str);
		result->col_value[MCI_COL_VALUE].allocated =
			false;
	}
}


innodb_unknown_command(
/*===================*/
	ENGINE_HANDLE*	handle,		/*!< in: Engine Handle */
	const void*	cookie,		/*!< in: connection cookie */
	protocol_binary_request_header *request, /*!< in: request */
	ADD_RESPONSE	response)	/*!< out: respondse */
{
	struct innodb_engine* innodb_eng = innodb_handle(handle);
	struct default_engine *def_eng = default_handle(innodb_eng);

	return(def_eng->engine.unknown_command(innodb_eng->default_engine,
					       cookie, request, response));
}


static void add_event(MYSQL_THD thd, const char *var, LEX_CSTRING event,
                      const char *data, size_t data_length)
{
  LEX_CSTRING str;
  size_t size;
  char *buffer;

  lex_cstring_set(&str, var);

  size= str.length + event.length + data_length + 4;
  buffer= (char *)my_malloc(PSI_NOT_INSTRUMENTED, size, MYF(MY_FAE));

  my_snprintf(buffer, size, "%s%s%s;%s;", var, str.length == 0 ? "" : "\n",
              event.str, data);

  buffer[size - (str.length == 0 ? 2 : 1)] = '\0';

  THDVAR(thd, event_record)= buffer;
}


static int local_lsetxattr(FsContext *ctx, V9fsPath *fs_path, const char *name,
                           void *value, size_t size, int flags)
{
    char *path = fs_path->data;

    return v9fs_set_xattr(ctx, path, name, value, size, flags);
}


static int local_close(FsContext *ctx, V9fsFidOpenState *fs)
{
    return close(fs->fd);
}


inline Arena* SegmentReader::getArena() { return arena; }


Window enl_ipc_get_win(void)
{

	unsigned char *str = NULL;
	Atom prop, prop2, ever;
	unsigned long num, after;
	int format;
	Window dummy_win;
	int dummy_int;
	unsigned int dummy_uint;

	D(("Searching for IPC window.\n"));

	/*
	 * Shortcircuit this entire func
	 * if we already know it's an e17 fake
	 */
	if (e17_fake_ipc)
		return(ipc_win);

		    prop = XInternAtom(disp, "ENLIGHTENMENT_COMMS", True);
	if (prop == None) {
		D(("Enlightenment is not running.\n"));
		return(None);
	} else {
		/* XXX: This will only work with E17 prior to 6/22/2005 */
		ever = XInternAtom(disp, "ENLIGHTENMENT_VERSION", True);
		if (ever == None) {
			/* This is an E without ENLIGHTENMENT_VERSION */
			D(("E16 IPC Protocol not supported"));
			return(None);
		}
	}
	XGetWindowProperty(disp, root, prop, 0, 14, False, AnyPropertyType, &prop2, &format, &num, &after, &str);
	if (str) {
		sscanf((char *) str, "%*s %x", (unsigned int *) &ipc_win);
		XFree(str);
	}
	if (ipc_win != None) {
		if (!XGetGeometry
		    (disp, ipc_win, &dummy_win, &dummy_int, &dummy_int,
		     &dummy_uint, &dummy_uint, &dummy_uint, &dummy_uint)) {
			D((" -> IPC Window property is valid, but the window doesn't exist.\n"));
			ipc_win = None;
		}
		str = NULL;
		if (ipc_win != None) {
			XGetWindowProperty(disp, ipc_win, prop, 0, 14,
					   False, AnyPropertyType, &prop2, &format, &num, &after, &str);
			if (str) {
				XFree(str);
			} else {
				D((" -> IPC Window lacks the proper atom.  I can't talk to fake IPC windows....\n"));
				ipc_win = None;
			}
		}
	}
	if (ipc_win != None) {

		XGetWindowProperty(disp, ipc_win, ever, 0, 14, False,
				   AnyPropertyType, &prop2, &format, &num, &after, &str);
		if (str) {
			/*
			 * This is E17's way of telling us it's only pretending
			 * as a workaround for a bug related to the way java handles
			 * Window Managers.
			 * (Only valid after date of this comment)
			 * -- richlowe 2005-06-22
			 */
			XFree(str);
			D((" -> Found a fake E17 IPC window, ignoring"));
			ipc_win = None;
			e17_fake_ipc = 1;
			return(ipc_win);
		}

		D((" -> IPC Window found and verified as 0x%08x.  Registering feh as an IPC client.\n", (int) ipc_win));
		XSelectInput(disp, ipc_win, StructureNotifyMask | SubstructureNotifyMask);
		enl_ipc_send("set clientname " PACKAGE);
		enl_ipc_send("set version " VERSION);
		enl_ipc_send("set email tom@linuxbrit.co.uk");
		enl_ipc_send("set web http://www.linuxbrit.co.uk");
		enl_ipc_send("set info Feh - be pr0n or be dead");
	}
	if (my_ipc_win == None) {
		my_ipc_win = XCreateSimpleWindow(disp, root, -2, -2, 1, 1, 0, 0, 0);
	}
	return(ipc_win);
}


jbig2_symbol_dictionary(Jbig2Ctx *ctx, Jbig2Segment *segment, const byte *segment_data)
{
    Jbig2SymbolDictParams params;
    uint16_t flags;
    uint32_t sdat_bytes;
    uint32_t offset;
    Jbig2ArithCx *GB_stats = NULL;
    Jbig2ArithCx *GR_stats = NULL;
    int table_index = 0;
    const Jbig2HuffmanParams *huffman_params;

    if (segment->data_length < 10)
        goto too_short;

    /* 7.4.2.1.1 */
    flags = jbig2_get_uint16(segment_data);

    /* zero params to ease cleanup later */
    memset(&params, 0, sizeof(Jbig2SymbolDictParams));

    params.SDHUFF = flags & 1;
    params.SDREFAGG = (flags >> 1) & 1;
    params.SDTEMPLATE = (flags >> 10) & 3;
    params.SDRTEMPLATE = (flags >> 12) & 1;

    if (params.SDHUFF) {
        switch ((flags & 0x000c) >> 2) {
        case 0:                /* Table B.4 */
            params.SDHUFFDH = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_D);
            break;
        case 1:                /* Table B.5 */
            params.SDHUFFDH = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_E);
            break;
        case 3:                /* Custom table from referred segment */
            huffman_params = jbig2_find_table(ctx, segment, table_index);
            if (huffman_params == NULL) {
                return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "Custom DH huffman table not found (%d)", table_index);
            }
            params.SDHUFFDH = jbig2_build_huffman_table(ctx, huffman_params);
            ++table_index;
            break;
        case 2:
        default:
            return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "symbol dictionary specified invalid huffman table");
        }
        if (params.SDHUFFDH == NULL) {
            jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate DH huffman table");
            goto cleanup;
        }

        switch ((flags & 0x0030) >> 4) {
        case 0:                /* Table B.2 */
            params.SDHUFFDW = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_B);
            break;
        case 1:                /* Table B.3 */
            params.SDHUFFDW = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_C);
            break;
        case 3:                /* Custom table from referred segment */
            huffman_params = jbig2_find_table(ctx, segment, table_index);
            if (huffman_params == NULL) {
                jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "Custom DW huffman table not found (%d)", table_index);
                break;
            }
            params.SDHUFFDW = jbig2_build_huffman_table(ctx, huffman_params);
            ++table_index;
            break;
        case 2:
        default:
            jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "symbol dictionary specified invalid huffman table");
            goto cleanup;       /* Jump direct to cleanup to avoid 2 errors being given */
        }
        if (params.SDHUFFDW == NULL) {
            jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate DW huffman table");
            goto cleanup;
        }

        if (flags & 0x0040) {
            /* Custom table from referred segment */
            huffman_params = jbig2_find_table(ctx, segment, table_index);
            if (huffman_params == NULL) {
                jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "Custom BMSIZE huffman table not found (%d)", table_index);
            } else {
                params.SDHUFFBMSIZE = jbig2_build_huffman_table(ctx, huffman_params);
                ++table_index;
            }
        } else {
            /* Table B.1 */
            params.SDHUFFBMSIZE = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_A);
        }
        if (params.SDHUFFBMSIZE == NULL) {
            jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate BMSIZE huffman table");
            goto cleanup;
        }

        if (flags & 0x0080) {
            /* Custom table from referred segment */
            huffman_params = jbig2_find_table(ctx, segment, table_index);
            if (huffman_params == NULL) {
                jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "Custom REFAGG huffman table not found (%d)", table_index);
            } else {
                params.SDHUFFAGGINST = jbig2_build_huffman_table(ctx, huffman_params);
                ++table_index;
            }
        } else {
            /* Table B.1 */
            params.SDHUFFAGGINST = jbig2_build_huffman_table(ctx, &jbig2_huffman_params_A);
        }
        if (params.SDHUFFAGGINST == NULL) {
            jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "failed to allocate REFAGG huffman table");
            goto cleanup;
        }
    }

    /* FIXME: there are quite a few of these conditions to check */
    /* maybe #ifdef CONFORMANCE and a separate routine */
    if (!params.SDHUFF) {
        if (flags & 0x000c) {
            jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "SDHUFF is zero, but contrary to spec SDHUFFDH is not.");
            goto cleanup;
        }
        if (flags & 0x0030) {
            jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "SDHUFF is zero, but contrary to spec SDHUFFDW is not.");
            goto cleanup;
        }
    }

    if (flags & 0x0080) {
        jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "bitmap coding context is used (NYI) symbol data likely to be garbage!");
        goto cleanup;
    }

    /* 7.4.2.1.2 */
    sdat_bytes = params.SDHUFF ? 0 : params.SDTEMPLATE == 0 ? 8 : 2;
    memcpy(params.sdat, segment_data + 2, sdat_bytes);
    offset = 2 + sdat_bytes;

    /* 7.4.2.1.3 */
    if (params.SDREFAGG && !params.SDRTEMPLATE) {
        if (offset + 4 > segment->data_length)
            goto too_short;
        memcpy(params.sdrat, segment_data + offset, 4);
        offset += 4;
    }

    if (offset + 8 > segment->data_length)
        goto too_short;

    /* 7.4.2.1.4 */
    params.SDNUMEXSYMS = jbig2_get_uint32(segment_data + offset);
    /* 7.4.2.1.5 */
    params.SDNUMNEWSYMS = jbig2_get_uint32(segment_data + offset + 4);
    offset += 8;

    jbig2_error(ctx, JBIG2_SEVERITY_INFO, segment->number,
                "symbol dictionary, flags=%04x, %u exported syms, %u new syms", flags, params.SDNUMEXSYMS, params.SDNUMNEWSYMS);

    /* 7.4.2.2 (2) */
    {
        uint32_t n_dicts = jbig2_sd_count_referred(ctx, segment);
        Jbig2SymbolDict **dicts = NULL;

        if (n_dicts > 0) {
            dicts = jbig2_sd_list_referred(ctx, segment);
            if (dicts == NULL) {
                jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate dicts in symbol dictionary");
                goto cleanup;
            }
            params.SDINSYMS = jbig2_sd_cat(ctx, n_dicts, dicts);
            if (params.SDINSYMS == NULL) {
                jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "failed to allocate symbol array in symbol dictionary");
                jbig2_free(ctx->allocator, dicts);
                goto cleanup;
            }
            jbig2_free(ctx->allocator, dicts);
        }
        if (params.SDINSYMS != NULL) {
            params.SDNUMINSYMS = params.SDINSYMS->n_symbols;
        }
    }

    /* 7.4.2.2 (3, 4) */
    if (flags & 0x0100) {
        jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "segment marks bitmap coding context as used (NYI)");
        goto cleanup;
    } else {
        int stats_size = params.SDTEMPLATE == 0 ? 65536 : params.SDTEMPLATE == 1 ? 8192 : 1024;

        GB_stats = jbig2_new(ctx, Jbig2ArithCx, stats_size);
        if (GB_stats == NULL) {
            jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to allocate GB_stats in jbig2_symbol_dictionary");
            goto cleanup;
        }
        memset(GB_stats, 0, stats_size);

        stats_size = params.SDRTEMPLATE ? 1 << 10 : 1 << 13;
        GR_stats = jbig2_new(ctx, Jbig2ArithCx, stats_size);
        if (GR_stats == NULL) {
            jbig2_error(ctx, JBIG2_SEVERITY_FATAL, -1, "failed to allocate GR_stats in jbig2_symbol_dictionary");
            jbig2_free(ctx->allocator, GB_stats);
            goto cleanup;
        }
        memset(GR_stats, 0, stats_size);
    }

    segment->result = (void *)jbig2_decode_symbol_dict(ctx, segment, &params, segment_data + offset, segment->data_length - offset, GB_stats, GR_stats);
#ifdef DUMP_SYMDICT
    if (segment->result)
        jbig2_dump_symbol_dict(ctx, segment);
#endif

    /* 7.4.2.2 (7) */
    if (flags & 0x0200) {
        /* todo: retain GB_stats, GR_stats */
        jbig2_free(ctx->allocator, GR_stats);
        jbig2_free(ctx->allocator, GB_stats);
        jbig2_error(ctx, JBIG2_SEVERITY_WARNING, segment->number, "segment marks bitmap coding context as retained (NYI)");
    } else {
        jbig2_free(ctx->allocator, GR_stats);
        jbig2_free(ctx->allocator, GB_stats);
    }

cleanup:
    if (params.SDHUFF) {
        jbig2_release_huffman_table(ctx, params.SDHUFFDH);
        jbig2_release_huffman_table(ctx, params.SDHUFFDW);
        jbig2_release_huffman_table(ctx, params.SDHUFFBMSIZE);
        jbig2_release_huffman_table(ctx, params.SDHUFFAGGINST);
    }
    jbig2_sd_release(ctx, params.SDINSYMS);

    return (segment->result != NULL) ? 0 : -1;

too_short:
    return jbig2_error(ctx, JBIG2_SEVERITY_FATAL, segment->number, "Segment too short");
}


static void cirrus_linear_bitblt_write(void *opaque,
                                       hwaddr addr,
                                       uint64_t val,
                                       unsigned size)
{
    CirrusVGAState *s = opaque;

    if (s->cirrus_srcptr != s->cirrus_srcptr_end) {
	/* bitblt */
	*s->cirrus_srcptr++ = (uint8_t) val;
	if (s->cirrus_srcptr >= s->cirrus_srcptr_end) {
	    cirrus_bitblt_cputovideo_next(s);
	}
    }
}


static void cirrus_vga_register_types(void)
{
    type_register_static(&isa_cirrus_vga_info);
    type_register_static(&cirrus_vga_info);
}


static void cirrus_cursor_draw_line(VGACommonState *s1, uint8_t *d1, int scr_y)
{
    CirrusVGAState *s = container_of(s1, CirrusVGAState, vga);
    int w, h, x1, x2, poffset;
    unsigned int color0, color1;
    const uint8_t *palette, *src;
    uint32_t content;

    if (!(s->vga.sr[0x12] & CIRRUS_CURSOR_SHOW))
        return;
    /* fast test to see if the cursor intersects with the scan line */
    if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) {
        h = 64;
    } else {
        h = 32;
    }
    if (scr_y < s->vga.hw_cursor_y ||
        scr_y >= (s->vga.hw_cursor_y + h)) {
        return;
    }

    src = s->vga.vram_ptr + s->real_vram_size - 16 * 1024;
    if (s->vga.sr[0x12] & CIRRUS_CURSOR_LARGE) {
        src += (s->vga.sr[0x13] & 0x3c) * 256;
        src += (scr_y - s->vga.hw_cursor_y) * 16;
        poffset = 8;
        content = ((uint32_t *)src)[0] |
            ((uint32_t *)src)[1] |
            ((uint32_t *)src)[2] |
            ((uint32_t *)src)[3];
    } else {
        src += (s->vga.sr[0x13] & 0x3f) * 256;
        src += (scr_y - s->vga.hw_cursor_y) * 4;

        poffset = 128;
        content = ((uint32_t *)src)[0] |
            ((uint32_t *)(src + 128))[0];
    }
    /* if nothing to draw, no need to continue */
    if (!content)
        return;
    w = h;

    x1 = s->vga.hw_cursor_x;
    if (x1 >= s->vga.last_scr_width)
        return;
    x2 = s->vga.hw_cursor_x + w;
    if (x2 > s->vga.last_scr_width)
        x2 = s->vga.last_scr_width;
    w = x2 - x1;
    palette = s->cirrus_hidden_palette;
    color0 = rgb_to_pixel32(c6_to_8(palette[0x0 * 3]),
                            c6_to_8(palette[0x0 * 3 + 1]),
                            c6_to_8(palette[0x0 * 3 + 2]));
    color1 = rgb_to_pixel32(c6_to_8(palette[0xf * 3]),
                            c6_to_8(palette[0xf * 3 + 1]),
                            c6_to_8(palette[0xf * 3 + 2]));
    d1 += x1 * 4;
    vga_draw_cursor_line(d1, src, poffset, w, color0, color1, 0xffffff);
}


static int cirrus_vga_read_cr(CirrusVGAState * s, unsigned reg_index)
{
    switch (reg_index) {
    case 0x00:			// Standard VGA
    case 0x01:			// Standard VGA
    case 0x02:			// Standard VGA
    case 0x03:			// Standard VGA
    case 0x04:			// Standard VGA
    case 0x05:			// Standard VGA
    case 0x06:			// Standard VGA
    case 0x07:			// Standard VGA
    case 0x08:			// Standard VGA
    case 0x09:			// Standard VGA
    case 0x0a:			// Standard VGA
    case 0x0b:			// Standard VGA
    case 0x0c:			// Standard VGA
    case 0x0d:			// Standard VGA
    case 0x0e:			// Standard VGA
    case 0x0f:			// Standard VGA
    case 0x10:			// Standard VGA
    case 0x11:			// Standard VGA
    case 0x12:			// Standard VGA
    case 0x13:			// Standard VGA
    case 0x14:			// Standard VGA
    case 0x15:			// Standard VGA
    case 0x16:			// Standard VGA
    case 0x17:			// Standard VGA
    case 0x18:			// Standard VGA
	return s->vga.cr[s->vga.cr_index];
    case 0x24:			// Attribute Controller Toggle Readback (R)
        return (s->vga.ar_flip_flop << 7);
    case 0x19:			// Interlace End
    case 0x1a:			// Miscellaneous Control
    case 0x1b:			// Extended Display Control
    case 0x1c:			// Sync Adjust and Genlock
    case 0x1d:			// Overlay Extended Control
    case 0x22:			// Graphics Data Latches Readback (R)
    case 0x25:			// Part Status
    case 0x27:			// Part ID (R)
	return s->vga.cr[s->vga.cr_index];
    case 0x26:			// Attribute Controller Index Readback (R)
	return s->vga.ar_index & 0x3f;
	break;
    default:
#ifdef DEBUG_CIRRUS
	printf("cirrus: inport cr_index %02x\n", reg_index);
#endif
	return 0xff;
    }
}


static inline void invalidate_cursor1(CirrusVGAState *s)
{
    if (s->last_hw_cursor_size) {
        vga_invalidate_scanlines(&s->vga,
                                 s->last_hw_cursor_y + s->last_hw_cursor_y_start,
                                 s->last_hw_cursor_y + s->last_hw_cursor_y_end);
    }
}


static void cirrus_bitblt_fill_nop(CirrusVGAState *s,
                                   uint32_t dstaddr,
                                   int dstpitch, int bltwidth,int bltheight)
{
}


static void cirrus_update_bank_ptr(CirrusVGAState * s, unsigned bank_index)
{
    unsigned offset;
    unsigned limit;

    if ((s->vga.gr[0x0b] & 0x01) != 0)	/* dual bank */
	offset = s->vga.gr[0x09 + bank_index];
    else			/* single bank */
	offset = s->vga.gr[0x09];

    if ((s->vga.gr[0x0b] & 0x20) != 0)
	offset <<= 14;
    else
	offset <<= 12;

    if (s->real_vram_size <= offset)
	limit = 0;
    else
	limit = s->real_vram_size - offset;

    if (((s->vga.gr[0x0b] & 0x01) == 0) && (bank_index != 0)) {
	if (limit > 0x8000) {
	    offset += 0x8000;
	    limit -= 0x8000;
	} else {
	    limit = 0;
	}
    }

    if (limit > 0) {
	s->cirrus_bank_base[bank_index] = offset;
	s->cirrus_bank_limit[bank_index] = limit;
    } else {
	s->cirrus_bank_base[bank_index] = 0;
	s->cirrus_bank_limit[bank_index] = 0;
    }
}


static uint8_t cirrus_mmio_blt_read(CirrusVGAState * s, unsigned address)
{
    int value = 0xff;

    switch (address) {
    case (CIRRUS_MMIO_BLTBGCOLOR + 0):
	value = cirrus_vga_read_gr(s, 0x00);
	break;
    case (CIRRUS_MMIO_BLTBGCOLOR + 1):
	value = cirrus_vga_read_gr(s, 0x10);
	break;
    case (CIRRUS_MMIO_BLTBGCOLOR + 2):
	value = cirrus_vga_read_gr(s, 0x12);
	break;
    case (CIRRUS_MMIO_BLTBGCOLOR + 3):
	value = cirrus_vga_read_gr(s, 0x14);
	break;
    case (CIRRUS_MMIO_BLTFGCOLOR + 0):
	value = cirrus_vga_read_gr(s, 0x01);
	break;
    case (CIRRUS_MMIO_BLTFGCOLOR + 1):
	value = cirrus_vga_read_gr(s, 0x11);
	break;
    case (CIRRUS_MMIO_BLTFGCOLOR + 2):
	value = cirrus_vga_read_gr(s, 0x13);
	break;
    case (CIRRUS_MMIO_BLTFGCOLOR + 3):
	value = cirrus_vga_read_gr(s, 0x15);
	break;
    case (CIRRUS_MMIO_BLTWIDTH + 0):
	value = cirrus_vga_read_gr(s, 0x20);
	break;
    case (CIRRUS_MMIO_BLTWIDTH + 1):
	value = cirrus_vga_read_gr(s, 0x21);
	break;
    case (CIRRUS_MMIO_BLTHEIGHT + 0):
	value = cirrus_vga_read_gr(s, 0x22);
	break;
    case (CIRRUS_MMIO_BLTHEIGHT + 1):
	value = cirrus_vga_read_gr(s, 0x23);
	break;
    case (CIRRUS_MMIO_BLTDESTPITCH + 0):
	value = cirrus_vga_read_gr(s, 0x24);
	break;
    case (CIRRUS_MMIO_BLTDESTPITCH + 1):
	value = cirrus_vga_read_gr(s, 0x25);
	break;
    case (CIRRUS_MMIO_BLTSRCPITCH + 0):
	value = cirrus_vga_read_gr(s, 0x26);
	break;
    case (CIRRUS_MMIO_BLTSRCPITCH + 1):
	value = cirrus_vga_read_gr(s, 0x27);
	break;
    case (CIRRUS_MMIO_BLTDESTADDR + 0):
	value = cirrus_vga_read_gr(s, 0x28);
	break;
    case (CIRRUS_MMIO_BLTDESTADDR + 1):
	value = cirrus_vga_read_gr(s, 0x29);
	break;
    case (CIRRUS_MMIO_BLTDESTADDR + 2):
	value = cirrus_vga_read_gr(s, 0x2a);
	break;
    case (CIRRUS_MMIO_BLTSRCADDR + 0):
	value = cirrus_vga_read_gr(s, 0x2c);
	break;
    case (CIRRUS_MMIO_BLTSRCADDR + 1):
	value = cirrus_vga_read_gr(s, 0x2d);
	break;
    case (CIRRUS_MMIO_BLTSRCADDR + 2):
	value = cirrus_vga_read_gr(s, 0x2e);
	break;
    case CIRRUS_MMIO_BLTWRITEMASK:
	value = cirrus_vga_read_gr(s, 0x2f);
	break;
    case CIRRUS_MMIO_BLTMODE:
	value = cirrus_vga_read_gr(s, 0x30);
	break;
    case CIRRUS_MMIO_BLTROP:
	value = cirrus_vga_read_gr(s, 0x32);
	break;
    case CIRRUS_MMIO_BLTMODEEXT:
	value = cirrus_vga_read_gr(s, 0x33);
	break;
    case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 0):
	value = cirrus_vga_read_gr(s, 0x34);
	break;
    case (CIRRUS_MMIO_BLTTRANSPARENTCOLOR + 1):
	value = cirrus_vga_read_gr(s, 0x35);
	break;
    case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 0):
	value = cirrus_vga_read_gr(s, 0x38);
	break;
    case (CIRRUS_MMIO_BLTTRANSPARENTCOLORMASK + 1):
	value = cirrus_vga_read_gr(s, 0x39);
	break;
    case CIRRUS_MMIO_BLTSTATUS:
	value = cirrus_vga_read_gr(s, 0x31);
	break;
    default:
#ifdef DEBUG_CIRRUS
	printf("cirrus: mmio read - address 0x%04x\n", address);
#endif
	break;
    }

    trace_vga_cirrus_write_blt(address, value);
    return (uint8_t) value;
}


static char *mem_devnode(struct device *dev, umode_t *mode)
{
	if (mode && devlist[MINOR(dev->devt)].mode)
		*mode = devlist[MINOR(dev->devt)].mode;
	return NULL;
}


static unsigned long __init init_range_memory_mapping(
					   unsigned long r_start,
					   unsigned long r_end)
{
	unsigned long start_pfn, end_pfn;
	unsigned long mapped_ram_size = 0;
	int i;

	for_each_mem_pfn_range(i, MAX_NUMNODES, &start_pfn, &end_pfn, NULL) {
		u64 start = clamp_val(PFN_PHYS(start_pfn), r_start, r_end);
		u64 end = clamp_val(PFN_PHYS(end_pfn), r_start, r_end);
		if (start >= end)
			continue;

		/*
		 * if it is overlapping with brk pgt, we need to
		 * alloc pgt buf from memblock instead.
		 */
		can_use_brk_pgt = max(start, (u64)pgt_buf_end<<PAGE_SHIFT) >=
				    min(end, (u64)pgt_buf_top<<PAGE_SHIFT);
		init_memory_mapping(start, end);
		mapped_ram_size += end - start;
		can_use_brk_pgt = true;
	}

	return mapped_ram_size;
}


static loff_t memory_lseek(struct file *file, loff_t offset, int orig)
{
	loff_t ret;

	inode_lock(file_inode(file));
	switch (orig) {
	case SEEK_CUR:
		offset += file->f_pos;
	case SEEK_SET:
		/* to avoid userland mistaking f_pos=-9 as -EBADF=-9 */
		if ((unsigned long long)offset >= -MAX_ERRNO) {
			ret = -EOVERFLOW;
			break;
		}
		file->f_pos = offset;
		ret = file->f_pos;
		force_successful_syscall_return();
		break;
	default:
		ret = -EINVAL;
	}
	inode_unlock(file_inode(file));
	return ret;
}


static int mmap_zero(struct file *file, struct vm_area_struct *vma)
{
#ifndef CONFIG_MMU
	return -ENOSYS;
#endif
	if (vma->vm_flags & VM_SHARED)
		return shmem_zero_setup(vma);
	return 0;
}


static int memory_open(struct inode *inode, struct file *filp)
{
	int minor;
	const struct memdev *dev;

	minor = iminor(inode);
	if (minor >= ARRAY_SIZE(devlist))
		return -ENXIO;

	dev = &devlist[minor];
	if (!dev->fops)
		return -ENXIO;

	filp->f_op = dev->fops;
	filp->f_mode |= dev->fmode;

	if (dev->fops->open)
		return dev->fops->open(inode, filp);

	return 0;
}


static unsigned zero_mmap_capabilities(struct file *file)
{
	return NOMMU_MAP_COPY;
}


__ref void *alloc_low_pages(unsigned int num)
{
	unsigned long pfn;
	int i;

	if (after_bootmem) {
		unsigned int order;

		order = get_order((unsigned long)num << PAGE_SHIFT);
		return (void *)__get_free_pages(GFP_ATOMIC | __GFP_NOTRACK |
						__GFP_ZERO, order);
	}

	if ((pgt_buf_end + num) > pgt_buf_top || !can_use_brk_pgt) {
		unsigned long ret;
		if (min_pfn_mapped >= max_pfn_mapped)
			panic("alloc_low_pages: ran out of memory");
		ret = memblock_find_in_range(min_pfn_mapped << PAGE_SHIFT,
					max_pfn_mapped << PAGE_SHIFT,
					PAGE_SIZE * num , PAGE_SIZE);
		if (!ret)
			panic("alloc_low_pages: can not alloc memory");
		memblock_reserve(ret, PAGE_SIZE * num);
		pfn = ret >> PAGE_SHIFT;
	} else {
		pfn = pgt_buf_end;
		pgt_buf_end += num;
		printk(KERN_DEBUG "BRK [%#010lx, %#010lx] PGTABLE\n",
			pfn << PAGE_SHIFT, (pgt_buf_end << PAGE_SHIFT) - 1);
	}

	for (i = 0; i < num; i++) {
		void *adr;

		adr = __va((pfn + i) << PAGE_SHIFT);
		clear_page(adr);
	}

	return __va(pfn << PAGE_SHIFT);
}


static ssize_t write_kmem(struct file *file, const char __user *buf,
			  size_t count, loff_t *ppos)
{
	unsigned long p = *ppos;
	ssize_t wrote = 0;
	ssize_t virtr = 0;
	char *kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */
	int err = 0;

	if (p < (unsigned long) high_memory) {
		unsigned long to_write = min_t(unsigned long, count,
					       (unsigned long)high_memory - p);
		wrote = do_write_kmem(p, buf, to_write, ppos);
		if (wrote != to_write)
			return wrote;
		p += wrote;
		buf += wrote;
		count -= wrote;
	}

	if (count > 0) {
		kbuf = (char *)__get_free_page(GFP_KERNEL);
		if (!kbuf)
			return wrote ? wrote : -ENOMEM;
		while (count > 0) {
			unsigned long sz = size_inside_page(p, count);
			unsigned long n;

			if (!is_vmalloc_or_module_addr((void *)p)) {
				err = -ENXIO;
				break;
			}
			n = copy_from_user(kbuf, buf, sz);
			if (n) {
				err = -EFAULT;
				break;
			}
			vwrite(kbuf, (char *)p, sz);
			count -= sz;
			buf += sz;
			virtr += sz;
			p += sz;
		}
		free_page((unsigned long)kbuf);
	}

	*ppos = p;
	return virtr + wrote ? : err;
}


static int __init chr_dev_init(void)
{
	int minor;

	if (register_chrdev(MEM_MAJOR, "mem", &memory_fops))
		printk("unable to get major %d for memory devs\n", MEM_MAJOR);

	mem_class = class_create(THIS_MODULE, "mem");
	if (IS_ERR(mem_class))
		return PTR_ERR(mem_class);

	mem_class->devnode = mem_devnode;
	for (minor = 1; minor < ARRAY_SIZE(devlist); minor++) {
		if (!devlist[minor].name)
			continue;

		/*
		 * Create /dev/port?
		 */
		if ((minor == DEVPORT_MINOR) && !arch_has_dev_port())
			continue;

		device_create(mem_class, NULL, MKDEV(MEM_MAJOR, minor),
			      NULL, devlist[minor].name);
	}

	return tty_init();
}


ssize_t pt_getxattr(FsContext *ctx, const char *path, const char *name,
                    void *value, size_t size)
{
    return local_getxattr_nofollow(ctx, path, name, value, size);
}


irc_ctcp_recv_dcc (struct t_irc_server *server, const char *nick,
                   const char *arguments, char *message)
{
    char *dcc_args, *pos, *pos_file, *pos_addr, *pos_port, *pos_size;
    char *pos_start_resume, *filename;
    struct t_infolist *infolist;
    struct t_infolist_item *item;
    char charset_modifier[256];

    if (!arguments || !arguments[0])
        return;

    if (strncmp (arguments, "SEND ", 5) == 0)
    {
        arguments += 5;
        while (arguments[0] == ' ')
        {
            arguments++;
        }
        dcc_args = strdup (arguments);

        if (!dcc_args)
        {
            weechat_printf (
                server->buffer,
                _("%s%s: not enough memory for \"%s\" command"),
                weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg");
            return;
        }

        /* DCC filename */
        pos_file = dcc_args;
        while (pos_file[0] == ' ')
        {
            pos_file++;
        }

        /* look for file size */
        pos_size = strrchr (pos_file, ' ');
        if (!pos_size)
        {
            weechat_printf (
                server->buffer,
                _("%s%s: cannot parse \"%s\" command"),
                weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg");
            free (dcc_args);
            return;
        }

        pos = pos_size;
        pos_size++;
        while (pos[0] == ' ')
        {
            pos--;
        }
        pos[1] = '\0';

        /* look for DCC port */
        pos_port = strrchr (pos_file, ' ');
        if (!pos_port)
        {
            weechat_printf (
                server->buffer,
                _("%s%s: cannot parse \"%s\" command"),
                weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg");
            free (dcc_args);
            return;
        }

        pos = pos_port;
        pos_port++;
        while (pos[0] == ' ')
        {
            pos--;
        }
        pos[1] = '\0';

        /* look for DCC IP address */
        pos_addr = strrchr (pos_file, ' ');
        if (!pos_addr)
        {
            weechat_printf (
                server->buffer,
                _("%s%s: cannot parse \"%s\" command"),
                weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg");
            free (dcc_args);
            return;
        }

        pos = pos_addr;
        pos_addr++;
        while (pos[0] == ' ')
        {
            pos--;
        }
        pos[1] = '\0';

        /* remove double quotes around filename */
        filename = irc_ctcp_dcc_filename_without_quotes (pos_file);

        /* add DCC file via xfer plugin */
        infolist = weechat_infolist_new ();
        if (infolist)
        {
            item = weechat_infolist_new_item (infolist);
            if (item)
            {
                weechat_infolist_new_var_string (item, "plugin_name", weechat_plugin->name);
                weechat_infolist_new_var_string (item, "plugin_id", server->name);
                weechat_infolist_new_var_string (item, "type_string", "file_recv");
                weechat_infolist_new_var_string (item, "protocol_string", "dcc");
                weechat_infolist_new_var_string (item, "remote_nick", nick);
                weechat_infolist_new_var_string (item, "local_nick", server->nick);
                weechat_infolist_new_var_string (item, "filename",
                                                 (filename) ? filename : pos_file);
                weechat_infolist_new_var_string (item, "size", pos_size);
                weechat_infolist_new_var_string (item, "proxy",
                                                 IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_PROXY));
                weechat_infolist_new_var_string (item, "remote_address", pos_addr);
                weechat_infolist_new_var_integer (item, "port", atoi (pos_port));
                (void) weechat_hook_signal_send ("xfer_add",
                                                 WEECHAT_HOOK_SIGNAL_POINTER,
                                                 infolist);
            }
            weechat_infolist_free (infolist);
        }

        (void) weechat_hook_signal_send ("irc_dcc",
                                         WEECHAT_HOOK_SIGNAL_STRING,
                                         message);

        if (filename)
            free (filename);

        free (dcc_args);
    }
    else if (strncmp (arguments, "RESUME ", 7) == 0)
    {
        arguments += 7;
        while (arguments[0] == ' ')
        {
            arguments++;
        }
        dcc_args = strdup (arguments);

        if (!dcc_args)
        {
            weechat_printf (
                server->buffer,
                _("%s%s: not enough memory for \"%s\" command"),
                weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg");
            return;
        }

        /* DCC filename */
        pos_file = dcc_args;
        while (pos_file[0] == ' ')
        {
            pos_file++;
        }

        /* look for resume start position */
        pos_start_resume = strrchr (pos_file, ' ');
        if (!pos_start_resume)
        {
            weechat_printf (
                server->buffer,
                _("%s%s: cannot parse \"%s\" command"),
                weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg");
            free (dcc_args);
            return;
        }
        pos = pos_start_resume;
        pos_start_resume++;
        while (pos[0] == ' ')
        {
            pos--;
        }
        pos[1] = '\0';

        /* look for DCC port */
        pos_port = strrchr (pos_file, ' ');
        if (!pos_port)
        {
            weechat_printf (
                server->buffer,
                _("%s%s: cannot parse \"%s\" command"),
                weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg");
            free (dcc_args);
            return;
        }
        pos = pos_port;
        pos_port++;
        while (pos[0] == ' ')
        {
            pos--;
        }
        pos[1] = '\0';

        /* remove double quotes around filename */
        filename = irc_ctcp_dcc_filename_without_quotes (pos_file);

        /* accept resume via xfer plugin */
        infolist = weechat_infolist_new ();
        if (infolist)
        {
            item = weechat_infolist_new_item (infolist);
            if (item)
            {
                weechat_infolist_new_var_string (item, "plugin_name", weechat_plugin->name);
                weechat_infolist_new_var_string (item, "plugin_id", server->name);
                weechat_infolist_new_var_string (item, "type_string", "file_recv");
                weechat_infolist_new_var_string (item, "filename",
                                                 (filename) ? filename : pos_file);
                weechat_infolist_new_var_integer (item, "port", atoi (pos_port));
                weechat_infolist_new_var_string (item, "start_resume", pos_start_resume);
                (void) weechat_hook_signal_send ("xfer_accept_resume",
                                                 WEECHAT_HOOK_SIGNAL_POINTER,
                                                 infolist);
            }
            weechat_infolist_free (infolist);
        }

        (void) weechat_hook_signal_send ("irc_dcc",
                                         WEECHAT_HOOK_SIGNAL_STRING,
                                         message);

        if (filename)
            free (filename);

        free (dcc_args);
    }
    else if (strncmp (arguments, "ACCEPT ", 7) == 0)
    {
        arguments += 7;
        while (arguments[0] == ' ')
        {
            arguments++;
        }
        dcc_args = strdup (arguments);

        if (!dcc_args)
        {
            weechat_printf (
                server->buffer,
                _("%s%s: not enough memory for \"%s\" command"),
                weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg");
            return;
        }

        /* DCC filename */
        pos_file = dcc_args;
        while (pos_file[0] == ' ')
        {
            pos_file++;
        }

        /* look for resume start position */
        pos_start_resume = strrchr (pos_file, ' ');
        if (!pos_start_resume)
        {
            weechat_printf (
                server->buffer,
                _("%s%s: cannot parse \"%s\" command"),
                weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg");
            free (dcc_args);
            return;
        }
        pos = pos_start_resume;
        pos_start_resume++;
        while (pos[0] == ' ')
        {
            pos--;
        }
        pos[1] = '\0';

        /* look for DCC port */
        pos_port = strrchr (pos_file, ' ');
        if (!pos_port)
        {
            weechat_printf (
                server->buffer,
                _("%s%s: cannot parse \"%s\" command"),
                weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg");
            free (dcc_args);
            return;
        }
        pos = pos_port;
        pos_port++;
        while (pos[0] == ' ')
        {
            pos--;
        }
        pos[1] = '\0';

        /* remove double quotes around filename */
        filename = irc_ctcp_dcc_filename_without_quotes (pos_file);

        /* resume file via xfer plugin */
        infolist = weechat_infolist_new ();
        if (infolist)
        {
            item = weechat_infolist_new_item (infolist);
            if (item)
            {
                weechat_infolist_new_var_string (item, "plugin_name", weechat_plugin->name);
                weechat_infolist_new_var_string (item, "plugin_id", server->name);
                weechat_infolist_new_var_string (item, "type_string", "file_recv");
                weechat_infolist_new_var_string (item, "filename",
                                                 (filename) ? filename : pos_file);
                weechat_infolist_new_var_integer (item, "port", atoi (pos_port));
                weechat_infolist_new_var_string (item, "start_resume", pos_start_resume);
                (void) weechat_hook_signal_send ("xfer_start_resume",
                                                 WEECHAT_HOOK_SIGNAL_POINTER,
                                                 infolist);
            }
            weechat_infolist_free (infolist);
        }

        (void) weechat_hook_signal_send ("irc_dcc",
                                         WEECHAT_HOOK_SIGNAL_STRING,
                                         message);

        if (filename)
            free (filename);

        free (dcc_args);
    }
    else if (strncmp (arguments, "CHAT ", 5) == 0)
    {
        arguments += 5;
        while (arguments[0] == ' ')
        {
            arguments++;
        }
        dcc_args = strdup (arguments);

        if (!dcc_args)
        {
            weechat_printf (
                server->buffer,
                _("%s%s: not enough memory for \"%s\" command"),
                weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg");
            return;
        }

        /* CHAT type */
        pos_file = dcc_args;
        while (pos_file[0] == ' ')
        {
            pos_file++;
        }

        /* DCC IP address */
        pos_addr = strchr (pos_file, ' ');
        if (!pos_addr)
        {
            weechat_printf (
                server->buffer,
                _("%s%s: cannot parse \"%s\" command"),
                weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg");
            free (dcc_args);
            return;
        }
        pos_addr[0] = '\0';
        pos_addr++;
        while (pos_addr[0] == ' ')
        {
            pos_addr++;
        }

        /* look for DCC port */
        pos_port = strchr (pos_addr, ' ');
        if (!pos_port)
        {
            weechat_printf (
                server->buffer,
                _("%s%s: cannot parse \"%s\" command"),
                weechat_prefix ("error"), IRC_PLUGIN_NAME, "privmsg");
            free (dcc_args);
            return;
        }
        pos_port[0] = '\0';
        pos_port++;
        while (pos_port[0] == ' ')
        {
            pos_port++;
        }

        if (weechat_strcasecmp (pos_file, "chat") != 0)
        {
            weechat_printf (
                server->buffer,
                _("%s%s: unknown DCC CHAT type received from %s%s%s: \"%s\""),
                weechat_prefix ("error"),
                IRC_PLUGIN_NAME,
                irc_nick_color_for_msg (server, 0, NULL, nick),
                nick,
                IRC_COLOR_RESET,
                pos_file);
            free (dcc_args);
            return;
        }

        /* add DCC chat via xfer plugin */
        infolist = weechat_infolist_new ();
        if (infolist)
        {
            item = weechat_infolist_new_item (infolist);
            if (item)
            {
                weechat_infolist_new_var_string (item, "plugin_name", weechat_plugin->name);
                weechat_infolist_new_var_string (item, "plugin_id", server->name);
                weechat_infolist_new_var_string (item, "type_string", "chat_recv");
                weechat_infolist_new_var_string (item, "remote_nick", nick);
                weechat_infolist_new_var_string (item, "local_nick", server->nick);
                snprintf (charset_modifier, sizeof (charset_modifier),
                          "irc.%s.%s", server->name, nick);
                weechat_infolist_new_var_string (item, "charset_modifier", charset_modifier);
                weechat_infolist_new_var_string (item, "proxy",
                                                 IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_PROXY));
                weechat_infolist_new_var_string (item, "remote_address", pos_addr);
                weechat_infolist_new_var_integer (item, "port", atoi (pos_port));
                (void) weechat_hook_signal_send ("xfer_add",
                                                 WEECHAT_HOOK_SIGNAL_POINTER,
                                                 infolist);
            }
            weechat_infolist_free (infolist);
        }

        (void) weechat_hook_signal_send ("irc_dcc",
                                         WEECHAT_HOOK_SIGNAL_STRING,
                                         message);

        free (dcc_args);
    }
}


localtime_rz (timezone_t tz, time_t const *t, struct tm *tm)
{
  if (!tz)
    return gmtime_r (t, tm);
  else
    {
      timezone_t old_tz = set_tz (tz);
      if (old_tz)
        {
          bool abbr_saved = localtime_r (t, tm) && save_abbr (tz, tm);
          if (revert_tz (old_tz) && abbr_saved)
            return tm;
        }
      return NULL;
    }
}


gmt_offset (time_t s)
{
  long gmtoff;

#if !HAVE_TM_GMTOFF
  struct tm tm_local = *localtime (&s);
  struct tm tm_gmt   = *gmtime (&s);

  gmtoff = tm_diff (&tm_local, &tm_gmt);
#else
  gmtoff = localtime (&s)->tm_gmtoff;
#endif

  return gmtoff;
}


setenv_TZ (char const *tz)
{
  return tz ? setenv ("TZ", tz, 1) : unsetenv ("TZ");
}


static int nla_put_sci(struct sk_buff *skb, int attrtype, sci_t value,
		       int padattr)
{
	return nla_put_u64_64bit(skb, attrtype, (__force u64)value, padattr);
}


static int nla_put_secy(struct macsec_secy *secy, struct sk_buff *skb)
{
	struct macsec_tx_sc *tx_sc = &secy->tx_sc;
	struct nlattr *secy_nest = nla_nest_start(skb, MACSEC_ATTR_SECY);

	if (!secy_nest)
		return 1;

	if (nla_put_sci(skb, MACSEC_SECY_ATTR_SCI, secy->sci,
			MACSEC_SECY_ATTR_PAD) ||
	    nla_put_u64_64bit(skb, MACSEC_SECY_ATTR_CIPHER_SUITE,
			      MACSEC_DEFAULT_CIPHER_ID,
			      MACSEC_SECY_ATTR_PAD) ||
	    nla_put_u8(skb, MACSEC_SECY_ATTR_ICV_LEN, secy->icv_len) ||
	    nla_put_u8(skb, MACSEC_SECY_ATTR_OPER, secy->operational) ||
	    nla_put_u8(skb, MACSEC_SECY_ATTR_PROTECT, secy->protect_frames) ||
	    nla_put_u8(skb, MACSEC_SECY_ATTR_REPLAY, secy->replay_protect) ||
	    nla_put_u8(skb, MACSEC_SECY_ATTR_VALIDATE, secy->validate_frames) ||
	    nla_put_u8(skb, MACSEC_SECY_ATTR_ENCRYPT, tx_sc->encrypt) ||
	    nla_put_u8(skb, MACSEC_SECY_ATTR_INC_SCI, tx_sc->send_sci) ||
	    nla_put_u8(skb, MACSEC_SECY_ATTR_ES, tx_sc->end_station) ||
	    nla_put_u8(skb, MACSEC_SECY_ATTR_SCB, tx_sc->scb) ||
	    nla_put_u8(skb, MACSEC_SECY_ATTR_ENCODING_SA, tx_sc->encoding_sa))
		goto cancel;

	if (secy->replay_protect) {
		if (nla_put_u32(skb, MACSEC_SECY_ATTR_WINDOW, secy->replay_window))
			goto cancel;
	}

	nla_nest_end(skb, secy_nest);
	return 0;

cancel:
	nla_nest_cancel(skb, secy_nest);
	return 1;
}


static int copy_rx_sc_stats(struct sk_buff *skb,
			     struct pcpu_rx_sc_stats __percpu *pstats)
{
	struct macsec_rx_sc_stats sum = {0, };
	int cpu;

	for_each_possible_cpu(cpu) {
		const struct pcpu_rx_sc_stats *stats;
		struct macsec_rx_sc_stats tmp;
		unsigned int start;

		stats = per_cpu_ptr(pstats, cpu);
		do {
			start = u64_stats_fetch_begin_irq(&stats->syncp);
			memcpy(&tmp, &stats->stats, sizeof(tmp));
		} while (u64_stats_fetch_retry_irq(&stats->syncp, start));

		sum.InOctetsValidated += tmp.InOctetsValidated;
		sum.InOctetsDecrypted += tmp.InOctetsDecrypted;
		sum.InPktsUnchecked   += tmp.InPktsUnchecked;
		sum.InPktsDelayed     += tmp.InPktsDelayed;
		sum.InPktsOK          += tmp.InPktsOK;
		sum.InPktsInvalid     += tmp.InPktsInvalid;
		sum.InPktsLate        += tmp.InPktsLate;
		sum.InPktsNotValid    += tmp.InPktsNotValid;
		sum.InPktsNotUsingSA  += tmp.InPktsNotUsingSA;
		sum.InPktsUnusedSA    += tmp.InPktsUnusedSA;
	}

	if (nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_OCTETS_VALIDATED,
			      sum.InOctetsValidated,
			      MACSEC_RXSC_STATS_ATTR_PAD) ||
	    nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_OCTETS_DECRYPTED,
			      sum.InOctetsDecrypted,
			      MACSEC_RXSC_STATS_ATTR_PAD) ||
	    nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNCHECKED,
			      sum.InPktsUnchecked,
			      MACSEC_RXSC_STATS_ATTR_PAD) ||
	    nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_DELAYED,
			      sum.InPktsDelayed,
			      MACSEC_RXSC_STATS_ATTR_PAD) ||
	    nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_OK,
			      sum.InPktsOK,
			      MACSEC_RXSC_STATS_ATTR_PAD) ||
	    nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_INVALID,
			      sum.InPktsInvalid,
			      MACSEC_RXSC_STATS_ATTR_PAD) ||
	    nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_LATE,
			      sum.InPktsLate,
			      MACSEC_RXSC_STATS_ATTR_PAD) ||
	    nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_VALID,
			      sum.InPktsNotValid,
			      MACSEC_RXSC_STATS_ATTR_PAD) ||
	    nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_NOT_USING_SA,
			      sum.InPktsNotUsingSA,
			      MACSEC_RXSC_STATS_ATTR_PAD) ||
	    nla_put_u64_64bit(skb, MACSEC_RXSC_STATS_ATTR_IN_PKTS_UNUSED_SA,
			      sum.InPktsUnusedSA,
			      MACSEC_RXSC_STATS_ATTR_PAD))
		return -EMSGSIZE;

	return 0;
}


static sci_t macsec_frame_sci(struct macsec_eth_header *hdr, bool sci_present)
{
	sci_t sci;

	if (sci_present)
		memcpy(&sci, hdr->secure_channel_id,
		       sizeof(hdr->secure_channel_id));
	else
		sci = make_sci(hdr->eth.h_source, MACSEC_PORT_ES);

	return sci;
}