static int jpc_enc_encodemainbody(jpc_enc_t *enc)
{
	int tileno;
	jpc_sot_t *sot;
	jpc_enc_tcmpt_t *comp;
	jpc_enc_tcmpt_t *endcomps;
	jpc_enc_band_t *band;
	jpc_enc_band_t *endbands;
	jpc_enc_rlvl_t *lvl;
	unsigned rlvlno;
	jpc_qcc_t *qcc;
	jpc_cod_t *cod;
	int adjust;
	int absbandno;
	long tilehdrlen;
	long tilelen;
	jpc_enc_tile_t *tile;
	jpc_enc_cp_t *cp;
	double rho;
	unsigned cmptno;
	int samestepsizes;
	jpc_enc_ccp_t *ccps;
	jpc_enc_tccp_t *tccp;
	int bandno;
	int mingbits;
	int actualnumbps;
	jpc_fix_t mxmag;
	jpc_fix_t mag;
	int numgbits;

	cp = enc->cp;

	for (tileno = 0; tileno < JAS_CAST(int, cp->numtiles); ++tileno) {
		if (!(enc->curtile = jpc_enc_tile_create(enc->cp, enc->image,
		  tileno))) {
			jas_eprintf("cannot create tile\n");
			return -1;
		}

		tile = enc->curtile;

		if (jas_getdbglevel() >= 10) {
			jpc_enc_dump(enc);
		}

		endcomps = &tile->tcmpts[tile->numtcmpts];
		for (cmptno = 0, comp = tile->tcmpts; cmptno < tile->numtcmpts; ++cmptno, ++comp) {
			if (!cp->ccps[cmptno].sgnd) {
				adjust = 1 << (cp->ccps[cmptno].prec - 1);
				for (jas_matind_t i = 0; i < jas_matrix_numrows(comp->data); ++i) {
					for (jas_matind_t j = 0; j < jas_matrix_numcols(comp->data); ++j) {
						*jas_matrix_getref(comp->data, i, j) -= adjust;
					}
				}
			}
		}

		if (!tile->intmode) {
				endcomps = &tile->tcmpts[tile->numtcmpts];
				for (comp = tile->tcmpts; comp != endcomps; ++comp) {
					jas_matrix_asl(comp->data, JPC_FIX_FRACBITS);
				}
		}

		switch (tile->mctid) {
		case JPC_MCT_RCT:
assert(jas_image_numcmpts(enc->image) == 3);
			jpc_rct(tile->tcmpts[0].data, tile->tcmpts[1].data,
			  tile->tcmpts[2].data);
			break;
		case JPC_MCT_ICT:
assert(jas_image_numcmpts(enc->image) == 3);
			jpc_ict(tile->tcmpts[0].data, tile->tcmpts[1].data,
			  tile->tcmpts[2].data);
			break;
		default:
			break;
		}

		for (unsigned  i = 0; i < jas_image_numcmpts(enc->image); ++i) {
			comp = &tile->tcmpts[i];
			jpc_tsfb_analyze(comp->tsfb, comp->data);

		}

		endcomps = &tile->tcmpts[tile->numtcmpts];
		for (cmptno = 0, comp = tile->tcmpts; comp != endcomps; ++cmptno, ++comp) {
			mingbits = 0;
			absbandno = 0;
			/* All bands must have a corresponding quantizer step size,
			  even if they contain no samples and are never coded. */
			/* Some bands may not be hit by the loop below, so we must
			  initialize all of the step sizes to a sane value. */
			memset(comp->stepsizes, 0, sizeof(comp->stepsizes));
			for (rlvlno = 0, lvl = comp->rlvls; rlvlno < comp->numrlvls; ++rlvlno, ++lvl) {
				if (!lvl->bands) {
					absbandno += rlvlno ? 3 : 1;
					continue;
				}
				endbands = &lvl->bands[lvl->numbands];
				for (band = lvl->bands; band != endbands; ++band) {
					if (!band->data) {
						++absbandno;
						continue;
					}
					actualnumbps = 0;
					mxmag = 0;
					for (jas_matind_t y = 0; y < jas_matrix_numrows(band->data); ++y) {
						for (jas_matind_t x = 0; x < jas_matrix_numcols(band->data); ++x) {
							mag = JAS_ABS(jas_matrix_get(band->data, y, x));
							if (mag > mxmag) {
								mxmag = mag;
							}
						}
					}
					if (tile->intmode) {
						actualnumbps = jpc_fix_firstone(mxmag) + 1;
					} else {
						actualnumbps = jpc_fix_firstone(mxmag) + 1 - JPC_FIX_FRACBITS;
					}
					numgbits = actualnumbps - (cp->ccps[cmptno].prec - 1 +
					  band->analgain);
#if 0
jas_eprintf("%d %d mag=%d actual=%d numgbits=%d\n", cp->ccps[cmptno].prec, band->analgain, mxmag, actualnumbps, numgbits);
#endif
					if (numgbits > mingbits) {
						mingbits = numgbits;
					}
					if (!tile->intmode) {
						band->absstepsize = jpc_fix_div(jpc_inttofix(1
						  << (band->analgain + 1)),
						  band->synweight);
					} else {
						band->absstepsize = jpc_inttofix(1);
					}
					const uint_fast32_t stepsize = jpc_abstorelstepsize(
					  band->absstepsize, cp->ccps[cmptno].prec +
					  band->analgain);
					if (stepsize == UINT_FAST32_MAX)
						return -1;
					band->stepsize = stepsize;
					band->numbps = cp->tccp.numgbits +
					  JPC_QCX_GETEXPN(band->stepsize) - 1;

					if ((!tile->intmode) && band->data) {
						jpc_quantize(band->data, band->absstepsize);
					}

					comp->stepsizes[absbandno] = band->stepsize;
					++absbandno;
				}
			}

			assert(JPC_FIX_FRACBITS >= JPC_NUMEXTRABITS);
			if (!tile->intmode) {
				jas_matrix_divpow2(comp->data, JPC_FIX_FRACBITS - JPC_NUMEXTRABITS);
			} else {
				jas_matrix_asl(comp->data, JPC_NUMEXTRABITS);
			}

#if 0
jas_eprintf("mingbits %d\n", mingbits);
#endif
			if (mingbits > cp->tccp.numgbits) {
				jas_eprintf("error: too few guard bits (need at least %d)\n",
				  mingbits);
				return -1;
			}
		}

		if (!(enc->tmpstream = jas_stream_memopen(0, 0))) {
			jas_eprintf("cannot open tmp file\n");
			return -1;
		}

		/* Write the tile header. */
		if (!(enc->mrk = jpc_ms_create(JPC_MS_SOT))) {
			return -1;
		}
		sot = &enc->mrk->parms.sot;
		sot->len = 0;
		sot->tileno = tileno;
		sot->partno = 0;
		sot->numparts = 1;
		if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
			jas_eprintf("cannot write SOT marker\n");
			return -1;
		}
		jpc_ms_destroy(enc->mrk);
		enc->mrk = 0;

/************************************************************************/
/************************************************************************/
/************************************************************************/

		tccp = &cp->tccp;
		for (cmptno = 0; cmptno < cp->numcmpts; ++cmptno) {
			comp = &tile->tcmpts[cmptno];
			if (comp->numrlvls != tccp->maxrlvls) {
				if (!(enc->mrk = jpc_ms_create(JPC_MS_COD))) {
					return -1;
				}
/* XXX = this is not really correct. we are using comp #0's precint sizes
and other characteristics */
				comp = &tile->tcmpts[0];
				cod = &enc->mrk->parms.cod;
				cod->compparms.csty = 0;
				cod->compparms.numdlvls = comp->numrlvls - 1;
				cod->prg = tile->prg;
				cod->numlyrs = tile->numlyrs;
				cod->compparms.cblkwidthval = JPC_COX_CBLKSIZEEXPN(comp->cblkwidthexpn);
				cod->compparms.cblkheightval = JPC_COX_CBLKSIZEEXPN(comp->cblkheightexpn);
				cod->compparms.cblksty = comp->cblksty;
				cod->compparms.qmfbid = comp->qmfbid;
				cod->mctrans = (tile->mctid != JPC_MCT_NONE);
				for (unsigned i = 0; i < comp->numrlvls; ++i) {
					cod->compparms.rlvls[i].parwidthval = comp->rlvls[i].prcwidthexpn;
					cod->compparms.rlvls[i].parheightval = comp->rlvls[i].prcheightexpn;
				}
				if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
					return -1;
				}
				jpc_ms_destroy(enc->mrk);
				enc->mrk = 0;
			}
		}

		for (cmptno = 0, comp = tile->tcmpts; cmptno < cp->numcmpts; ++cmptno, ++comp) {
			ccps = &cp->ccps[cmptno];
			if (JAS_CAST(int, ccps->numstepsizes) == comp->numstepsizes) {
				samestepsizes = 1;
				for (bandno = 0; bandno < JAS_CAST(int, ccps->numstepsizes);
				  ++bandno) {
					if (ccps->stepsizes[bandno] != comp->stepsizes[bandno]) {
						samestepsizes = 0;
						break;
					}
				}
			} else {
				samestepsizes = 0;
			}
			if (!samestepsizes) {
				if (!(enc->mrk = jpc_ms_create(JPC_MS_QCC))) {
					return -1;
				}
				qcc = &enc->mrk->parms.qcc;
				qcc->compno = cmptno;
				qcc->compparms.numguard = cp->tccp.numgbits;
				qcc->compparms.qntsty = (comp->qmfbid == JPC_COX_INS) ?
				  JPC_QCX_SEQNT : JPC_QCX_NOQNT;
				qcc->compparms.numstepsizes = comp->numstepsizes;
				qcc->compparms.stepsizes = comp->stepsizes;
				if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
					return -1;
				}
				qcc->compparms.stepsizes = 0;
				jpc_ms_destroy(enc->mrk);
				enc->mrk = 0;
			}
		}

		/* Write a SOD marker to indicate the end of the tile header. */
		if (!(enc->mrk = jpc_ms_create(JPC_MS_SOD))) {
			return -1;
		}
		if (jpc_putms(enc->tmpstream, enc->cstate, enc->mrk)) {
			jas_eprintf("cannot write SOD marker\n");
			return -1;
		}
		jpc_ms_destroy(enc->mrk);
		enc->mrk = 0;
		tilehdrlen = jas_stream_getrwcount(enc->tmpstream);
		assert(tilehdrlen >= 0);

/************************************************************************/
/************************************************************************/
/************************************************************************/

		if (jpc_enc_enccblks(enc)) {
			return -1;
		}

		cp = enc->cp;
		rho = (double) (tile->brx - tile->tlx) * (tile->bry - tile->tly) /
		  ((cp->refgrdwidth - cp->imgareatlx) * (cp->refgrdheight -
		  cp->imgareatly));
		tile->rawsize = cp->rawsize * rho;

		for (unsigned lyrno = 0; lyrno < tile->numlyrs - 1; ++lyrno) {
			tile->lyrsizes[lyrno] = tile->rawsize * jpc_fixtodbl(
			  cp->tcp.ilyrrates[lyrno]);
		}
#if !defined(__clang__)
		// WARNING:
		// Some versions of Clang (e.g., 3.7.1 and 3.8.1) appear to generate
		// incorrect code for the following line.
		tile->lyrsizes[tile->numlyrs - 1] =
		  (cp->totalsize != UINT_FAST32_MAX) ?
		  (rho * enc->mainbodysize) : UINT_FAST32_MAX;
#else
		if (cp->totalsize != UINT_FAST32_MAX) {
			tile->lyrsizes[tile->numlyrs - 1] = (rho * enc->mainbodysize);
		} else {
			tile->lyrsizes[tile->numlyrs - 1] = UINT_FAST32_MAX;
		}
#endif
//jas_eprintf("TESTING %ld %ld\n", cp->totalsize != UINT_FAST32_MAX, tile->lyrsizes[0]);
		for (unsigned lyrno = 0; lyrno < tile->numlyrs; ++lyrno) {
			if (tile->lyrsizes[lyrno] != UINT_FAST32_MAX) {
				if (JAS_CAST(uint_fast32_t, tilehdrlen) <= tile->lyrsizes[lyrno]) {
					tile->lyrsizes[lyrno] -= tilehdrlen;
				} else {
					tile->lyrsizes[lyrno] = 0;
				}
			}
		}

		if (rateallocate(enc, tile->numlyrs, tile->lyrsizes)) {
			return -1;
		}

#if 0
jas_eprintf("ENCODE TILE DATA\n");
#endif
		if (jpc_enc_encodetiledata(enc)) {
			jas_eprintf("dotile failed\n");
			return -1;
		}

/************************************************************************/
/************************************************************************/
/************************************************************************/

/************************************************************************/
/************************************************************************/
/************************************************************************/

		tilelen = jas_stream_tell(enc->tmpstream);

		if (jas_stream_seek(enc->tmpstream, 6, SEEK_SET) < 0) {
			return -1;
		}
		jpc_putuint32(enc->tmpstream, tilelen);

		if (jas_stream_seek(enc->tmpstream, 0, SEEK_SET) < 0) {
			return -1;
		}
		if (jpc_putdata(enc->out, enc->tmpstream, -1)) {
			return -1;
		}
		enc->len += tilelen;

		jas_stream_close(enc->tmpstream);
		enc->tmpstream = 0;

		jpc_enc_tile_destroy(enc->curtile);
		enc->curtile = 0;

	}

	return 0;
}


void jpc_quantize(jas_matrix_t *data, jpc_fix_t stepsize)
{
	jpc_fix_t t;

	if (stepsize == jpc_inttofix(1)) {
		return;
	}

	for (jas_matind_t i = 0; i < jas_matrix_numrows(data); ++i) {
		for (jas_matind_t j = 0; j < jas_matrix_numcols(data); ++j) {
			t = jas_matrix_get(data, i, j);

{
	if (t < 0) {
		t = jpc_fix_neg(jpc_fix_div(jpc_fix_neg(t), stepsize));
	} else {
		t = jpc_fix_div(t, stepsize);
	}
}

			jas_matrix_set(data, i, j, t);
		}
	}
}

Avoid maxrlvls more than upper bound to cause heap-buffer-overflow


int rateallocate(jpc_enc_t *enc, unsigned numlyrs, uint_fast32_t *cumlens)
{
	jpc_flt_t lo;
	jpc_flt_t hi;
	jas_stream_t *out;
	uint_fast32_t cumlen;
	jpc_flt_t thresh;
	jpc_flt_t goodthresh;
	int success;
	long pos;
	long oldpos;
	int numiters;

	jpc_enc_tcmpt_t *comp;
	jpc_enc_tcmpt_t *endcomps;
	jpc_enc_rlvl_t *lvl;
	jpc_enc_rlvl_t *endlvls;
	jpc_enc_band_t *band;
	jpc_enc_band_t *endbands;
	jpc_enc_cblk_t *cblk;
	jpc_enc_cblk_t *endcblks;
	jpc_enc_pass_t *pass;
	jpc_enc_pass_t *endpasses;
	jpc_enc_pass_t *pass1;
	jpc_flt_t mxrdslope;
	jpc_flt_t mnrdslope;
	jpc_enc_tile_t *tile;
	jpc_enc_prc_t *prc;
	unsigned prcno;

	JAS_DBGLOG(10, ("starting rate allocation\n"));

	tile = enc->curtile;

	for (unsigned lyrno = 1; lyrno < numlyrs - 1; ++lyrno) {
		assert(cumlens[lyrno - 1] <= cumlens[lyrno]);
	}

	if (!(out = jas_stream_memopen(0, 0))) {
		return -1;
	}

	/* Find minimum and maximum R-D slope values. */
	mnrdslope = DBL_MAX;
	mxrdslope = 0;
	endcomps = &tile->tcmpts[tile->numtcmpts];
	for (comp = tile->tcmpts; comp != endcomps; ++comp) {
		endlvls = &comp->rlvls[comp->numrlvls];
		for (lvl = comp->rlvls; lvl != endlvls; ++lvl) {
			if (!lvl->bands) {
				continue;
			}
			endbands = &lvl->bands[lvl->numbands];
			for (band = lvl->bands; band != endbands; ++band) {
				if (!band->data) {
					continue;
				}
				for (prcno = 0, prc = band->prcs; prcno < lvl->numprcs;
				  ++prcno, ++prc) {
					if (!prc->cblks) {
						continue;
					}
					endcblks = &prc->cblks[prc->numcblks];
					for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
						calcrdslopes(cblk);
						endpasses = &cblk->passes[cblk->numpasses];
						for (pass = cblk->passes; pass != endpasses; ++pass) {
							if (pass->rdslope > 0) {
								if (pass->rdslope < mnrdslope) {
									mnrdslope = pass->rdslope;
								}
								if (pass->rdslope > mxrdslope) {
									mxrdslope = pass->rdslope;
								}
							}
						}
					}
				}
			}
		}
	}
	JAS_DBGLOG(10, ("min rdslope = %f max rdslope = %f\n", mnrdslope,
	  mxrdslope));

	jpc_init_t2state(enc, true);

	for (unsigned lyrno = 0; lyrno < numlyrs; ++lyrno) {

		lo = mnrdslope;
		hi = mxrdslope;

		success = 0;
		goodthresh = 0;
		numiters = 0;

		do {

			cumlen = cumlens[lyrno];
			if (cumlen == UINT_FAST32_MAX) {
				/* Only the last layer can be free of a rate
				  constraint (e.g., for lossless coding). */
				assert(lyrno == numlyrs - 1);
				goodthresh = -1;
				success = 1;
				break;
			}

			thresh = (lo + hi) / 2;

			/* Save the tier 2 coding state. */
			jpc_save_t2state(enc);
			oldpos = jas_stream_tell(out);
			assert(oldpos >= 0);

			/* Assign all passes with R-D slopes greater than or
			  equal to the current threshold to this layer. */
			endcomps = &tile->tcmpts[tile->numtcmpts];
			for (comp = tile->tcmpts; comp != endcomps; ++comp) {
				endlvls = &comp->rlvls[comp->numrlvls];
				for (lvl = comp->rlvls; lvl != endlvls; ++lvl) {
					if (!lvl->bands) {
						continue;
					}
					endbands = &lvl->bands[lvl->numbands];
					for (band = lvl->bands; band != endbands; ++band) {
						if (!band->data) {
							continue;
						}
						for (prcno = 0, prc = band->prcs; prcno < lvl->numprcs;
						  ++prcno, ++prc) {
							if (!prc->cblks) {
								continue;
							}
							endcblks = &prc->cblks[prc->numcblks];
							for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
								if (cblk->curpass) {
									endpasses = &cblk->passes[cblk->numpasses];
									pass1 = cblk->curpass;
									for (pass = cblk->curpass; pass !=
									  endpasses; ++pass) {
										if (pass->rdslope >= thresh) {
											pass1 = &pass[1];
										}
									}
									for (pass = cblk->curpass; pass != pass1;
									  ++pass) {
										pass->lyrno = lyrno;
									}
									for (; pass != endpasses; ++pass) {
										pass->lyrno = -1;
									}
								}
							}
						}
					}
				}
			}

			/* Perform tier 2 coding. */
			endcomps = &tile->tcmpts[tile->numtcmpts];
			for (comp = tile->tcmpts; comp != endcomps; ++comp) {
				endlvls = &comp->rlvls[comp->numrlvls];
				for (lvl = comp->rlvls; lvl != endlvls; ++lvl) {
					if (!lvl->bands) {
						continue;
					}
					for (prcno = 0; prcno < lvl->numprcs; ++prcno) {
						if (jpc_enc_encpkt(enc, out, comp - tile->tcmpts,
						  lvl - comp->rlvls, prcno, lyrno)) {
							return -1;
						}
					}
				}
			}

			pos = jas_stream_tell(out);

			/* Check the rate constraint. */
			assert(pos >= 0);
			if ((uint_fast32_t)pos > cumlen) {
				/* The rate is too high. */
				lo = thresh;
			} else if ((uint_fast32_t)pos <= cumlen) {
				/* The rate is low enough, so try higher. */
				hi = thresh;
				if (!success || thresh < goodthresh) {
					goodthresh = thresh;
					success = 1;
				}
			}

			/* Save the tier 2 coding state. */
			jpc_restore_t2state(enc);
			if (jas_stream_seek(out, oldpos, SEEK_SET) < 0) {
				return -1;
			}

			JAS_DBGLOG(10, ("maxlen=%08ld actuallen=%08ld thresh=%f\n",
			  cumlen, pos, thresh));

			++numiters;
		} while (lo < hi - 1e-3 && numiters < 32);

		if (!success) {
			jas_eprintf("warning: empty layer generated\n");
		}

		JAS_DBGLOG(10, ("success %d goodthresh %f\n", success, goodthresh));

		/* Assign all passes with R-D slopes greater than or
		  equal to the selected threshold to this layer. */
		endcomps = &tile->tcmpts[tile->numtcmpts];
		for (comp = tile->tcmpts; comp != endcomps; ++comp) {
			endlvls = &comp->rlvls[comp->numrlvls];
			for (lvl = comp->rlvls; lvl != endlvls; ++lvl) {
				if (!lvl->bands) {
					continue;
				}
				endbands = &lvl->bands[lvl->numbands];
				for (band = lvl->bands; band != endbands; ++band) {
					if (!band->data) {
						continue;
					}
					for (prcno = 0, prc = band->prcs; prcno < lvl->numprcs;
					  ++prcno, ++prc) {
						if (!prc->cblks) {
							continue;
						}
						endcblks = &prc->cblks[prc->numcblks];
						for (cblk = prc->cblks; cblk != endcblks; ++cblk) {
							if (cblk->curpass) {
								endpasses = &cblk->passes[cblk->numpasses];
								pass1 = cblk->curpass;
								if (success) {
									for (pass = cblk->curpass; pass !=
									  endpasses; ++pass) {
										if (pass->rdslope >= goodthresh) {
											pass1 = &pass[1];
										}
									}
								}
								for (pass = cblk->curpass; pass != pass1;
								  ++pass) {
									pass->lyrno = lyrno;
								}
								for (; pass != endpasses; ++pass) {
									pass->lyrno = -1;
								}
							}
						}
					}
				}
			}
		}

		/* Perform tier 2 coding. */
		endcomps = &tile->tcmpts[tile->numtcmpts];
		for (comp = tile->tcmpts; comp != endcomps; ++comp) {
			endlvls = &comp->rlvls[comp->numrlvls];
			for (lvl = comp->rlvls; lvl != endlvls; ++lvl) {
				if (!lvl->bands) {
					continue;
				}
				for (prcno = 0; prcno < lvl->numprcs; ++prcno) {
					if (jpc_enc_encpkt(enc, out, comp - tile->tcmpts,
					  lvl - comp->rlvls, prcno, lyrno)) {
						return -1;
					}
				}
			}
		}
	}

	if (jas_getdbglevel() >= 5) {
		dump_layeringinfo(enc);
	}

	jas_stream_close(out);

	JAS_DBGLOG(10, ("finished rate allocation\n"));

	return 0;
}


static int jpc_enc_encodemainhdr(jpc_enc_t *enc)
{
	jpc_siz_t *siz;
	jpc_cod_t *cod;
	jpc_qcd_t *qcd;
	int i;
long startoff;
long mainhdrlen;
	jpc_enc_cp_t *cp;
	jpc_qcc_t *qcc;
	jpc_enc_tccp_t *tccp;
	uint_fast16_t cmptno;
	jpc_tsfb_band_t bandinfos[JPC_MAXBANDS];
	jpc_enc_tcp_t *tcp;
	jpc_tsfb_t *tsfb;
	jpc_tsfb_band_t *bandinfo;
	uint_fast16_t numbands;
	uint_fast16_t bandno;
	uint_fast16_t rlvlno;
	uint_fast16_t analgain;
	jpc_fix_t absstepsize;
	char buf[1024];
	jpc_com_t *com;

	cp = enc->cp;

startoff = jas_stream_getrwcount(enc->out);

	/* Write SOC marker segment. */
	if (!(enc->mrk = jpc_ms_create(JPC_MS_SOC))) {
		jas_eprintf("cannot create SOC marker\n");
		return -1;
	}
	if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
		jas_eprintf("cannot write SOC marker\n");
		return -1;
	}
	jpc_ms_destroy(enc->mrk);
	enc->mrk = 0;

	/* Write SIZ marker segment. */
	if (!(enc->mrk = jpc_ms_create(JPC_MS_SIZ))) {
		jas_eprintf("cannot create SIZ marker\n");
		return -1;
	}
	siz = &enc->mrk->parms.siz;
	siz->caps = 0;
	siz->xoff = cp->imgareatlx;
	siz->yoff = cp->imgareatly;
	siz->width = cp->refgrdwidth;
	siz->height = cp->refgrdheight;
	siz->tilexoff = cp->tilegrdoffx;
	siz->tileyoff = cp->tilegrdoffy;
	siz->tilewidth = cp->tilewidth;
	siz->tileheight = cp->tileheight;
	siz->numcomps = cp->numcmpts;
	siz->comps = jas_alloc2(siz->numcomps, sizeof(jpc_sizcomp_t));
	assert(siz->comps);
	for (i = 0; i < JAS_CAST(int, cp->numcmpts); ++i) {
		siz->comps[i].prec = cp->ccps[i].prec;
		siz->comps[i].sgnd = cp->ccps[i].sgnd;
		siz->comps[i].hsamp = cp->ccps[i].sampgrdstepx;
		siz->comps[i].vsamp = cp->ccps[i].sampgrdstepy;
	}
	if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
		jas_eprintf("cannot write SIZ marker\n");
		return -1;
	}
	jpc_ms_destroy(enc->mrk);
	enc->mrk = 0;

	if (!(enc->mrk = jpc_ms_create(JPC_MS_COM))) {
		jas_eprintf("cannot create COM marker\n");
		return -1;
	}
	sprintf(buf, "Creator: JasPer Version %s", jas_getversion());
	com = &enc->mrk->parms.com;
	com->len = JAS_CAST(uint_fast16_t, strlen(buf));
	com->regid = JPC_COM_LATIN;
	if (!(com->data = JAS_CAST(jas_uchar *, jas_strdup(buf)))) {
		return -1;
	}
	if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
		jas_eprintf("cannot write COM marker\n");
		return -1;
	}
	jpc_ms_destroy(enc->mrk);
	enc->mrk = 0;

#if 0
	if (!(enc->mrk = jpc_ms_create(JPC_MS_CRG))) {
		return -1;
	}
	crg = &enc->mrk->parms.crg;
	crg->comps = jas_alloc2(crg->numcomps, sizeof(jpc_crgcomp_t));
	if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
		jas_eprintf("cannot write CRG marker\n");
		return -1;
	}
	jpc_ms_destroy(enc->mrk);
	enc->mrk = 0;
#endif

	tcp = &cp->tcp;
	tccp = &cp->tccp;
	for (cmptno = 0; cmptno < cp->numcmpts; ++cmptno) {
		tsfb = jpc_cod_gettsfb(tccp->qmfbid, tccp->maxrlvls - 1);
		jpc_tsfb_getbands(tsfb, 0, 0, 1 << tccp->maxrlvls, 1 << tccp->maxrlvls,
		  bandinfos);
		jpc_tsfb_destroy(tsfb);
		numbands = 3 * tccp->maxrlvls - 2;
		for (bandno = 0, bandinfo = bandinfos; bandno < numbands;
		  ++bandno, ++bandinfo) {
			rlvlno = (bandno) ? ((bandno - 1) / 3 + 1) : 0;
			analgain = JPC_NOMINALGAIN(tccp->qmfbid, tccp->maxrlvls,
			  rlvlno, bandinfo->orient);
			if (!tcp->intmode) {
				absstepsize = jpc_fix_div(jpc_inttofix(1 <<
				  (analgain + 1)), bandinfo->synenergywt);
			} else {
				absstepsize = jpc_inttofix(1);
			}
			const uint_fast32_t stepsize =
			  jpc_abstorelstepsize(absstepsize,
			  cp->ccps[cmptno].prec + analgain);
			if (stepsize == UINT_FAST32_MAX)
				return -1;
			cp->ccps[cmptno].stepsizes[bandno] = stepsize;
		}
		cp->ccps[cmptno].numstepsizes = numbands;
	}

	if (!(enc->mrk = jpc_ms_create(JPC_MS_COD))) {
		jas_eprintf("cannot create COD marker\n");
		return -1;
	}
	cod = &enc->mrk->parms.cod;
	cod->csty = cp->tccp.csty | cp->tcp.csty;
	cod->compparms.csty = cp->tccp.csty | cp->tcp.csty;
	cod->compparms.numdlvls = cp->tccp.maxrlvls - 1;
	cod->compparms.numrlvls = cp->tccp.maxrlvls;
	cod->prg = cp->tcp.prg;
	cod->numlyrs = cp->tcp.numlyrs;
	cod->compparms.cblkwidthval = JPC_COX_CBLKSIZEEXPN(cp->tccp.cblkwidthexpn);
	cod->compparms.cblkheightval = JPC_COX_CBLKSIZEEXPN(cp->tccp.cblkheightexpn);
	cod->compparms.cblksty = cp->tccp.cblksty;
	cod->compparms.qmfbid = cp->tccp.qmfbid;
	cod->mctrans = (cp->tcp.mctid != JPC_MCT_NONE);
	if (tccp->csty & JPC_COX_PRT) {
		for (rlvlno = 0; rlvlno < tccp->maxrlvls; ++rlvlno) {
			cod->compparms.rlvls[rlvlno].parwidthval = tccp->prcwidthexpns[rlvlno];
			cod->compparms.rlvls[rlvlno].parheightval = tccp->prcheightexpns[rlvlno];
		}
	}
	if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
		jas_eprintf("cannot write COD marker\n");
		return -1;
	}
	jpc_ms_destroy(enc->mrk);
	enc->mrk = 0;

	if (!(enc->mrk = jpc_ms_create(JPC_MS_QCD))) {
		jas_eprintf("cannot create QCD marker\n");
		return -1;
	}
	qcd = &enc->mrk->parms.qcd;
	qcd->compparms.qntsty = (tccp->qmfbid == JPC_COX_INS) ?
	  JPC_QCX_SEQNT : JPC_QCX_NOQNT;
	qcd->compparms.numstepsizes = cp->ccps[0].numstepsizes;
	qcd->compparms.numguard = cp->tccp.numgbits;
	qcd->compparms.stepsizes = cp->ccps[0].stepsizes;
	if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
		jas_eprintf("cannot write marker\n");
		return -1;
	}
	/* We do not want the step size array to be freed! */
	qcd->compparms.stepsizes = 0;
	jpc_ms_destroy(enc->mrk);
	enc->mrk = 0;

	tccp = &cp->tccp;
	for (cmptno = 1; cmptno < cp->numcmpts; ++cmptno) {
		if (!(enc->mrk = jpc_ms_create(JPC_MS_QCC))) {
			jas_eprintf("cannot create QCC marker\n");
			return -1;
		}
		qcc = &enc->mrk->parms.qcc;
		qcc->compno = cmptno;
		qcc->compparms.qntsty = (tccp->qmfbid == JPC_COX_INS) ?
		  JPC_QCX_SEQNT : JPC_QCX_NOQNT;
		qcc->compparms.numstepsizes = cp->ccps[cmptno].numstepsizes;
		qcc->compparms.numguard = cp->tccp.numgbits;
		qcc->compparms.stepsizes = cp->ccps[cmptno].stepsizes;
		if (jpc_putms(enc->out, enc->cstate, enc->mrk)) {
			jas_eprintf("cannot write marker\n");
			return -1;
		}
		/* We do not want the step size array to be freed! */
		qcc->compparms.stepsizes = 0;
		jpc_ms_destroy(enc->mrk);
		enc->mrk = 0;
	}

#define MAINTLRLEN	2
	mainhdrlen = jas_stream_getrwcount(enc->out) - startoff;
	enc->len += mainhdrlen;
	if (enc->cp->totalsize != UINT_FAST32_MAX) {
		uint_fast32_t overhead;
		overhead = mainhdrlen + MAINTLRLEN;
		enc->mainbodysize = (enc->cp->totalsize >= overhead) ?
		  (enc->cp->totalsize - overhead) : 0;
	} else {
		enc->mainbodysize = UINT_FAST32_MAX;
	}

	return 0;
}


static jpc_enc_cp_t *cp_create(const char *optstr, jas_image_t *image)
{
	jpc_enc_cp_t *cp;
	jas_tvparser_t *tvp;
	int ret;
	int numilyrrates;
	double *ilyrrates;
	int i;
	int tagid;
	jpc_enc_tcp_t *tcp;
	jpc_enc_tccp_t *tccp;
	jpc_enc_ccp_t *ccp;
	uint_fast16_t rlvlno;
	uint_fast16_t prcwidthexpn;
	uint_fast16_t prcheightexpn;
	bool enablemct;
	uint_fast32_t jp2overhead;
	uint_fast16_t lyrno;
	uint_fast32_t hsteplcm;
	uint_fast32_t vsteplcm;
	bool mctvalid;

	tvp = 0;
	cp = 0;
	ilyrrates = 0;
	numilyrrates = 0;

	if (!(cp = jas_malloc(sizeof(jpc_enc_cp_t)))) {
		goto error;
	}

	prcwidthexpn = 15;
	prcheightexpn = 15;
	enablemct = true;
	jp2overhead = 0;

	cp->ccps = 0;
	cp->debug = 0;
	cp->imgareatlx = UINT_FAST32_MAX;
	cp->imgareatly = UINT_FAST32_MAX;
	cp->refgrdwidth = 0;
	cp->refgrdheight = 0;
	cp->tilegrdoffx = UINT_FAST32_MAX;
	cp->tilegrdoffy = UINT_FAST32_MAX;
	cp->tilewidth = 0;
	cp->tileheight = 0;
	cp->numcmpts = jas_image_numcmpts(image);

	hsteplcm = 1;
	vsteplcm = 1;
	for (unsigned cmptno = 0; cmptno < jas_image_numcmpts(image); ++cmptno) {
		if (jas_image_cmptbrx(image, cmptno) + jas_image_cmpthstep(image, cmptno) <=
		  jas_image_brx(image) || jas_image_cmptbry(image, cmptno) +
		  jas_image_cmptvstep(image, cmptno) <= jas_image_bry(image)) {
			jas_eprintf("unsupported image type\n");
			goto error;
		}
		/* Note: We ought to be calculating the LCMs here.  Fix some day. */
		hsteplcm *= jas_image_cmpthstep(image, cmptno);
		vsteplcm *= jas_image_cmptvstep(image, cmptno);
	}

	if (!(cp->ccps = jas_alloc2(cp->numcmpts, sizeof(jpc_enc_ccp_t)))) {
		goto error;
	}
	unsigned cmptno;
	for (cmptno = 0, ccp = cp->ccps; cmptno < cp->numcmpts; ++cmptno,
	  ++ccp) {
		ccp->sampgrdstepx = jas_image_cmpthstep(image, cmptno);
		ccp->sampgrdstepy = jas_image_cmptvstep(image, cmptno);
		/* XXX - this isn't quite correct for more general image */
		ccp->sampgrdsubstepx = 0;
		ccp->sampgrdsubstepx = 0;
		ccp->prec = jas_image_cmptprec(image, cmptno);
		ccp->sgnd = jas_image_cmptsgnd(image, cmptno);
		ccp->numstepsizes = 0;
		memset(ccp->stepsizes, 0, sizeof(ccp->stepsizes));
	}

	cp->rawsize = jas_image_rawsize(image);
	if (cp->rawsize == 0) {
		/* prevent division by zero in cp_create() */
		goto error;
	}
	cp->totalsize = UINT_FAST32_MAX;

	tcp = &cp->tcp;
	tcp->csty = 0;
	tcp->intmode = true;
	tcp->prg = JPC_COD_LRCPPRG;
	tcp->numlyrs = 1;
	tcp->ilyrrates = 0;

	tccp = &cp->tccp;
	tccp->csty = 0;
	tccp->maxrlvls = 6;
	tccp->cblkwidthexpn = 6;
	tccp->cblkheightexpn = 6;
	tccp->cblksty = 0;
	tccp->numgbits = 2;

	if (!(tvp = jas_tvparser_create(optstr ? optstr : ""))) {
		goto error;
	}

	while (!(ret = jas_tvparser_next(tvp))) {
		switch (jas_taginfo_nonull(jas_taginfos_lookup(encopts,
		  jas_tvparser_gettag(tvp)))->id) {
		case OPT_DEBUG:
			cp->debug = atoi(jas_tvparser_getval(tvp));
			break;
		case OPT_IMGAREAOFFX:
			cp->imgareatlx = atoi(jas_tvparser_getval(tvp));
			break;
		case OPT_IMGAREAOFFY:
			cp->imgareatly = atoi(jas_tvparser_getval(tvp));
			break;
		case OPT_TILEGRDOFFX:
			cp->tilegrdoffx = atoi(jas_tvparser_getval(tvp));
			break;
		case OPT_TILEGRDOFFY:
			cp->tilegrdoffy = atoi(jas_tvparser_getval(tvp));
			break;
		case OPT_TILEWIDTH:
			cp->tilewidth = atoi(jas_tvparser_getval(tvp));
			break;
		case OPT_TILEHEIGHT:
			cp->tileheight = atoi(jas_tvparser_getval(tvp));
			break;
		case OPT_PRCWIDTH:
			prcwidthexpn = jpc_floorlog2(atoi(jas_tvparser_getval(tvp)));
			break;
		case OPT_PRCHEIGHT:
			prcheightexpn = jpc_floorlog2(atoi(jas_tvparser_getval(tvp)));
			break;
		case OPT_CBLKWIDTH:
			tccp->cblkwidthexpn =
			  jpc_floorlog2(atoi(jas_tvparser_getval(tvp)));
			break;
		case OPT_CBLKHEIGHT:
			tccp->cblkheightexpn =
			  jpc_floorlog2(atoi(jas_tvparser_getval(tvp)));
			break;
		case OPT_MODE:
			if ((tagid = jas_taginfo_nonull(jas_taginfos_lookup(modetab,
			  jas_tvparser_getval(tvp)))->id) < 0) {
				jas_eprintf("ignoring invalid mode %s\n",
				  jas_tvparser_getval(tvp));
			} else {
				tcp->intmode = (tagid == MODE_INT);
			}
			break;
		case OPT_PRG:
			if ((tagid = jas_taginfo_nonull(jas_taginfos_lookup(prgordtab,
			  jas_tvparser_getval(tvp)))->id) < 0) {
				jas_eprintf("ignoring invalid progression order %s\n",
				  jas_tvparser_getval(tvp));
			} else {
				tcp->prg = tagid;
			}
			break;
		case OPT_NOMCT:
			enablemct = false;
			break;
		case OPT_MAXRLVLS:
			tccp->maxrlvls = atoi(jas_tvparser_getval(tvp));
			if(tccp->maxrlvls > JPC_MAXRLVLS) {
				jas_eprintf("invalid number of resolution levels upper than %d\n",JPC_MAXRLVLS);
				goto error;
			}
			break;
		case OPT_SOP:
			cp->tcp.csty |= JPC_COD_SOP;
			break;
		case OPT_EPH:
			cp->tcp.csty |= JPC_COD_EPH;
			break;
		case OPT_LAZY:
			tccp->cblksty |= JPC_COX_LAZY;
			break;
		case OPT_TERMALL:
			tccp->cblksty |= JPC_COX_TERMALL;
			break;
		case OPT_SEGSYM:
			tccp->cblksty |= JPC_COX_SEGSYM;
			break;
		case OPT_VCAUSAL:
			tccp->cblksty |= JPC_COX_VSC;
			break;
		case OPT_RESET:
			tccp->cblksty |= JPC_COX_RESET;
			break;
		case OPT_PTERM:
			tccp->cblksty |= JPC_COX_PTERM;
			break;
		case OPT_NUMGBITS:
			cp->tccp.numgbits = atoi(jas_tvparser_getval(tvp));
			break;
		case OPT_RATE:
			if (ratestrtosize(jas_tvparser_getval(tvp), cp->rawsize,
			  &cp->totalsize)) {
				jas_eprintf("ignoring bad rate specifier %s\n",
				  jas_tvparser_getval(tvp));
			}
			break;
		case OPT_ILYRRATES:
			if (jpc_atoaf(jas_tvparser_getval(tvp), &numilyrrates,
			  &ilyrrates)) {
				jas_eprintf("warning: invalid intermediate layer rates specifier ignored (%s)\n",
				  jas_tvparser_getval(tvp));
			}
			break;

		case OPT_JP2OVERHEAD:
			jp2overhead = atoi(jas_tvparser_getval(tvp));
			break;
		default:
			jas_eprintf("warning: ignoring invalid option %s\n",
			 jas_tvparser_gettag(tvp));
			break;
		}
	}

	jas_tvparser_destroy(tvp);
	tvp = 0;

	if (cp->totalsize != UINT_FAST32_MAX) {
		cp->totalsize = (cp->totalsize > jp2overhead) ?
		  (cp->totalsize - jp2overhead) : 0;
	}

	if (cp->imgareatlx == UINT_FAST32_MAX) {
		cp->imgareatlx = 0;
	} else {
		if (hsteplcm != 1) {
			jas_eprintf("warning: overriding imgareatlx value\n");
		}
		cp->imgareatlx *= hsteplcm;
	}
	if (cp->imgareatly == UINT_FAST32_MAX) {
		cp->imgareatly = 0;
	} else {
		if (vsteplcm != 1) {
			jas_eprintf("warning: overriding imgareatly value\n");
		}
		cp->imgareatly *= vsteplcm;
	}
	cp->refgrdwidth = cp->imgareatlx + jas_image_width(image);
	cp->refgrdheight = cp->imgareatly + jas_image_height(image);
	if (cp->tilegrdoffx == UINT_FAST32_MAX) {
		cp->tilegrdoffx = cp->imgareatlx;
	}
	if (cp->tilegrdoffy == UINT_FAST32_MAX) {
		cp->tilegrdoffy = cp->imgareatly;
	}
	if (!cp->tilewidth) {
		cp->tilewidth = cp->refgrdwidth - cp->tilegrdoffx;
	}
	if (!cp->tileheight) {
		cp->tileheight = cp->refgrdheight - cp->tilegrdoffy;
	}

	if (cp->numcmpts == 3) {
		mctvalid = true;
		for (cmptno = 0; cmptno < jas_image_numcmpts(image); ++cmptno) {
			if (jas_image_cmptprec(image, cmptno) != jas_image_cmptprec(image, 0) ||
			  jas_image_cmptsgnd(image, cmptno) != jas_image_cmptsgnd(image, 0) ||
			  jas_image_cmptwidth(image, cmptno) != jas_image_cmptwidth(image, 0) ||
			  jas_image_cmptheight(image, cmptno) != jas_image_cmptheight(image, 0)) {
				mctvalid = false;
			}
		}
	} else {
		mctvalid = false;
	}
	if (mctvalid && enablemct && jas_clrspc_fam(jas_image_clrspc(image)) != JAS_CLRSPC_FAM_RGB) {
		jas_eprintf("warning: color space apparently not RGB\n");
	}
	if (mctvalid && enablemct && jas_clrspc_fam(jas_image_clrspc(image)) == JAS_CLRSPC_FAM_RGB) {
		tcp->mctid = (tcp->intmode) ? (JPC_MCT_RCT) : (JPC_MCT_ICT);
	} else {
		tcp->mctid = JPC_MCT_NONE;
	}
	tccp->qmfbid = (tcp->intmode) ? (JPC_COX_RFT) : (JPC_COX_INS);

	for (rlvlno = 0; rlvlno < tccp->maxrlvls; ++rlvlno) {
		tccp->prcwidthexpns[rlvlno] = prcwidthexpn;
		tccp->prcheightexpns[rlvlno] = prcheightexpn;
	}
	if (prcwidthexpn != 15 || prcheightexpn != 15) {
		tccp->csty |= JPC_COX_PRT;
	}

	/* Ensure that the tile width and height is valid. */
	if (!cp->tilewidth) {
		jas_eprintf("invalid tile width %lu\n", (unsigned long)
		  cp->tilewidth);
		goto error;
	}
	if (!cp->tileheight) {
		jas_eprintf("invalid tile height %lu\n", (unsigned long)
		  cp->tileheight);
		goto error;
	}

	/* Ensure that the tile grid offset is valid. */
	if (cp->tilegrdoffx > cp->imgareatlx ||
	  cp->tilegrdoffy > cp->imgareatly ||
	  cp->tilegrdoffx + cp->tilewidth < cp->imgareatlx ||
	  cp->tilegrdoffy + cp->tileheight < cp->imgareatly) {
		jas_eprintf("invalid tile grid offset (%lu, %lu)\n",
		  (unsigned long) cp->tilegrdoffx, (unsigned long)
		  cp->tilegrdoffy);
		goto error;
	}

	cp->numhtiles = JPC_CEILDIV(cp->refgrdwidth - cp->tilegrdoffx,
	  cp->tilewidth);
	cp->numvtiles = JPC_CEILDIV(cp->refgrdheight - cp->tilegrdoffy,
	  cp->tileheight);
	cp->numtiles = cp->numhtiles * cp->numvtiles;

	if (ilyrrates && numilyrrates > 0) {
		tcp->numlyrs = numilyrrates + 1;
		if (!(tcp->ilyrrates = jas_alloc2((tcp->numlyrs - 1),
		  sizeof(jpc_fix_t)))) {
			goto error;
		}
		for (i = 0; i < JAS_CAST(int, tcp->numlyrs - 1); ++i) {
			tcp->ilyrrates[i] = jpc_dbltofix(ilyrrates[i]);
		}
	}

	/* Ensure that the integer mode is used in the case of lossless
	  coding. */
	if (cp->totalsize == UINT_FAST32_MAX && (!cp->tcp.intmode)) {
		jas_eprintf("cannot use real mode for lossless coding\n");
		goto error;
	}

	/* Ensure that the precinct width is valid. */
	if (prcwidthexpn > 15) {
		jas_eprintf("invalid precinct width\n");
		goto error;
	}

	/* Ensure that the precinct height is valid. */
	if (prcheightexpn > 15) {
		jas_eprintf("invalid precinct height\n");
		goto error;
	}

	/* Ensure that the code block width is valid. */
	if (cp->tccp.cblkwidthexpn < 2 || cp->tccp.cblkwidthexpn > 12) {
		jas_eprintf("invalid code block width %d\n",
		  JPC_POW2(cp->tccp.cblkwidthexpn));
		goto error;
	}

	/* Ensure that the code block height is valid. */
	if (cp->tccp.cblkheightexpn < 2 || cp->tccp.cblkheightexpn > 12) {
		jas_eprintf("invalid code block height %d\n",
		  JPC_POW2(cp->tccp.cblkheightexpn));
		goto error;
	}

	/* Ensure that the code block size is not too large. */
	if (cp->tccp.cblkwidthexpn + cp->tccp.cblkheightexpn > 12) {
		jas_eprintf("code block size too large\n");
		goto error;
	}

	/* Ensure that the number of layers is valid. */
	if (cp->tcp.numlyrs > 16384) {
		jas_eprintf("too many layers\n");
		goto error;
	}

	/* There must be at least one resolution level. */
	if (cp->tccp.maxrlvls < 1) {
		jas_eprintf("must be at least one resolution level\n");
		goto error;
	}

	/* Ensure that the number of guard bits is valid. */
	if (cp->tccp.numgbits > 8) {
		jas_eprintf("invalid number of guard bits\n");
		goto error;
	}

	/* Ensure that the rate is within the legal range. */
	if (cp->totalsize != UINT_FAST32_MAX && cp->totalsize > cp->rawsize) {
		jas_eprintf("warning: specified rate is unreasonably large (%lu > %lu)\n", (unsigned long) cp->totalsize, (unsigned long) cp->rawsize);
	}

	/* Ensure that the intermediate layer rates are valid. */
	if (tcp->numlyrs > 1) {
		/* The intermediate layers rates must increase monotonically. */
		for (lyrno = 0; lyrno + 2 < tcp->numlyrs; ++lyrno) {
			if (tcp->ilyrrates[lyrno] >= tcp->ilyrrates[lyrno + 1]) {
				jas_eprintf("intermediate layer rates must increase monotonically\n");
				goto error;
			}
		}
		/* The intermediate layer rates must be less than the overall rate. */
		if (cp->totalsize != UINT_FAST32_MAX) {
			for (lyrno = 0; lyrno < tcp->numlyrs - 1; ++lyrno) {
				if (jpc_fixtodbl(tcp->ilyrrates[lyrno]) > ((double) cp->totalsize)
				  / cp->rawsize) {
					jas_eprintf("warning: intermediate layer rates must be less than overall rate\n");
					goto error;
				}
			}
		}
	}

	if (ilyrrates) {
		jas_free(ilyrrates);
	}

	return cp;

error:

	if (ilyrrates) {
		jas_free(ilyrrates);
	}
	if (tvp) {
		jas_tvparser_destroy(tvp);
	}
	if (cp) {
		jpc_enc_cp_destroy(cp);
	}
	return 0;
}


PHP_FUNCTION(xsl_xsltprocessor_transform_to_xml)
{
	zval *id, *docp = NULL;
	xmlDoc *newdocp;
	xsltStylesheetPtr sheetp;
	int ret;
	xmlChar *doc_txt_ptr;
	int doc_txt_len;
	xsl_object *intern;

	id = getThis();
	intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC);
	sheetp = (xsltStylesheetPtr) intern->ptr;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o", &docp) == FAILURE) {
		RETURN_FALSE;
	}

	newdocp = php_xsl_apply_stylesheet(id, intern, sheetp, docp TSRMLS_CC);

	ret = -1;
	if (newdocp) {
		ret = xsltSaveResultToString(&doc_txt_ptr, &doc_txt_len, newdocp, sheetp);
		if (doc_txt_ptr && doc_txt_len) {
			RETVAL_STRINGL(doc_txt_ptr, doc_txt_len, 1);
			xmlFree(doc_txt_ptr);
		}
		xmlFreeDoc(newdocp);
	}

	if (ret < 0) {
		RETURN_FALSE;
	}
}

Fix for bug #69782


PHP_FUNCTION(xsl_xsltprocessor_import_stylesheet)
{
	zval *id, *docp = NULL;
	xmlDoc *doc = NULL, *newdoc = NULL;
	xsltStylesheetPtr sheetp, oldsheetp;
	xsl_object *intern;
	int prevSubstValue, prevExtDtdValue, clone_docu = 0;
	xmlNode *nodep = NULL;
	zend_object_handlers *std_hnd;
	zval *cloneDocu, *member;

	if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oo", &id, xsl_xsltprocessor_class_entry, &docp) == FAILURE) {
		RETURN_FALSE;
	}

	nodep = php_libxml_import_node(docp TSRMLS_CC);

	if (nodep) {
		doc = nodep->doc;
	}
	if (doc == NULL) {
		php_error(E_WARNING, "Invalid Document");
		RETURN_FALSE;
	}

	/* libxslt uses _private, so we must copy the imported
	stylesheet document otherwise the node proxies will be a mess */
	newdoc = xmlCopyDoc(doc, 1);
	xmlNodeSetBase((xmlNodePtr) newdoc, (xmlChar *)doc->URL);
	prevSubstValue = xmlSubstituteEntitiesDefault(1);
	prevExtDtdValue = xmlLoadExtDtdDefaultValue;
	xmlLoadExtDtdDefaultValue = XML_DETECT_IDS | XML_COMPLETE_ATTRS;

	sheetp = xsltParseStylesheetDoc(newdoc);
	xmlSubstituteEntitiesDefault(prevSubstValue);
	xmlLoadExtDtdDefaultValue = prevExtDtdValue;

	if (!sheetp) {
		xmlFreeDoc(newdoc);
		RETURN_FALSE;
	}

	intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC);

	std_hnd = zend_get_std_object_handlers();
	MAKE_STD_ZVAL(member);
	ZVAL_STRING(member, "cloneDocument", 0);
	cloneDocu = std_hnd->read_property(id, member, BP_VAR_IS, NULL TSRMLS_CC);
	if (Z_TYPE_P(cloneDocu) != IS_NULL) {
		convert_to_long(cloneDocu);
		clone_docu = Z_LVAL_P(cloneDocu);
	}
	efree(member);
	if (clone_docu == 0) {
		/* check if the stylesheet is using xsl:key, if yes, we have to clone the document _always_ before a transformation */
		nodep = xmlDocGetRootElement(sheetp->doc);
		if (nodep && (nodep = nodep->children)) {
			while (nodep) {
				if (nodep->type == XML_ELEMENT_NODE && xmlStrEqual(nodep->name, "key") && xmlStrEqual(nodep->ns->href, XSLT_NAMESPACE)) {
					intern->hasKeys = 1;
					break;
				}
				nodep = nodep->next;
			}
		}
	} else {
		intern->hasKeys = clone_docu;
	}

	if ((oldsheetp = (xsltStylesheetPtr)intern->ptr)) {
		/* free wrapper */
		if (((xsltStylesheetPtr) intern->ptr)->_private != NULL) {
			((xsltStylesheetPtr) intern->ptr)->_private = NULL;
		}
		xsltFreeStylesheet((xsltStylesheetPtr) intern->ptr);
		intern->ptr = NULL;
	}

	php_xsl_set_object(id, sheetp TSRMLS_CC);
	RETVAL_TRUE;
}

Fix for bug #69782


PHP_FUNCTION(xsl_xsltprocessor_transform_to_uri)
{
	zval *id, *docp = NULL;
	xmlDoc *newdocp;
	xsltStylesheetPtr sheetp;
	int ret, uri_len;
	char *uri;
	xsl_object *intern;

	id = getThis();
	intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC);
	sheetp = (xsltStylesheetPtr) intern->ptr;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "op", &docp, &uri, &uri_len) == FAILURE) {
		RETURN_FALSE;
	}

	newdocp = php_xsl_apply_stylesheet(id, intern, sheetp, docp TSRMLS_CC);

	ret = -1;
	if (newdocp) {
		ret = xsltSaveResultToFilename(uri, newdocp, sheetp, 0);
		xmlFreeDoc(newdocp);
	}

	RETVAL_LONG(ret);
}

Fix for bug #69782


static char **php_xsl_xslt_make_params(HashTable *parht, int xpath_params TSRMLS_DC)
{

	int parsize;
	zval **value;
	char *xpath_expr, *string_key = NULL;
	ulong num_key;
	char **params = NULL;
	int i = 0;

	parsize = (2 * zend_hash_num_elements(parht) + 1) * sizeof(char *);
	params = (char **)safe_emalloc((2 * zend_hash_num_elements(parht) + 1), sizeof(char *), 0);
	memset((char *)params, 0, parsize);

	for (zend_hash_internal_pointer_reset(parht);
		zend_hash_get_current_data(parht, (void **)&value) == SUCCESS;
		zend_hash_move_forward(parht)) {

		if (zend_hash_get_current_key(parht, &string_key, &num_key, 1) != HASH_KEY_IS_STRING) {
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid argument or parameter array");
			efree(params);
			return NULL;
		} else {
			if (Z_TYPE_PP(value) != IS_STRING) {
				SEPARATE_ZVAL(value);
				convert_to_string(*value);
			}

			if (!xpath_params) {
				xpath_expr = php_xsl_xslt_string_to_xpathexpr(Z_STRVAL_PP(value) TSRMLS_CC);
			} else {
				xpath_expr = estrndup(Z_STRVAL_PP(value), Z_STRLEN_PP(value));
			}
			if (xpath_expr) {
				params[i++] = string_key;
				params[i++] = xpath_expr;
			} else {
				efree(string_key);
			}
		}
	}

	params[i++] = NULL;

	return params;
}

Fix for bug #69782


static void xsl_ext_function_php(xmlXPathParserContextPtr ctxt, int nargs, int type) /* {{{ */
{
	xsltTransformContextPtr tctxt;
	zval **args;
	zval *retval;
	int result, i, ret;
	int error = 0;
	zend_fcall_info fci;
	zval handler;
	xmlXPathObjectPtr obj;
	char *str;
	char *callable = NULL;
	xsl_object *intern;

	TSRMLS_FETCH();

	if (! zend_is_executing(TSRMLS_C)) {
		xsltGenericError(xsltGenericErrorContext,
		"xsltExtFunctionTest: Function called from outside of PHP\n");
		error = 1;
	} else {
		tctxt = xsltXPathGetTransformContext(ctxt);
		if (tctxt == NULL) {
			xsltGenericError(xsltGenericErrorContext,
			"xsltExtFunctionTest: failed to get the transformation context\n");
			error = 1;
		} else {
			intern = (xsl_object *) tctxt->_private;
			if (intern == NULL) {
				xsltGenericError(xsltGenericErrorContext,
				"xsltExtFunctionTest: failed to get the internal object\n");
				error = 1;
			}
			else if (intern->registerPhpFunctions == 0) {
				xsltGenericError(xsltGenericErrorContext,
				"xsltExtFunctionTest: PHP Object did not register PHP functions\n");
				error = 1;
			}
		}
	}

	if (error == 1) {
		for (i = nargs - 1; i >= 0; i--) {
			obj = valuePop(ctxt);
			if (obj) {
				xmlXPathFreeObject(obj);
			}
		}
		return;
	}

	fci.param_count = nargs - 1;
	if (fci.param_count > 0) {
		fci.params = safe_emalloc(fci.param_count, sizeof(zval**), 0);
		args = safe_emalloc(fci.param_count, sizeof(zval *), 0);
	}
	/* Reverse order to pop values off ctxt stack */
	for (i = nargs - 2; i >= 0; i--) {
		obj = valuePop(ctxt);
		MAKE_STD_ZVAL(args[i]);
		switch (obj->type) {
			case XPATH_STRING:
				ZVAL_STRING(args[i],  obj->stringval, 1);
				break;
			case XPATH_BOOLEAN:
				ZVAL_BOOL(args[i],  obj->boolval);
				break;
			case XPATH_NUMBER:
				ZVAL_DOUBLE(args[i], obj->floatval);
				break;
			case XPATH_NODESET:
				if (type == 1) {
					str = xmlXPathCastToString(obj);
					ZVAL_STRING(args[i], str, 1);
					xmlFree(str);
				} else if (type == 2) {
					int j;
					dom_object *domintern = (dom_object *)intern->doc;
					array_init(args[i]);
					if (obj->nodesetval && obj->nodesetval->nodeNr > 0) {
						for (j = 0; j < obj->nodesetval->nodeNr; j++) {
							xmlNodePtr node = obj->nodesetval->nodeTab[j];
							zval *child;
							MAKE_STD_ZVAL(child);
							/* not sure, if we need this... it's copied from xpath.c */
							if (node->type == XML_NAMESPACE_DECL) {
								xmlNsPtr curns;
								xmlNodePtr nsparent;

								nsparent = node->_private;
								curns = xmlNewNs(NULL, node->name, NULL);
								if (node->children) {
									curns->prefix = xmlStrdup((char *) node->children);
								}
								if (node->children) {
									node = xmlNewDocNode(node->doc, NULL, (char *) node->children, node->name);
								} else {
									node = xmlNewDocNode(node->doc, NULL, "xmlns", node->name);
								}
								node->type = XML_NAMESPACE_DECL;
								node->parent = nsparent;
								node->ns = curns;
							} else {
								node = xmlDocCopyNodeList(domintern->document->ptr, node);
							}

							child = php_dom_create_object(node, &ret, child, domintern TSRMLS_CC);
							add_next_index_zval(args[i], child);
						}
					}
				}
				break;
			default:
				str = xmlXPathCastToString(obj);
				ZVAL_STRING(args[i], str, 1);
				xmlFree(str);
		}
		xmlXPathFreeObject(obj);
		fci.params[i] = &args[i];
	}

	fci.size = sizeof(fci);
	fci.function_table = EG(function_table);

	obj = valuePop(ctxt);
	if (obj == NULL || obj->stringval == NULL) {
		if (obj) {
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Handler name must be a string");
			xmlXPathFreeObject(obj);
		}
		valuePush(ctxt, xmlXPathNewString(""));
		if (fci.param_count > 0) {
			for (i = 0; i < nargs - 1; i++) {
				zval_ptr_dtor(&args[i]);
			}
			efree(args);
			efree(fci.params);
		}
		return;
	}
	INIT_PZVAL(&handler);
	ZVAL_STRING(&handler, obj->stringval, 1);
	xmlXPathFreeObject(obj);

	fci.function_name = &handler;
	fci.symbol_table = NULL;
	fci.object_ptr = NULL;
	fci.retval_ptr_ptr = &retval;
	fci.no_separation = 0;
	/*fci.function_handler_cache = &function_ptr;*/
	if (!zend_make_callable(&handler, &callable TSRMLS_CC)) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to call handler %s()", callable);
		valuePush(ctxt, xmlXPathNewString(""));
	} else if ( intern->registerPhpFunctions == 2 && zend_hash_exists(intern->registered_phpfunctions, callable, strlen(callable) + 1) == 0) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Not allowed to call handler '%s()'", callable);
		/* Push an empty string, so that we at least have an xslt result... */
		valuePush(ctxt, xmlXPathNewString(""));
	} else {
		result = zend_call_function(&fci, NULL TSRMLS_CC);
		if (result == FAILURE) {
			if (Z_TYPE(handler) == IS_STRING) {
				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to call handler %s()", Z_STRVAL_P(&handler));
				valuePush(ctxt, xmlXPathNewString(""));
			}
		/* retval is == NULL, when an exception occurred, don't report anything, because PHP itself will handle that */
		} else if (retval == NULL) {
		} else {
			if (retval->type == IS_OBJECT && instanceof_function( Z_OBJCE_P(retval), dom_node_class_entry TSRMLS_CC)) {
				xmlNode *nodep;
				dom_object *obj;
				if (intern->node_list == NULL) {
					ALLOC_HASHTABLE(intern->node_list);
					zend_hash_init(intern->node_list, 0, NULL, ZVAL_PTR_DTOR, 0);
				}
				zval_add_ref(&retval);
				zend_hash_next_index_insert(intern->node_list, &retval, sizeof(zval *), NULL);
				obj = (dom_object *)zend_object_store_get_object(retval TSRMLS_CC);
				nodep = dom_object_get_node(obj);
				valuePush(ctxt, xmlXPathNewNodeSet(nodep));
			} else if (retval->type == IS_BOOL) {
				valuePush(ctxt, xmlXPathNewBoolean(retval->value.lval));
			} else if (retval->type == IS_OBJECT) {
				php_error_docref(NULL TSRMLS_CC, E_WARNING, "A PHP Object cannot be converted to a XPath-string");
				valuePush(ctxt, xmlXPathNewString(""));
			} else {
				convert_to_string_ex(&retval);
				valuePush(ctxt, xmlXPathNewString( Z_STRVAL_P(retval)));
			}
			zval_ptr_dtor(&retval);
		}
	}
	efree(callable);
	zval_dtor(&handler);
	if (fci.param_count > 0) {
		for (i = 0; i < nargs - 1; i++) {
			zval_ptr_dtor(&args[i]);
		}
		efree(args);
		efree(fci.params);
	}
}

Fix for bug #69782


static xmlDocPtr php_xsl_apply_stylesheet(zval *id, xsl_object *intern, xsltStylesheetPtr style, zval *docp TSRMLS_DC) /* {{{ */
{
	xmlDocPtr newdocp = NULL;
	xmlDocPtr doc = NULL;
	xmlNodePtr node = NULL;
	xsltTransformContextPtr ctxt;
	php_libxml_node_object *object;
	char **params = NULL;
	int clone;
	zval *doXInclude, *member;
	zend_object_handlers *std_hnd;
	FILE *f;
	int secPrefsError = 0;
	int secPrefsValue, secPrefsIni;
	xsltSecurityPrefsPtr secPrefs = NULL;

	node = php_libxml_import_node(docp TSRMLS_CC);

	if (node) {
		doc = node->doc;
	}
	if (doc == NULL) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid Document");
		return NULL;
	}

	if (style == NULL) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "No stylesheet associated to this object");
		return NULL;
	}

	if (intern->profiling) {
		if (php_check_open_basedir(intern->profiling TSRMLS_CC)) {
			f = NULL;
		} else {
			f = VCWD_FOPEN(intern->profiling, "w");
		}
	} else {
		f = NULL;
	}

	if (intern->parameter) {
		params = php_xsl_xslt_make_params(intern->parameter, 0 TSRMLS_CC);
	}

	intern->doc = emalloc(sizeof(php_libxml_node_object));
	memset(intern->doc, 0, sizeof(php_libxml_node_object));

	if (intern->hasKeys == 1) {
		doc = xmlCopyDoc(doc, 1);
	} else {
		object = (php_libxml_node_object *)zend_object_store_get_object(docp TSRMLS_CC);
		intern->doc->document = object->document;
	}

	php_libxml_increment_doc_ref(intern->doc, doc TSRMLS_CC);

	ctxt = xsltNewTransformContext(style, doc);
	ctxt->_private = (void *) intern;

	std_hnd = zend_get_std_object_handlers();

	MAKE_STD_ZVAL(member);
	ZVAL_STRING(member, "doXInclude", 0);
	doXInclude = std_hnd->read_property(id, member, BP_VAR_IS, NULL TSRMLS_CC);
	if (Z_TYPE_P(doXInclude) != IS_NULL) {
		convert_to_long(doXInclude);
		ctxt->xinclude = Z_LVAL_P(doXInclude);
	}
	efree(member);

	secPrefsValue = intern->securityPrefs;

	/* This whole if block can be removed, when we remove the xsl.security_prefs php.ini option in PHP 6+ */
	secPrefsIni= INI_INT("xsl.security_prefs");
	/* if secPrefsIni has the same value as secPrefsValue, all is fine */
	if (secPrefsIni != secPrefsValue) {
		if (secPrefsIni != XSL_SECPREF_DEFAULT) {
			/* if the ini value is not set to the default, throw an E_DEPRECATED warning */
			php_error_docref(NULL TSRMLS_CC, E_DEPRECATED, "The xsl.security_prefs php.ini option is deprecated; use XsltProcessor->setSecurityPrefs() instead");
			if (intern->securityPrefsSet == 0) {
				/* if securityPrefs were not set through the setSecurityPrefs method, take the ini setting */
				secPrefsValue = secPrefsIni;
			} else {
				/* else throw a notice, that the ini setting was not used */
				php_error_docref(NULL TSRMLS_CC, E_NOTICE, "The xsl.security_prefs php.ini was not used, since the  XsltProcessor->setSecurityPrefs() method was used");
			}
		}
	}

	/* if securityPrefs is set to NONE, we don't have to do any checks, but otherwise... */
	if (secPrefsValue != XSL_SECPREF_NONE) {
		secPrefs = xsltNewSecurityPrefs();
		if (secPrefsValue & XSL_SECPREF_READ_FILE ) {
			if (0 != xsltSetSecurityPrefs(secPrefs, XSLT_SECPREF_READ_FILE, xsltSecurityForbid)) {
				secPrefsError = 1;
			}
		}
		if (secPrefsValue & XSL_SECPREF_WRITE_FILE ) {
			if (0 != xsltSetSecurityPrefs(secPrefs, XSLT_SECPREF_WRITE_FILE, xsltSecurityForbid)) {
				secPrefsError = 1;
			}
		}
		if (secPrefsValue & XSL_SECPREF_CREATE_DIRECTORY ) {
			if (0 != xsltSetSecurityPrefs(secPrefs, XSLT_SECPREF_CREATE_DIRECTORY, xsltSecurityForbid)) {
				secPrefsError = 1;
			}
		}
		if (secPrefsValue & XSL_SECPREF_READ_NETWORK) {
			if (0 != xsltSetSecurityPrefs(secPrefs, XSLT_SECPREF_READ_NETWORK, xsltSecurityForbid)) {
				secPrefsError = 1;
			}
		}
		if (secPrefsValue & XSL_SECPREF_WRITE_NETWORK) {
			if (0 != xsltSetSecurityPrefs(secPrefs, XSLT_SECPREF_WRITE_NETWORK, xsltSecurityForbid)) {
				secPrefsError = 1;
			}
		}

		if (0 != xsltSetCtxtSecurityPrefs(secPrefs, ctxt)) {
			secPrefsError = 1;
		}
	}

	if (secPrefsError == 1) {
		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Can't set libxslt security properties, not doing transformation for security reasons");
	} else {
		newdocp = xsltApplyStylesheetUser(style, doc, (const char**) params,  NULL, f, ctxt);
	}
	if (f) {
		fclose(f);
	}

	xsltFreeTransformContext(ctxt);
	if (secPrefs) {
		xsltFreeSecurityPrefs(secPrefs);
	}

	if (intern->node_list != NULL) {
		zend_hash_destroy(intern->node_list);
		FREE_HASHTABLE(intern->node_list);
		intern->node_list = NULL;
	}

	php_libxml_decrement_doc_ref(intern->doc TSRMLS_CC);
	efree(intern->doc);
	intern->doc = NULL;

	if (params) {
		clone = 0;
		while(params[clone]) {
			efree(params[clone++]);
		}
		efree(params);
	}

	return newdocp;

}

Fix for bug #69782


PHP_FUNCTION(xsl_xsltprocessor_set_security_prefs)
{
	zval *id;
	xsl_object *intern;
	long securityPrefs, oldSecurityPrefs;

	DOM_GET_THIS(id);
 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &securityPrefs) == FAILURE) {
		return;
	}
	intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC);
	oldSecurityPrefs = intern->securityPrefs;
	intern->securityPrefs = securityPrefs;
	/* set this to 1 so that we know, it was set through this method. Can be removed, when we remove the ini setting */
	intern->securityPrefsSet = 1;
	RETURN_LONG(oldSecurityPrefs);
}

Fix for bug #69782


PHP_FUNCTION(xsl_xsltprocessor_remove_parameter)
{
	zval *id;
	int name_len = 0, namespace_len = 0;
	char *name, *namespace;
	xsl_object *intern;

	DOM_GET_THIS(id);

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &namespace, &namespace_len, &name, &name_len) == FAILURE) {
		RETURN_FALSE;
	}
	intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC);
	if ( zend_hash_del(intern->parameter, name, name_len + 1) == SUCCESS) {
		RETURN_TRUE;
	} else {
		RETURN_FALSE;
	}
}

Fix for bug #69782


PHP_FUNCTION(xsl_xsltprocessor_register_php_functions)
{
	zval *id;
	xsl_object *intern;
	zval *array_value, **entry, *new_string;
	int  name_len = 0;
	char *name;

	DOM_GET_THIS(id);

	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "a",  &array_value) == SUCCESS) {
		intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC);
		zend_hash_internal_pointer_reset(Z_ARRVAL_P(array_value));

		while (zend_hash_get_current_data(Z_ARRVAL_P(array_value), (void **)&entry) == SUCCESS) {
			SEPARATE_ZVAL(entry);
			convert_to_string_ex(entry);

			MAKE_STD_ZVAL(new_string);
			ZVAL_LONG(new_string,1);

			zend_hash_update(intern->registered_phpfunctions, Z_STRVAL_PP(entry), Z_STRLEN_PP(entry) + 1, &new_string, sizeof(zval*), NULL);
			zend_hash_move_forward(Z_ARRVAL_P(array_value));
		}
		intern->registerPhpFunctions = 2;

	} else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "s",  &name, &name_len) == SUCCESS) {
		intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC);

		MAKE_STD_ZVAL(new_string);
		ZVAL_LONG(new_string,1);
		zend_hash_update(intern->registered_phpfunctions, name, name_len + 1, &new_string, sizeof(zval*), NULL);
		intern->registerPhpFunctions = 2;

	} else {
		intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC);
		intern->registerPhpFunctions = 1;
	}

}

Fix for bug #69782


PHP_FUNCTION(xsl_xsltprocessor_set_parameter)
{

	zval *id;
	zval *array_value, **entry, *new_string;
	xsl_object *intern;
	char *string_key, *name, *value, *namespace;
	ulong idx;
	int string_key_len, namespace_len, name_len, value_len;
	DOM_GET_THIS(id);

	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "sa", &namespace, &namespace_len, &array_value) == SUCCESS) {
		intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC);
		zend_hash_internal_pointer_reset(Z_ARRVAL_P(array_value));

		while (zend_hash_get_current_data(Z_ARRVAL_P(array_value), (void **)&entry) == SUCCESS) {
			SEPARATE_ZVAL(entry);
			convert_to_string_ex(entry);

			if (zend_hash_get_current_key_ex(Z_ARRVAL_P(array_value), &string_key, &string_key_len, &idx, 0, NULL) != HASH_KEY_IS_STRING) {
				php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid parameter array");
				RETURN_FALSE;
			}

			ALLOC_ZVAL(new_string);
			Z_ADDREF_PP(entry);
			COPY_PZVAL_TO_ZVAL(*new_string, *entry);

			zend_hash_update(intern->parameter, string_key, string_key_len, &new_string, sizeof(zval*), NULL);
			zend_hash_move_forward(Z_ARRVAL_P(array_value));
		}
		RETURN_TRUE;

	} else if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "sss", &namespace, &namespace_len, &name, &name_len, &value, &value_len) == SUCCESS) {

		intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC);

		MAKE_STD_ZVAL(new_string);
		ZVAL_STRING(new_string, value, 1);

		zend_hash_update(intern->parameter, name, name_len + 1, &new_string, sizeof(zval*), NULL);
		RETURN_TRUE;
	} else {
		WRONG_PARAM_COUNT;
	}

}

Fix for bug #69782


PHP_FUNCTION(xsl_xsltprocessor_get_parameter)
{
	zval *id;
	int name_len = 0, namespace_len = 0;
	char *name, *namespace;
	zval **value;
	xsl_object *intern;

	DOM_GET_THIS(id);

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &namespace, &namespace_len, &name, &name_len) == FAILURE) {
		RETURN_FALSE;
	}
	intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC);
	if ( zend_hash_find(intern->parameter, name, name_len + 1,  (void**) &value) == SUCCESS) {
		convert_to_string_ex(value);
		RETVAL_STRING(Z_STRVAL_PP(value),1);
	} else {
		RETURN_FALSE;
	}
}

Fix for bug #69782


	PHP_FALIAS(getSecurityPrefs, xsl_xsltprocessor_get_security_prefs, arginfo_xsl_xsltprocessor_get_security_prefs)
	{NULL, NULL, NULL}

Fix for bug #69782


PHP_FUNCTION(xsl_xsltprocessor_transform_to_doc)
{
	zval *id, *docp = NULL;
	xmlDoc *newdocp;
	xsltStylesheetPtr sheetp;
	int ret, ret_class_len=0;
	char *ret_class = NULL;
	xsl_object *intern;

	id = getThis();
	intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC);
	sheetp = (xsltStylesheetPtr) intern->ptr;

	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o|s!", &docp, &ret_class, &ret_class_len) == FAILURE) {
		RETURN_FALSE;
	}

	newdocp = php_xsl_apply_stylesheet(id, intern, sheetp, docp TSRMLS_CC);

	if (newdocp) {
		if (ret_class) {
			int found;
			char *curclass_name;
			zend_class_entry *curce, **ce;
			php_libxml_node_object *interndoc;

			curce = Z_OBJCE_P(docp);
			curclass_name = curce->name;
			while (curce->parent != NULL) {
				curce = curce->parent;
			}

			found = zend_lookup_class(ret_class, ret_class_len, &ce TSRMLS_CC);
			if ((found != SUCCESS) || !instanceof_function(*ce, curce TSRMLS_CC)) {
				xmlFreeDoc(newdocp);
				php_error_docref(NULL TSRMLS_CC, E_WARNING,
					"Expecting class compatible with %s, '%s' given", curclass_name, ret_class);
				RETURN_FALSE;
			}

			object_init_ex(return_value, *ce);

			interndoc = (php_libxml_node_object *)zend_objects_get_address(return_value TSRMLS_CC);
			php_libxml_increment_doc_ref(interndoc, newdocp TSRMLS_CC);
			php_libxml_increment_node_ptr(interndoc, (xmlNodePtr)newdocp, (void *)interndoc TSRMLS_CC);
		} else {
			DOM_RET_OBJ((xmlNodePtr) newdocp, &ret, NULL);
		}
	} else {
		RETURN_FALSE;
	}

}

Fix for bug #69782


PHP_FUNCTION(xsl_xsltprocessor_get_security_prefs)
{
	zval *id;
	xsl_object *intern;

	DOM_GET_THIS(id);
	if (zend_parse_parameters_ex(ZEND_PARSE_PARAMS_QUIET, ZEND_NUM_ARGS() TSRMLS_CC, "") == SUCCESS) {
		intern = (xsl_object *)zend_object_store_get_object(id TSRMLS_CC);
		RETURN_LONG(intern->securityPrefs);
	} else {
		WRONG_PARAM_COUNT;
	}
}

Fix for bug #69782


static char *php_xsl_xslt_string_to_xpathexpr(const char *str TSRMLS_DC)
{
	const xmlChar *string = (const xmlChar *)str;

	xmlChar *value;
	int str_len;

	str_len = xmlStrlen(string) + 3;

	if (xmlStrchr(string, '"')) {
		if (xmlStrchr(string, '\'')) {
			php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot create XPath expression (string contains both quote and double-quotes)");
			return NULL;
		}
		value = (xmlChar*) safe_emalloc (str_len, sizeof(xmlChar), 0);
		snprintf(value, str_len, "'%s'", string);
	} else {
		value = (xmlChar*) safe_emalloc (str_len, sizeof(xmlChar), 0);
		snprintf(value, str_len, "\"%s\"", string);
	}
	return (char *) value;
}

Fix for bug #69782


ex_retab(exarg_T *eap)
{
    linenr_T	lnum;
    int		got_tab = FALSE;
    long	num_spaces = 0;
    long	num_tabs;
    long	len;
    long	col;
    long	vcol;
    long	start_col = 0;		// For start of white-space string
    long	start_vcol = 0;		// For start of white-space string
    long	old_len;
    long	new_len;
    char_u	*ptr;
    char_u	*new_line = (char_u *)1; // init to non-NULL
    int		did_undo;		// called u_save for current line
#ifdef FEAT_VARTABS
    int		*new_vts_array = NULL;
    char_u	*new_ts_str;		// string value of tab argument
#else
    int		temp;
    int		new_ts;
#endif
    int		save_list;
    linenr_T	first_line = 0;		// first changed line
    linenr_T	last_line = 0;		// last changed line

    save_list = curwin->w_p_list;
    curwin->w_p_list = 0;	    // don't want list mode here

#ifdef FEAT_VARTABS
    new_ts_str = eap->arg;
    if (tabstop_set(eap->arg, &new_vts_array) == FAIL)
	return;
    while (vim_isdigit(*(eap->arg)) || *(eap->arg) == ',')
	++(eap->arg);

    // This ensures that either new_vts_array and new_ts_str are freshly
    // allocated, or new_vts_array points to an existing array and new_ts_str
    // is null.
    if (new_vts_array == NULL)
    {
	new_vts_array = curbuf->b_p_vts_array;
	new_ts_str = NULL;
    }
    else
	new_ts_str = vim_strnsave(new_ts_str, eap->arg - new_ts_str);
#else
    ptr = eap->arg;
    new_ts = getdigits(&ptr);
    if (new_ts < 0 && *eap->arg == '-')
    {
	emsg(_(e_argument_must_be_positive));
	return;
    }
    if (new_ts < 0 || new_ts > TABSTOP_MAX)
    {
	semsg(_(e_invalid_argument_str), eap->arg);
	return;
    }
    if (new_ts == 0)
	new_ts = curbuf->b_p_ts;
#endif
    for (lnum = eap->line1; !got_int && lnum <= eap->line2; ++lnum)
    {
	ptr = ml_get(lnum);
	col = 0;
	vcol = 0;
	did_undo = FALSE;
	for (;;)
	{
	    if (VIM_ISWHITE(ptr[col]))
	    {
		if (!got_tab && num_spaces == 0)
		{
		    // First consecutive white-space
		    start_vcol = vcol;
		    start_col = col;
		}
		if (ptr[col] == ' ')
		    num_spaces++;
		else
		    got_tab = TRUE;
	    }
	    else
	    {
		if (got_tab || (eap->forceit && num_spaces > 1))
		{
		    // Retabulate this string of white-space

		    // len is virtual length of white string
		    len = num_spaces = vcol - start_vcol;
		    num_tabs = 0;
		    if (!curbuf->b_p_et)
		    {
#ifdef FEAT_VARTABS
			int t, s;

			tabstop_fromto(start_vcol, vcol,
					curbuf->b_p_ts, new_vts_array, &t, &s);
			num_tabs = t;
			num_spaces = s;
#else
			temp = new_ts - (start_vcol % new_ts);
			if (num_spaces >= temp)
			{
			    num_spaces -= temp;
			    num_tabs++;
			}
			num_tabs += num_spaces / new_ts;
			num_spaces -= (num_spaces / new_ts) * new_ts;
#endif
		    }
		    if (curbuf->b_p_et || got_tab ||
					(num_spaces + num_tabs < len))
		    {
			if (did_undo == FALSE)
			{
			    did_undo = TRUE;
			    if (u_save((linenr_T)(lnum - 1),
						(linenr_T)(lnum + 1)) == FAIL)
			    {
				new_line = NULL;	// flag out-of-memory
				break;
			    }
			}

			// len is actual number of white characters used
			len = num_spaces + num_tabs;
			old_len = (long)STRLEN(ptr);
			new_len = old_len - col + start_col + len + 1;
			if (new_len <= 0 || new_len >= MAXCOL)
			{
			    emsg(_(e_resulting_text_too_long));
			    break;
			}
			new_line = alloc(new_len);
			if (new_line == NULL)
			    break;
			if (start_col > 0)
			    mch_memmove(new_line, ptr, (size_t)start_col);
			mch_memmove(new_line + start_col + len,
				      ptr + col, (size_t)(old_len - col + 1));
			ptr = new_line + start_col;
			for (col = 0; col < len; col++)
			    ptr[col] = (col < num_tabs) ? '\t' : ' ';
			if (ml_replace(lnum, new_line, FALSE) == OK)
			    // "new_line" may have been copied
			    new_line = curbuf->b_ml.ml_line_ptr;
			if (first_line == 0)
			    first_line = lnum;
			last_line = lnum;
			ptr = new_line;
			col = start_col + len;
		    }
		}
		got_tab = FALSE;
		num_spaces = 0;
	    }
	    if (ptr[col] == NUL)
		break;
	    vcol += chartabsize(ptr + col, (colnr_T)vcol);
	    if (vcol >= MAXCOL)
	    {
		emsg(_(e_resulting_text_too_long));
		break;
	    }
	    if (has_mbyte)
		col += (*mb_ptr2len)(ptr + col);
	    else
		++col;
	}
	if (new_line == NULL)		    // out of memory
	    break;
	line_breakcheck();
    }
    if (got_int)
	emsg(_(e_interrupted));

#ifdef FEAT_VARTABS
    // If a single value was given then it can be considered equal to
    // either the value of 'tabstop' or the value of 'vartabstop'.
    if (tabstop_count(curbuf->b_p_vts_array) == 0
	&& tabstop_count(new_vts_array) == 1
	&& curbuf->b_p_ts == tabstop_first(new_vts_array))
	; // not changed
    else if (tabstop_count(curbuf->b_p_vts_array) > 0
        && tabstop_eq(curbuf->b_p_vts_array, new_vts_array))
	; // not changed
    else
	redraw_curbuf_later(NOT_VALID);
#else
    if (curbuf->b_p_ts != new_ts)
	redraw_curbuf_later(NOT_VALID);
#endif
    if (first_line != 0)
	changed_lines(first_line, 0, last_line + 1, 0L);

    curwin->w_p_list = save_list;	// restore 'list'

#ifdef FEAT_VARTABS
    if (new_ts_str != NULL)		// set the new tabstop
    {
	// If 'vartabstop' is in use or if the value given to retab has more
	// than one tabstop then update 'vartabstop'.
	int *old_vts_ary = curbuf->b_p_vts_array;

	if (tabstop_count(old_vts_ary) > 0 || tabstop_count(new_vts_array) > 1)
	{
	    set_string_option_direct((char_u *)"vts", -1, new_ts_str,
							OPT_FREE|OPT_LOCAL, 0);
	    curbuf->b_p_vts_array = new_vts_array;
	    vim_free(old_vts_ary);
	}
	else
	{
	    // 'vartabstop' wasn't in use and a single value was given to
	    // retab then update 'tabstop'.
	    curbuf->b_p_ts = tabstop_first(new_vts_array);
	    vim_free(new_vts_array);
	}
	vim_free(new_ts_str);
    }
#else
    curbuf->b_p_ts = new_ts;
#endif
    coladvance(curwin->w_curswant);

    u_clearline();
}

patch 8.2.4436: crash with weird 'vartabstop' value

Problem:    Crash with weird 'vartabstop' value.
Solution:   Check for running into the end of the line.


change_indent(
    int		type,
    int		amount,
    int		round,
    int		replaced,	// replaced character, put on replace stack
    int		call_changed_bytes)	// call changed_bytes()
{
    int		vcol;
    int		last_vcol;
    int		insstart_less;		// reduction for Insstart.col
    int		new_cursor_col;
    int		i;
    char_u	*ptr;
    int		save_p_list;
    int		start_col;
    colnr_T	vc;
    colnr_T	orig_col = 0;		// init for GCC
    char_u	*new_line, *orig_line = NULL;	// init for GCC

    // VREPLACE mode needs to know what the line was like before changing
    if (State & VREPLACE_FLAG)
    {
	orig_line = vim_strsave(ml_get_curline());  // Deal with NULL below
	orig_col = curwin->w_cursor.col;
    }

    // for the following tricks we don't want list mode
    save_p_list = curwin->w_p_list;
    curwin->w_p_list = FALSE;
    vc = getvcol_nolist(&curwin->w_cursor);
    vcol = vc;

    // For Replace mode we need to fix the replace stack later, which is only
    // possible when the cursor is in the indent.  Remember the number of
    // characters before the cursor if it's possible.
    start_col = curwin->w_cursor.col;

    // determine offset from first non-blank
    new_cursor_col = curwin->w_cursor.col;
    beginline(BL_WHITE);
    new_cursor_col -= curwin->w_cursor.col;

    insstart_less = curwin->w_cursor.col;

    // If the cursor is in the indent, compute how many screen columns the
    // cursor is to the left of the first non-blank.
    if (new_cursor_col < 0)
	vcol = get_indent() - vcol;

    if (new_cursor_col > 0)	    // can't fix replace stack
	start_col = -1;

    // Set the new indent.  The cursor will be put on the first non-blank.
    if (type == INDENT_SET)
	(void)set_indent(amount, call_changed_bytes ? SIN_CHANGED : 0);
    else
    {
	int	save_State = State;

	// Avoid being called recursively.
	if (State & VREPLACE_FLAG)
	    State = INSERT;
	shift_line(type == INDENT_DEC, round, 1, call_changed_bytes);
	State = save_State;
    }
    insstart_less -= curwin->w_cursor.col;

    // Try to put cursor on same character.
    // If the cursor is at or after the first non-blank in the line,
    // compute the cursor column relative to the column of the first
    // non-blank character.
    // If we are not in insert mode, leave the cursor on the first non-blank.
    // If the cursor is before the first non-blank, position it relative
    // to the first non-blank, counted in screen columns.
    if (new_cursor_col >= 0)
    {
	// When changing the indent while the cursor is touching it, reset
	// Insstart_col to 0.
	if (new_cursor_col == 0)
	    insstart_less = MAXCOL;
	new_cursor_col += curwin->w_cursor.col;
    }
    else if (!(State & INSERT))
	new_cursor_col = curwin->w_cursor.col;
    else
    {
	// Compute the screen column where the cursor should be.
	vcol = get_indent() - vcol;
	curwin->w_virtcol = (colnr_T)((vcol < 0) ? 0 : vcol);

	// Advance the cursor until we reach the right screen column.
	vcol = last_vcol = 0;
	new_cursor_col = -1;
	ptr = ml_get_curline();
	while (vcol <= (int)curwin->w_virtcol)
	{
	    last_vcol = vcol;
	    if (has_mbyte && new_cursor_col >= 0)
		new_cursor_col += (*mb_ptr2len)(ptr + new_cursor_col);
	    else
		++new_cursor_col;
	    if (ptr[new_cursor_col] == NUL)
		break;
	    vcol += lbr_chartabsize(ptr, ptr + new_cursor_col, (colnr_T)vcol);
	}
	vcol = last_vcol;

	// May need to insert spaces to be able to position the cursor on
	// the right screen column.
	if (vcol != (int)curwin->w_virtcol)
	{
	    curwin->w_cursor.col = (colnr_T)new_cursor_col;
	    i = (int)curwin->w_virtcol - vcol;
	    ptr = alloc(i + 1);
	    if (ptr != NULL)
	    {
		new_cursor_col += i;
		ptr[i] = NUL;
		while (--i >= 0)
		    ptr[i] = ' ';
		ins_str(ptr);
		vim_free(ptr);
	    }
	}

	// When changing the indent while the cursor is in it, reset
	// Insstart_col to 0.
	insstart_less = MAXCOL;
    }

    curwin->w_p_list = save_p_list;

    if (new_cursor_col <= 0)
	curwin->w_cursor.col = 0;
    else
	curwin->w_cursor.col = (colnr_T)new_cursor_col;
    curwin->w_set_curswant = TRUE;
    changed_cline_bef_curs();

    // May have to adjust the start of the insert.
    if (State & INSERT)
    {
	if (curwin->w_cursor.lnum == Insstart.lnum && Insstart.col != 0)
	{
	    if ((int)Insstart.col <= insstart_less)
		Insstart.col = 0;
	    else
		Insstart.col -= insstart_less;
	}
	if ((int)ai_col <= insstart_less)
	    ai_col = 0;
	else
	    ai_col -= insstart_less;
    }

    // For REPLACE mode, may have to fix the replace stack, if it's possible.
    // If the number of characters before the cursor decreased, need to pop a
    // few characters from the replace stack.
    // If the number of characters before the cursor increased, need to push a
    // few NULs onto the replace stack.
    if (REPLACE_NORMAL(State) && start_col >= 0)
    {
	while (start_col > (int)curwin->w_cursor.col)
	{
	    replace_join(0);	    // remove a NUL from the replace stack
	    --start_col;
	}
	while (start_col < (int)curwin->w_cursor.col || replaced)
	{
	    replace_push(NUL);
	    if (replaced)
	    {
		replace_push(replaced);
		replaced = NUL;
	    }
	    ++start_col;
	}
    }

    // For VREPLACE mode, we also have to fix the replace stack.  In this case
    // it is always possible because we backspace over the whole line and then
    // put it back again the way we wanted it.
    if (State & VREPLACE_FLAG)
    {
	// If orig_line didn't allocate, just return.  At least we did the job,
	// even if you can't backspace.
	if (orig_line == NULL)
	    return;

	// Save new line
	new_line = vim_strsave(ml_get_curline());
	if (new_line == NULL)
	    return;

	// We only put back the new line up to the cursor
	new_line[curwin->w_cursor.col] = NUL;

	// Put back original line
	ml_replace(curwin->w_cursor.lnum, orig_line, FALSE);
	curwin->w_cursor.col = orig_col;

	// Backspace from cursor to start of line
	backspace_until_column(0);

	// Insert new stuff into line again
	ins_bytes(new_line);

	vim_free(new_line);
    }
}


static tmsize_t TIFFWritePixels(TIFF *tiff,TIFFInfo *tiff_info,ssize_t row,
  tsample_t sample,Image *image)
{
  ssize_t
    i;

  tmsize_t
    status;

  size_t
    number_tiles,
    tile_width;

  ssize_t
    bytes_per_pixel,
    j,
    k,
    l;

  unsigned char
    *p,
    *q;

  if (TIFFIsTiled(tiff) == 0)
    return(TIFFWriteScanline(tiff,tiff_info->scanline,(uint32) row,sample));
  /*
    Fill scanlines to tile height.
  */
  i=(ssize_t) (row % tiff_info->tile_geometry.height)*TIFFScanlineSize(tiff);
  (void) memcpy(tiff_info->scanlines+i,(char *) tiff_info->scanline,
    (size_t) TIFFScanlineSize(tiff));
  if (((size_t) (row % tiff_info->tile_geometry.height) !=
      (tiff_info->tile_geometry.height-1)) &&
      (row != (ssize_t) (image->rows-1)))
    return(0);
  /*
    Write tile to TIFF image.
  */
  status=0;
  bytes_per_pixel=TIFFTileSize(tiff)/(ssize_t) (tiff_info->tile_geometry.height*
    tiff_info->tile_geometry.width);
  number_tiles=(image->columns+tiff_info->tile_geometry.width)/
    tiff_info->tile_geometry.width;
  for (i=0; i < (ssize_t) number_tiles; i++)
  {
    tile_width=(i == (ssize_t) (number_tiles-1)) ? image->columns-(i*
      tiff_info->tile_geometry.width) : tiff_info->tile_geometry.width;
    for (j=0; j < (ssize_t) ((row % tiff_info->tile_geometry.height)+1); j++)
      for (k=0; k < (ssize_t) tile_width; k++)
      {
        if (bytes_per_pixel == 0)
          {
            p=tiff_info->scanlines+(j*TIFFScanlineSize(tiff)+(i*
              tiff_info->tile_geometry.width+k)/8);
            q=tiff_info->pixels+(j*TIFFTileRowSize(tiff)+k/8);
            *q++=(*p++);
            continue;
          }
        p=tiff_info->scanlines+(j*TIFFScanlineSize(tiff)+(i*
          tiff_info->tile_geometry.width+k)*bytes_per_pixel);
        q=tiff_info->pixels+(j*TIFFTileRowSize(tiff)+k*bytes_per_pixel);
        for (l=0; l < bytes_per_pixel; l++)
          *q++=(*p++);
      }
    if ((i*tiff_info->tile_geometry.width) != image->columns)
      status=TIFFWriteTile(tiff,tiff_info->pixels,(uint32) (i*
        tiff_info->tile_geometry.width),(uint32) ((row/
        tiff_info->tile_geometry.height)*tiff_info->tile_geometry.height),0,
        sample);
    if (status < 0)
      break;
  }
  return(status);
}

squash heap-buffer-overflow, PoC TIFF from Hardik


static MagickBooleanType GetTIFFInfo(const ImageInfo *image_info,TIFF *tiff,
  TIFFInfo *tiff_info)
{
#define TIFFStripSizeDefault  1048576

  const char
    *option;

  MagickStatusType
    flags;

  uint32
    tile_columns,
    tile_rows;

  assert(tiff_info != (TIFFInfo *) NULL);
  (void) memset(tiff_info,0,sizeof(*tiff_info));
  option=GetImageOption(image_info,"tiff:tile-geometry");
  if (option == (const char *) NULL)
    {
      size_t
        extent;

      uint32
        rows = 0,
        rows_per_strip = 0;

      extent=TIFFScanlineSize(tiff);
      rows_per_strip=TIFFStripSizeDefault/(extent == 0 ? 1 : (uint32) extent);
      rows_per_strip=16*(((rows_per_strip < 16 ? 16 : rows_per_strip)+1)/16);
      (void) TIFFGetField(tiff,TIFFTAG_IMAGELENGTH,&rows);
      if (rows_per_strip > rows)
        rows_per_strip=rows;
      option=GetImageOption(image_info,"tiff:rows-per-strip");
      if (option != (const char *) NULL)
        rows_per_strip=(uint32) strtoul(option,(char **) NULL,10);
      rows_per_strip=TIFFDefaultStripSize(tiff,rows_per_strip);
      (void) TIFFSetField(tiff,TIFFTAG_ROWSPERSTRIP,rows_per_strip);
      return(MagickTrue);
    }
  flags=ParseAbsoluteGeometry(option,&tiff_info->tile_geometry);
  if ((flags & HeightValue) == 0)
    tiff_info->tile_geometry.height=tiff_info->tile_geometry.width;
  tile_columns=(uint32) tiff_info->tile_geometry.width;
  tile_rows=(uint32) tiff_info->tile_geometry.height;
  TIFFDefaultTileSize(tiff,&tile_columns,&tile_rows);
  (void) TIFFSetField(tiff,TIFFTAG_TILEWIDTH,tile_columns);
  (void) TIFFSetField(tiff,TIFFTAG_TILELENGTH,tile_rows);
  tiff_info->tile_geometry.width=tile_columns;
  tiff_info->tile_geometry.height=tile_rows;
  if ((TIFFScanlineSize(tiff) <= 0) || (TIFFTileSize(tiff) <= 0))
    {
      DestroyTIFFInfo(tiff_info);
      return(MagickFalse);
    }
  tiff_info->scanlines=(unsigned char *) AcquireQuantumMemory((size_t)
    tile_rows*TIFFScanlineSize(tiff),sizeof(*tiff_info->scanlines));
  tiff_info->pixels=(unsigned char *) AcquireQuantumMemory((size_t)
    tile_rows*TIFFTileSize(tiff),sizeof(*tiff_info->scanlines));
  if ((tiff_info->scanlines == (unsigned char *) NULL) ||
      (tiff_info->pixels == (unsigned char *) NULL))
    {
      DestroyTIFFInfo(tiff_info);
      return(MagickFalse);
    }
  return(MagickTrue);
}


static int TIFFReadPixels(TIFF *tiff,const tsample_t sample,const ssize_t row,
  tdata_t scanline)
{
  int
    status;

  status=TIFFReadScanline(tiff,scanline,(uint32) row,sample);
  return(status);
}