void ServerSecurityFeature::collectOptions(std::shared_ptr<ProgramOptions> options) {
  options->addOption("--server.harden",
                     "lock down REST APIs that reveal version information or server "
                     "internals for non-admin users",
                     new BooleanParameter(&_hardenedRestApi))
                     .setIntroducedIn(30500);

  options->addOption("--foxx.api", "enables Foxx management REST APIs",
                     new BooleanParameter(&_enableFoxxApi),
                     arangodb::options::makeFlags(
                     arangodb::options::Flags::DefaultNoComponents,
                     arangodb::options::Flags::OnCoordinator,
                     arangodb::options::Flags::OnSingle))
                     .setIntroducedIn(30500);

  options->addOption("--foxx.store", "enables Foxx store in web interface",
                     new BooleanParameter(&_enableFoxxStore),
                     arangodb::options::makeFlags(
                     arangodb::options::Flags::DefaultNoComponents,
                     arangodb::options::Flags::OnCoordinator,
                     arangodb::options::Flags::OnSingle))
                     .setIntroducedIn(30500);

  options->addOption("--foxx.allow-install-from-remote", "allow installing Foxx apps from remote URLs other than Github",
                     new BooleanParameter(&_foxxAllowInstallFromRemote),
                     arangodb::options::makeFlags(
                     arangodb::options::Flags::DefaultNoComponents,
                     arangodb::options::Flags::OnCoordinator,
                     arangodb::options::Flags::OnSingle))
                     .setIntroducedIn(30805);
}


static void JS_DebugShouldFailAt(v8::FunctionCallbackInfo<v8::Value> const& args) {
  TRI_V8_TRY_CATCH_BEGIN(isolate);
  v8::HandleScope scope(isolate);

  TRI_GET_GLOBALS();

  if (v8g->_vocbase == nullptr) {
    TRI_V8_THROW_EXCEPTION_MEMORY();
  }

  // extract arguments
  if (args.Length() != 1) {
    TRI_V8_THROW_EXCEPTION_USAGE("debugShouldFailAt(<point>)");
  }

  std::string const point = TRI_ObjectToString(isolate, args[0]);

  TRI_V8_RETURN_BOOL(TRI_ShouldFailDebugging(point.c_str()));

  TRI_V8_TRY_CATCH_END
}


static void JS_DebugSetFailAt(v8::FunctionCallbackInfo<v8::Value> const& args) {
  TRI_V8_TRY_CATCH_BEGIN(isolate);
  v8::HandleScope scope(isolate);

  TRI_GET_GLOBALS();

  if (v8g->_vocbase == nullptr) {
    TRI_V8_THROW_EXCEPTION_MEMORY();
  }
  std::string dbname(v8g->_vocbase->name());

  // extract arguments
  if (args.Length() != 1) {
    TRI_V8_THROW_EXCEPTION_USAGE("debugSetFailAt(<point>)");
  }

  std::string const point = TRI_ObjectToString(isolate, args[0]);

  TRI_AddFailurePointDebugging(point.c_str());

  if (ServerState::instance()->isCoordinator()) {
    auto res = clusterSendToAllServers(isolate, dbname,
                                       "_admin/debug/failat/" + StringUtils::urlEncode(point),
                                       arangodb::rest::RequestType::PUT, "");
    if (res != TRI_ERROR_NO_ERROR) {
      TRI_V8_THROW_EXCEPTION(res);
    }
  }

  TRI_V8_RETURN_UNDEFINED();
  TRI_V8_TRY_CATCH_END
}


static void JS_DebugRemoveFailAt(
    v8::FunctionCallbackInfo<v8::Value> const& args) {
  TRI_V8_TRY_CATCH_BEGIN(isolate);
  v8::HandleScope scope(isolate);

  TRI_GET_GLOBALS();

  if (v8g->_vocbase == nullptr) {
    TRI_V8_THROW_EXCEPTION_MEMORY();
  }
  std::string dbname(v8g->_vocbase->name());

  // extract arguments
  if (args.Length() != 1) {
    TRI_V8_THROW_EXCEPTION_USAGE("debugRemoveFailAt(<point>)");
  }

  std::string const point = TRI_ObjectToString(isolate, args[0]);

  TRI_RemoveFailurePointDebugging(point.c_str());

  if (ServerState::instance()->isCoordinator()) {
    auto res = clusterSendToAllServers(
        isolate, dbname, "_admin/debug/failat/" + StringUtils::urlEncode(point),
        arangodb::rest::RequestType::DELETE_REQ, "");
    if (res != TRI_ERROR_NO_ERROR) {
      TRI_V8_THROW_EXCEPTION(res);
    }
  }

  TRI_V8_RETURN_UNDEFINED();
  TRI_V8_TRY_CATCH_END
}


static void JS_DebugShouldFailAt(
    v8::FunctionCallbackInfo<v8::Value> const& args) {
  TRI_V8_TRY_CATCH_BEGIN(isolate);
  v8::HandleScope scope(isolate);

  TRI_GET_GLOBALS();

  if (v8g->_vocbase == nullptr) {
    TRI_V8_THROW_EXCEPTION_MEMORY();
  }

  // extract arguments
  if (args.Length() != 1) {
    TRI_V8_THROW_EXCEPTION_USAGE("debugShouldFailAt(<point>)");
  }

  std::string const point = TRI_ObjectToString(isolate, args[0]);

  TRI_V8_RETURN_BOOL(TRI_ShouldFailDebugging(point.c_str()));

  TRI_V8_TRY_CATCH_END
}


ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
{
	struct dev_data			*dev = fd->private_data;
	ssize_t				retval;
	enum ep0_state			state;

	spin_lock_irq (&dev->lock);
	if (dev->state <= STATE_DEV_OPENED) {
		retval = -EINVAL;
		goto done;
	}

	/* report fd mode change before acting on it */
	if (dev->setup_abort) {
		dev->setup_abort = 0;
		retval = -EIDRM;
		goto done;
	}

	/* control DATA stage */
	if ((state = dev->state) == STATE_DEV_SETUP) {

		if (dev->setup_in) {		/* stall IN */
			VDEBUG(dev, "ep0in stall\n");
			(void) usb_ep_set_halt (dev->gadget->ep0);
			retval = -EL2HLT;
			dev->state = STATE_DEV_CONNECTED;

		} else if (len == 0) {		/* ack SET_CONFIGURATION etc */
			struct usb_ep		*ep = dev->gadget->ep0;
			struct usb_request	*req = dev->req;

			if ((retval = setup_req (ep, req, 0)) == 0) {
				++dev->udc_usage;
				spin_unlock_irq (&dev->lock);
				retval = usb_ep_queue (ep, req, GFP_KERNEL);
				spin_lock_irq (&dev->lock);
				--dev->udc_usage;
			}
			dev->state = STATE_DEV_CONNECTED;

			/* assume that was SET_CONFIGURATION */
			if (dev->current_config) {
				unsigned power;

				if (gadget_is_dualspeed(dev->gadget)
						&& (dev->gadget->speed
							== USB_SPEED_HIGH))
					power = dev->hs_config->bMaxPower;
				else
					power = dev->config->bMaxPower;
				usb_gadget_vbus_draw(dev->gadget, 2 * power);
			}

		} else {			/* collect OUT data */
			if ((fd->f_flags & O_NONBLOCK) != 0
					&& !dev->setup_out_ready) {
				retval = -EAGAIN;
				goto done;
			}
			spin_unlock_irq (&dev->lock);
			retval = wait_event_interruptible (dev->wait,
					dev->setup_out_ready != 0);

			/* FIXME state could change from under us */
			spin_lock_irq (&dev->lock);
			if (retval)
				goto done;

			if (dev->state != STATE_DEV_SETUP) {
				retval = -ECANCELED;
				goto done;
			}
			dev->state = STATE_DEV_CONNECTED;

			if (dev->setup_out_error)
				retval = -EIO;
			else {
				len = min (len, (size_t)dev->req->actual);
				++dev->udc_usage;
				spin_unlock_irq(&dev->lock);
				if (copy_to_user (buf, dev->req->buf, len))
					retval = -EFAULT;
				else
					retval = len;
				spin_lock_irq(&dev->lock);
				--dev->udc_usage;
				clean_req (dev->gadget->ep0, dev->req);
				/* NOTE userspace can't yet choose to stall */
			}
		}
		goto done;
	}

	/* else normal: return event data */
	if (len < sizeof dev->event [0]) {
		retval = -EINVAL;
		goto done;
	}
	len -= len % sizeof (struct usb_gadgetfs_event);
	dev->usermode_setup = 1;

scan:
	/* return queued events right away */
	if (dev->ev_next != 0) {
		unsigned		i, n;

		n = len / sizeof (struct usb_gadgetfs_event);
		if (dev->ev_next < n)
			n = dev->ev_next;

		/* ep0 i/o has special semantics during STATE_DEV_SETUP */
		for (i = 0; i < n; i++) {
			if (dev->event [i].type == GADGETFS_SETUP) {
				dev->state = STATE_DEV_SETUP;
				n = i + 1;
				break;
			}
		}
		spin_unlock_irq (&dev->lock);
		len = n * sizeof (struct usb_gadgetfs_event);
		if (copy_to_user (buf, &dev->event, len))
			retval = -EFAULT;
		else
			retval = len;
		if (len > 0) {
			/* NOTE this doesn't guard against broken drivers;
			 * concurrent ep0 readers may lose events.
			 */
			spin_lock_irq (&dev->lock);
			if (dev->ev_next > n) {
				memmove(&dev->event[0], &dev->event[n],
					sizeof (struct usb_gadgetfs_event)
						* (dev->ev_next - n));
			}
			dev->ev_next -= n;
			spin_unlock_irq (&dev->lock);
		}
		return retval;
	}
	if (fd->f_flags & O_NONBLOCK) {
		retval = -EAGAIN;
		goto done;
	}

	switch (state) {
	default:
		DBG (dev, "fail %s, state %d\n", __func__, state);
		retval = -ESRCH;
		break;
	case STATE_DEV_UNCONNECTED:
	case STATE_DEV_CONNECTED:
		spin_unlock_irq (&dev->lock);
		DBG (dev, "%s wait\n", __func__);

		/* wait for events */
		retval = wait_event_interruptible (dev->wait,
				dev->ev_next != 0);
		if (retval < 0)
			return retval;
		spin_lock_irq (&dev->lock);
		goto scan;
	}

done:
	spin_unlock_irq (&dev->lock);
	return retval;
}


static void put_ep (struct ep_data *data)
{
	if (likely (!refcount_dec_and_test (&data->count)))
		return;
	put_dev (data->dev);
	/* needs no more cleanup */
	BUG_ON (!list_empty (&data->epfiles));
	BUG_ON (waitqueue_active (&data->wait));
	kfree (data);
}


ep_read_iter(struct kiocb *iocb, struct iov_iter *to)
{
	struct file *file = iocb->ki_filp;
	struct ep_data *epdata = file->private_data;
	size_t len = iov_iter_count(to);
	ssize_t value;
	char *buf;

	if ((value = get_ready_ep(file->f_flags, epdata, false)) < 0)
		return value;

	/* halt any endpoint by doing a "wrong direction" i/o call */
	if (usb_endpoint_dir_in(&epdata->desc)) {
		if (usb_endpoint_xfer_isoc(&epdata->desc) ||
		    !is_sync_kiocb(iocb)) {
			mutex_unlock(&epdata->lock);
			return -EINVAL;
		}
		DBG (epdata->dev, "%s halt\n", epdata->name);
		spin_lock_irq(&epdata->dev->lock);
		if (likely(epdata->ep != NULL))
			usb_ep_set_halt(epdata->ep);
		spin_unlock_irq(&epdata->dev->lock);
		mutex_unlock(&epdata->lock);
		return -EBADMSG;
	}

	buf = kmalloc(len, GFP_KERNEL);
	if (unlikely(!buf)) {
		mutex_unlock(&epdata->lock);
		return -ENOMEM;
	}
	if (is_sync_kiocb(iocb)) {
		value = ep_io(epdata, buf, len);
		if (value >= 0 && (copy_to_iter(buf, value, to) != value))
			value = -EFAULT;
	} else {
		struct kiocb_priv *priv = kzalloc(sizeof *priv, GFP_KERNEL);
		value = -ENOMEM;
		if (!priv)
			goto fail;
		priv->to_free = dup_iter(&priv->to, to, GFP_KERNEL);
		if (!priv->to_free) {
			kfree(priv);
			goto fail;
		}
		value = ep_aio(iocb, priv, epdata, buf, len);
		if (value == -EIOCBQUEUED)
			buf = NULL;
	}
fail:
	kfree(buf);
	mutex_unlock(&epdata->lock);
	return value;
}


static long ep_ioctl(struct file *fd, unsigned code, unsigned long value)
{
	struct ep_data		*data = fd->private_data;
	int			status;

	if ((status = get_ready_ep (fd->f_flags, data, false)) < 0)
		return status;

	spin_lock_irq (&data->dev->lock);
	if (likely (data->ep != NULL)) {
		switch (code) {
		case GADGETFS_FIFO_STATUS:
			status = usb_ep_fifo_status (data->ep);
			break;
		case GADGETFS_FIFO_FLUSH:
			usb_ep_fifo_flush (data->ep);
			break;
		case GADGETFS_CLEAR_HALT:
			status = usb_ep_clear_halt (data->ep);
			break;
		default:
			status = -ENOTTY;
		}
	} else
		status = -ENODEV;
	spin_unlock_irq (&data->dev->lock);
	mutex_unlock(&data->lock);
	return status;
}


static void ep_user_copy_worker(struct work_struct *work)
{
	struct kiocb_priv *priv = container_of(work, struct kiocb_priv, work);
	struct mm_struct *mm = priv->mm;
	struct kiocb *iocb = priv->iocb;
	size_t ret;

	kthread_use_mm(mm);
	ret = copy_to_iter(priv->buf, priv->actual, &priv->to);
	kthread_unuse_mm(mm);
	if (!ret)
		ret = -EFAULT;

	/* completing the iocb can drop the ctx and mm, don't touch mm after */
	iocb->ki_complete(iocb, ret);

	kfree(priv->buf);
	kfree(priv->to_free);
	kfree(priv);
}


static int yam_set_mac_address(struct net_device *dev, void *addr)
{
	struct sockaddr *sa = (struct sockaddr *) addr;

	/* addr is an AX.25 shifted ASCII mac address */
	dev_addr_set(dev, sa->sa_data);
	return 0;
}


static void yam_setup(struct net_device *dev)
{
	struct yam_port *yp = netdev_priv(dev);

	yp->magic = YAM_MAGIC;
	yp->bitrate = DEFAULT_BITRATE;
	yp->baudrate = DEFAULT_BITRATE * 2;
	yp->iobase = 0;
	yp->irq = 0;
	yp->dupmode = 0;
	yp->holdd = DEFAULT_HOLDD;
	yp->txd = DEFAULT_TXD;
	yp->txtail = DEFAULT_TXTAIL;
	yp->slot = DEFAULT_SLOT;
	yp->pers = DEFAULT_PERS;
	yp->dev = dev;

	dev->base_addr = yp->iobase;
	dev->irq = yp->irq;

	skb_queue_head_init(&yp->send_queue);

	dev->netdev_ops = &yam_netdev_ops;
	dev->header_ops = &ax25_header_ops;

	dev->type = ARPHRD_AX25;
	dev->hard_header_len = AX25_MAX_HEADER_LEN;
	dev->mtu = AX25_MTU;
	dev->addr_len = AX25_ADDR_LEN;
	memcpy(dev->broadcast, &ax25_bcast, AX25_ADDR_LEN);
	dev_addr_set(dev, (u8 *)&ax25_defaddr);
}


int __udf_add_aext(struct inode *inode, struct extent_position *epos,
		   struct kernel_lb_addr *eloc, uint32_t elen, int inc)
{
	struct udf_inode_info *iinfo = UDF_I(inode);
	struct allocExtDesc *aed;
	int adsize;

	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
		adsize = sizeof(struct short_ad);
	else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
		adsize = sizeof(struct long_ad);
	else
		return -EIO;

	if (!epos->bh) {
		WARN_ON(iinfo->i_lenAlloc !=
			epos->offset - udf_file_entry_alloc_offset(inode));
	} else {
		aed = (struct allocExtDesc *)epos->bh->b_data;
		WARN_ON(le32_to_cpu(aed->lengthAllocDescs) !=
			epos->offset - sizeof(struct allocExtDesc));
		WARN_ON(epos->offset + adsize > inode->i_sb->s_blocksize);
	}

	udf_write_aext(inode, epos, eloc, elen, inc);

	if (!epos->bh) {
		iinfo->i_lenAlloc += adsize;
		mark_inode_dirty(inode);
	} else {
		aed = (struct allocExtDesc *)epos->bh->b_data;
		le32_add_cpu(&aed->lengthAllocDescs, adsize);
		if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) ||
				UDF_SB(inode->i_sb)->s_udfrev >= 0x0201)
			udf_update_tag(epos->bh->b_data,
					epos->offset + (inc ? 0 : adsize));
		else
			udf_update_tag(epos->bh->b_data,
					sizeof(struct allocExtDesc));
		mark_buffer_dirty_inode(epos->bh, inode);
	}

	return 0;
}


static int udf_get_block(struct inode *inode, sector_t block,
			 struct buffer_head *bh_result, int create)
{
	int err, new;
	sector_t phys = 0;
	struct udf_inode_info *iinfo;

	if (!create) {
		phys = udf_block_map(inode, block);
		if (phys)
			map_bh(bh_result, inode->i_sb, phys);
		return 0;
	}

	err = -EIO;
	new = 0;
	iinfo = UDF_I(inode);

	down_write(&iinfo->i_data_sem);
	if (block == iinfo->i_next_alloc_block + 1) {
		iinfo->i_next_alloc_block++;
		iinfo->i_next_alloc_goal++;
	}

	udf_clear_extent_cache(inode);
	phys = inode_getblk(inode, block, &err, &new);
	if (!phys)
		goto abort;

	if (new)
		set_buffer_new(bh_result);
	map_bh(bh_result, inode->i_sb, phys);

abort:
	up_write(&iinfo->i_data_sem);
	return err;
}


static ssize_t udf_direct_IO(struct kiocb *iocb, struct iov_iter *iter)
{
	struct file *file = iocb->ki_filp;
	struct address_space *mapping = file->f_mapping;
	struct inode *inode = mapping->host;
	size_t count = iov_iter_count(iter);
	ssize_t ret;

	ret = blockdev_direct_IO(iocb, inode, iter, udf_get_block);
	if (unlikely(ret < 0 && iov_iter_rw(iter) == WRITE))
		udf_write_failed(mapping, iocb->ki_pos + count);
	return ret;
}


int8_t udf_delete_aext(struct inode *inode, struct extent_position epos)
{
	struct extent_position oepos;
	int adsize;
	int8_t etype;
	struct allocExtDesc *aed;
	struct udf_inode_info *iinfo;
	struct kernel_lb_addr eloc;
	uint32_t elen;

	if (epos.bh) {
		get_bh(epos.bh);
		get_bh(epos.bh);
	}

	iinfo = UDF_I(inode);
	if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
		adsize = sizeof(struct short_ad);
	else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
		adsize = sizeof(struct long_ad);
	else
		adsize = 0;

	oepos = epos;
	if (udf_next_aext(inode, &epos, &eloc, &elen, 1) == -1)
		return -1;

	while ((etype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1) {
		udf_write_aext(inode, &oepos, &eloc, (etype << 30) | elen, 1);
		if (oepos.bh != epos.bh) {
			oepos.block = epos.block;
			brelse(oepos.bh);
			get_bh(epos.bh);
			oepos.bh = epos.bh;
			oepos.offset = epos.offset - adsize;
		}
	}
	memset(&eloc, 0x00, sizeof(struct kernel_lb_addr));
	elen = 0;

	if (epos.bh != oepos.bh) {
		udf_free_blocks(inode->i_sb, inode, &epos.block, 0, 1);
		udf_write_aext(inode, &oepos, &eloc, elen, 1);
		udf_write_aext(inode, &oepos, &eloc, elen, 1);
		if (!oepos.bh) {
			iinfo->i_lenAlloc -= (adsize * 2);
			mark_inode_dirty(inode);
		} else {
			aed = (struct allocExtDesc *)oepos.bh->b_data;
			le32_add_cpu(&aed->lengthAllocDescs, -(2 * adsize));
			if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) ||
			    UDF_SB(inode->i_sb)->s_udfrev >= 0x0201)
				udf_update_tag(oepos.bh->b_data,
						oepos.offset - (2 * adsize));
			else
				udf_update_tag(oepos.bh->b_data,
						sizeof(struct allocExtDesc));
			mark_buffer_dirty_inode(oepos.bh, inode);
		}
	} else {
		udf_write_aext(inode, &oepos, &eloc, elen, 1);
		if (!oepos.bh) {
			iinfo->i_lenAlloc -= adsize;
			mark_inode_dirty(inode);
		} else {
			aed = (struct allocExtDesc *)oepos.bh->b_data;
			le32_add_cpu(&aed->lengthAllocDescs, -adsize);
			if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) ||
			    UDF_SB(inode->i_sb)->s_udfrev >= 0x0201)
				udf_update_tag(oepos.bh->b_data,
						epos.offset - adsize);
			else
				udf_update_tag(oepos.bh->b_data,
						sizeof(struct allocExtDesc));
			mark_buffer_dirty_inode(oepos.bh, inode);
		}
	}

	brelse(epos.bh);
	brelse(oepos.bh);

	return (elen >> 30);
}


void http_check_request_for_cacheability(struct stream *s, struct channel *req)
{
	struct http_txn *txn = s->txn;
	struct htx *htx;
	struct http_hdr_ctx ctx = { .blk = NULL };
	int pragma_found, cc_found;

	if ((txn->flags & (TX_CACHEABLE|TX_CACHE_IGNORE)) == TX_CACHE_IGNORE)
		return; /* nothing more to do here */

	htx = htxbuf(&req->buf);
	pragma_found = cc_found = 0;

	/* Check "pragma" header for HTTP/1.0 compatibility. */
	if (http_find_header(htx, ist("pragma"), &ctx, 1)) {
		if (isteqi(ctx.value, ist("no-cache"))) {
			pragma_found = 1;
		}
	}

	ctx.blk = NULL;
	/* Don't use the cache and don't try to store if we found the
	 * Authorization header */
	if (http_find_header(htx, ist("authorization"), &ctx, 1)) {
		txn->flags &= ~TX_CACHEABLE & ~TX_CACHE_COOK;
		txn->flags |= TX_CACHE_IGNORE;
	}

	/* Look for "cache-control" header and iterate over all the values
	 * until we find one that specifies that caching is possible or not. */
	ctx.blk = NULL;
	while (http_find_header(htx, ist("cache-control"), &ctx, 0)) {
		cc_found = 1;
		/* We don't check the values after max-age, max-stale nor min-fresh,
		 * we simply don't use the cache when they're specified. */
		if (istmatchi(ctx.value, ist("max-age")) ||
		    istmatchi(ctx.value, ist("no-cache")) ||
		    istmatchi(ctx.value, ist("max-stale")) ||
		    istmatchi(ctx.value, ist("min-fresh"))) {
			txn->flags |= TX_CACHE_IGNORE;
			continue;
		}
		if (istmatchi(ctx.value, ist("no-store"))) {
			txn->flags &= ~TX_CACHEABLE & ~TX_CACHE_COOK;
			continue;
		}
	}

	/* RFC7234#5.4:
	 *   When the Cache-Control header field is also present and
	 *   understood in a request, Pragma is ignored.
	 *   When the Cache-Control header field is not present in a
	 *   request, caches MUST consider the no-cache request
	 *   pragma-directive as having the same effect as if
	 *   "Cache-Control: no-cache" were present.
	 */
	if (!cc_found && pragma_found)
		txn->flags |= TX_CACHE_IGNORE;
}


int http_res_set_status(unsigned int status, struct ist reason, struct stream *s)
{
	struct htx *htx = htxbuf(&s->res.buf);
	char *res;

	chunk_reset(&trash);
	res = ultoa_o(status, trash.area, trash.size);
	trash.data = res - trash.area;

	/* Do we have a custom reason format string? */
	if (!isttest(reason)) {
		const char *str = http_get_reason(status);
		reason = ist(str);
	}

	if (!http_replace_res_status(htx, ist2(trash.area, trash.data), reason))
		return -1;
	return 0;
}


int http_process_request(struct stream *s, struct channel *req, int an_bit)
{
	struct session *sess = s->sess;
	struct http_txn *txn = s->txn;
	struct htx *htx;
	struct connection *cli_conn = objt_conn(strm_sess(s)->origin);

	DBG_TRACE_ENTER(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn);

	/*
	 * Right now, we know that we have processed the entire headers
	 * and that unwanted requests have been filtered out. We can do
	 * whatever we want with the remaining request. Also, now we
	 * may have separate values for ->fe, ->be.
	 */
	htx = htxbuf(&req->buf);

	/*
	 * 7: Now we can work with the cookies.
	 * Note that doing so might move headers in the request, but
	 * the fields will stay coherent and the URI will not move.
	 * This should only be performed in the backend.
	 */
	if (s->be->cookie_name || sess->fe->capture_name)
		http_manage_client_side_cookies(s, req);

	/* 8: Generate unique ID if a "unique-id-format" is defined.
	 *
	 * A unique ID is generated even when it is not sent to ensure that the ID can make use of
	 * fetches only available in the HTTP request processing stage.
	 */
	if (!LIST_ISEMPTY(&sess->fe->format_unique_id)) {
		struct ist unique_id = stream_generate_unique_id(s, &sess->fe->format_unique_id);

		if (!isttest(unique_id)) {
			if (!(s->flags & SF_ERR_MASK))
				s->flags |= SF_ERR_RESOURCE;
			goto return_int_err;
		}

		/* send unique ID if a "unique-id-header" is defined */
		if (isttest(sess->fe->header_unique_id) &&
		    unlikely(!http_add_header(htx, sess->fe->header_unique_id, s->unique_id)))
				goto return_int_err;
	}

	/*
	 * 9: add X-Forwarded-For if either the frontend or the backend
	 * asks for it.
	 */
	if ((sess->fe->options | s->be->options) & PR_O_FWDFOR) {
		const struct sockaddr_storage *src = si_src(&s->si[0]);
		struct http_hdr_ctx ctx = { .blk = NULL };
		struct ist hdr = ist2(s->be->fwdfor_hdr_len ? s->be->fwdfor_hdr_name : sess->fe->fwdfor_hdr_name,
				      s->be->fwdfor_hdr_len ? s->be->fwdfor_hdr_len : sess->fe->fwdfor_hdr_len);

		if (!((sess->fe->options | s->be->options) & PR_O_FF_ALWAYS) &&
		    http_find_header(htx, hdr, &ctx, 0)) {
			/* The header is set to be added only if none is present
			 * and we found it, so don't do anything.
			 */
		}
		else if (src && src->ss_family == AF_INET) {
			/* Add an X-Forwarded-For header unless the source IP is
			 * in the 'except' network range.
			 */
			if (ipcmp2net(src, &sess->fe->except_xff_net) &&
			    ipcmp2net(src, &s->be->except_xff_net)) {
				unsigned char *pn = (unsigned char *)&((struct sockaddr_in *)src)->sin_addr;

				/* Note: we rely on the backend to get the header name to be used for
				 * x-forwarded-for, because the header is really meant for the backends.
				 * However, if the backend did not specify any option, we have to rely
				 * on the frontend's header name.
				 */
				chunk_printf(&trash, "%d.%d.%d.%d", pn[0], pn[1], pn[2], pn[3]);
				if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data))))
					goto return_int_err;
			}
		}
		else if (src && src->ss_family == AF_INET6) {
			/* Add an X-Forwarded-For header unless the source IP is
			 * in the 'except' network range.
			 */
			if (ipcmp2net(src, &sess->fe->except_xff_net) &&
			    ipcmp2net(src, &s->be->except_xff_net)) {
				char pn[INET6_ADDRSTRLEN];

				inet_ntop(AF_INET6,
					  (const void *)&((struct sockaddr_in6 *)(src))->sin6_addr,
					  pn, sizeof(pn));

				/* Note: we rely on the backend to get the header name to be used for
				 * x-forwarded-for, because the header is really meant for the backends.
				 * However, if the backend did not specify any option, we have to rely
				 * on the frontend's header name.
				 */
				chunk_printf(&trash, "%s", pn);
				if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data))))
					goto return_int_err;
			}
		}
	}

	/*
	 * 10: add X-Original-To if either the frontend or the backend
	 * asks for it.
	 */
	if ((sess->fe->options | s->be->options) & PR_O_ORGTO) {
		const struct sockaddr_storage *dst = si_dst(&s->si[0]);
		struct ist hdr = ist2(s->be->orgto_hdr_len ? s->be->orgto_hdr_name : sess->fe->orgto_hdr_name,
				      s->be->orgto_hdr_len ? s->be->orgto_hdr_len  : sess->fe->orgto_hdr_len);

		if (dst && dst->ss_family == AF_INET) {
			/* Add an X-Original-To header unless the destination IP is
			 * in the 'except' network range.
			 */
			if (ipcmp2net(dst, &sess->fe->except_xot_net) &&
			    ipcmp2net(dst, &s->be->except_xot_net)) {
				unsigned char *pn = (unsigned char *)&((struct sockaddr_in *)dst)->sin_addr;

				/* Note: we rely on the backend to get the header name to be used for
				 * x-original-to, because the header is really meant for the backends.
				 * However, if the backend did not specify any option, we have to rely
				 * on the frontend's header name.
				 */
				chunk_printf(&trash, "%d.%d.%d.%d", pn[0], pn[1], pn[2], pn[3]);
				if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data))))
					goto return_int_err;
			}
		}
		else if (dst && dst->ss_family == AF_INET6) {
			/* Add an X-Original-To header unless the source IP is
			 * in the 'except' network range.
			 */
			if (ipcmp2net(dst, &sess->fe->except_xot_net) &&
			    ipcmp2net(dst, &s->be->except_xot_net)) {
				char pn[INET6_ADDRSTRLEN];

				inet_ntop(AF_INET6,
					  (const void *)&((struct sockaddr_in6 *)dst)->sin6_addr,
					  pn, sizeof(pn));

				/* Note: we rely on the backend to get the header name to be used for
				 * x-forwarded-for, because the header is really meant for the backends.
				 * However, if the backend did not specify any option, we have to rely
				 * on the frontend's header name.
				 */
				chunk_printf(&trash, "%s", pn);
				if (unlikely(!http_add_header(htx, hdr, ist2(trash.area, trash.data))))
					goto return_int_err;
			}
		}
	}

	/* Filter the request headers if there are filters attached to the
	 * stream.
	 */
	if (HAS_FILTERS(s))
		req->analysers |= AN_REQ_FLT_HTTP_HDRS;

	/* If we have no server assigned yet and we're balancing on url_param
	 * with a POST request, we may be interested in checking the body for
	 * that parameter. This will be done in another analyser.
	 */
	if (!(s->flags & (SF_ASSIGNED|SF_DIRECT)) &&
	    s->txn->meth == HTTP_METH_POST &&
	    (s->be->lbprm.algo & BE_LB_ALGO) == BE_LB_ALGO_PH) {
		channel_dont_connect(req);
		req->analysers |= AN_REQ_HTTP_BODY;
	}

	req->analysers &= ~AN_REQ_FLT_XFER_DATA;
	req->analysers |= AN_REQ_HTTP_XFER_BODY;

	/* We expect some data from the client. Unless we know for sure
	 * we already have a full request, we have to re-enable quick-ack
	 * in case we previously disabled it, otherwise we might cause
	 * the client to delay further data.
	 */
	if ((sess->listener && (sess->listener->options & LI_O_NOQUICKACK)) && !(htx->flags & HTX_FL_EOM))
		conn_set_quickack(cli_conn, 1);

	/*************************************************************
	 * OK, that's finished for the headers. We have done what we *
	 * could. Let's switch to the DATA state.                    *
	 ************************************************************/
	req->analyse_exp = TICK_ETERNITY;
	req->analysers &= ~an_bit;

	s->logs.tv_request = now;
	/* OK let's go on with the BODY now */
	DBG_TRACE_LEAVE(STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA, s, txn);
	return 1;

 return_int_err:
	txn->status = 500;
	if (!(s->flags & SF_ERR_MASK))
		s->flags |= SF_ERR_INTERNAL;
	_HA_ATOMIC_INC(&sess->fe->fe_counters.internal_errors);
	if (s->flags & SF_BE_ASSIGNED)
		_HA_ATOMIC_INC(&s->be->be_counters.internal_errors);
	if (sess->listener && sess->listener->counters)
		_HA_ATOMIC_INC(&sess->listener->counters->internal_errors);

	http_reply_and_close(s, txn->status, http_error_message(s));

	if (!(s->flags & SF_ERR_MASK))
		s->flags |= SF_ERR_PRXCOND;
	if (!(s->flags & SF_FINST_MASK))
		s->flags |= SF_FINST_R;

	DBG_TRACE_DEVEL("leaving on error",
			STRM_EV_STRM_ANA|STRM_EV_HTTP_ANA|STRM_EV_HTTP_ERR, s, txn);
	return 0;
}


static void http_end_request(struct stream *s)
{
	struct channel *chn = &s->req;
	struct http_txn *txn = s->txn;

	DBG_TRACE_ENTER(STRM_EV_HTTP_ANA, s, txn);

	if (unlikely(txn->req.msg_state == HTTP_MSG_ERROR ||
		     txn->rsp.msg_state == HTTP_MSG_ERROR)) {
		channel_abort(chn);
		channel_htx_truncate(chn, htxbuf(&chn->buf));
		goto end;
	}

	if (unlikely(txn->req.msg_state < HTTP_MSG_DONE)) {
		DBG_TRACE_DEVEL("waiting end of the request", STRM_EV_HTTP_ANA, s, txn);
		return;
	}

	if (txn->req.msg_state == HTTP_MSG_DONE) {
		/* No need to read anymore, the request was completely parsed.
		 * We can shut the read side unless we want to abort_on_close,
		 * or we have a POST request. The issue with POST requests is
		 * that some browsers still send a CRLF after the request, and
		 * this CRLF must be read so that it does not remain in the kernel
		 * buffers, otherwise a close could cause an RST on some systems
		 * (eg: Linux).
		 */
		if (!(s->be->options & PR_O_ABRT_CLOSE) && txn->meth != HTTP_METH_POST)
			channel_dont_read(chn);

		/* if the server closes the connection, we want to immediately react
		 * and close the socket to save packets and syscalls.
		 */
		s->si[1].flags |= SI_FL_NOHALF;

		/* In any case we've finished parsing the request so we must
		 * disable Nagle when sending data because 1) we're not going
		 * to shut this side, and 2) the server is waiting for us to
		 * send pending data.
		 */
		chn->flags |= CF_NEVER_WAIT;

		if (txn->rsp.msg_state < HTTP_MSG_DONE) {
			/* The server has not finished to respond, so we
			 * don't want to move in order not to upset it.
			 */
			DBG_TRACE_DEVEL("waiting end of the response", STRM_EV_HTTP_ANA, s, txn);
			return;
		}

		/* When we get here, it means that both the request and the
		 * response have finished receiving. Depending on the connection
		 * mode, we'll have to wait for the last bytes to leave in either
		 * direction, and sometimes for a close to be effective.
		 */
		if (txn->flags & TX_CON_WANT_TUN) {
			/* Tunnel mode will not have any analyser so it needs to
			 * poll for reads.
			 */
			channel_auto_read(chn);
			if (b_data(&chn->buf)) {
				DBG_TRACE_DEVEL("waiting to flush the request", STRM_EV_HTTP_ANA, s, txn);
				return;
			}
			txn->req.msg_state = HTTP_MSG_TUNNEL;
		}
		else {
			/* we're not expecting any new data to come for this
			 * transaction, so we can close it.
			 *
			 *  However, there is an exception if the response
			 *  length is undefined. In this case, we need to wait
			 *  the close from the server. The response will be
			 *  switched in TUNNEL mode until the end.
			 */
			if (!(txn->rsp.flags & HTTP_MSGF_XFER_LEN) &&
			    txn->rsp.msg_state != HTTP_MSG_CLOSED)
				goto check_channel_flags;

			if (!(chn->flags & (CF_SHUTW|CF_SHUTW_NOW))) {
				channel_shutr_now(chn);
				channel_shutw_now(chn);
			}
		}
		goto check_channel_flags;
	}

	if (txn->req.msg_state == HTTP_MSG_CLOSING) {
	  http_msg_closing:
		/* nothing else to forward, just waiting for the output buffer
		 * to be empty and for the shutw_now to take effect.
		 */
		if (channel_is_empty(chn)) {
			txn->req.msg_state = HTTP_MSG_CLOSED;
			goto http_msg_closed;
		}
		else if (chn->flags & CF_SHUTW) {
			txn->req.msg_state = HTTP_MSG_ERROR;
			goto end;
		}
		DBG_TRACE_LEAVE(STRM_EV_HTTP_ANA, s, txn);
		return;
	}

	if (txn->req.msg_state == HTTP_MSG_CLOSED) {
	  http_msg_closed:
		/* if we don't know whether the server will close, we need to hard close */
		if (txn->rsp.flags & HTTP_MSGF_XFER_LEN)
			s->si[1].flags |= SI_FL_NOLINGER;  /* we want to close ASAP */
		/* see above in MSG_DONE why we only do this in these states */
		if (!(s->be->options & PR_O_ABRT_CLOSE))
			channel_dont_read(chn);
		goto end;
	}

  check_channel_flags:
	/* Here, we are in HTTP_MSG_DONE or HTTP_MSG_TUNNEL */
	if (chn->flags & (CF_SHUTW|CF_SHUTW_NOW)) {
		/* if we've just closed an output, let's switch */
		txn->req.msg_state = HTTP_MSG_CLOSING;
		goto http_msg_closing;
	}

  end:
	chn->analysers &= AN_REQ_FLT_END;
	if (txn->req.msg_state == HTTP_MSG_TUNNEL) {
		chn->flags |= CF_NEVER_WAIT;
		if (HAS_REQ_DATA_FILTERS(s))
			chn->analysers |= AN_REQ_FLT_XFER_DATA;
	}
	channel_auto_close(chn);
	channel_auto_read(chn);
	DBG_TRACE_LEAVE(STRM_EV_HTTP_ANA, s, txn);
}


void http_perform_server_redirect(struct stream *s, struct stream_interface *si)
{
	struct channel *req = &s->req;
	struct channel *res = &s->res;
	struct server *srv;
	struct htx *htx;
	struct htx_sl *sl;
	struct ist path, location;
	unsigned int flags;
	struct http_uri_parser parser;

	/*
	 * Create the location
	 */
	chunk_reset(&trash);

	/* 1: add the server's prefix */
	/* special prefix "/" means don't change URL */
	srv = __objt_server(s->target);
	if (srv->rdr_len != 1 || *srv->rdr_pfx != '/') {
		if (!chunk_memcat(&trash, srv->rdr_pfx, srv->rdr_len))
			return;
	}

	/* 2: add the request Path */
	htx = htxbuf(&req->buf);
	sl = http_get_stline(htx);
	parser = http_uri_parser_init(htx_sl_req_uri(sl));
	path = http_parse_path(&parser);
	if (!isttest(path))
		return;

	if (!chunk_memcat(&trash, path.ptr, path.len))
		return;
	location = ist2(trash.area, trash.data);

	/*
	 * Create the 302 respone
	 */
	htx = htx_from_buf(&res->buf);
	flags = (HTX_SL_F_IS_RESP|HTX_SL_F_VER_11|HTX_SL_F_XFER_LEN|HTX_SL_F_BODYLESS);
	sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags,
			    ist("HTTP/1.1"), ist("302"), ist("Found"));
	if (!sl)
		goto fail;
	sl->info.res.status = 302;
	s->txn->status = 302;

        if (!htx_add_header(htx, ist("Cache-Control"), ist("no-cache")) ||
	    !htx_add_header(htx, ist("Connection"), ist("close")) ||
	    !htx_add_header(htx, ist("Content-length"), ist("0")) ||
	    !htx_add_header(htx, ist("Location"), location))
		goto fail;

	if (!htx_add_endof(htx, HTX_BLK_EOH))
		goto fail;

	htx->flags |= HTX_FL_EOM;
	htx_to_buf(htx, &res->buf);
	if (!http_forward_proxy_resp(s, 1))
		goto fail;

	/* return without error. */
	si_shutr(si);
	si_shutw(si);
	si->err_type = SI_ET_NONE;
	si->state    = SI_ST_CLO;

	if (!(s->flags & SF_ERR_MASK))
		s->flags |= SF_ERR_LOCAL;
	if (!(s->flags & SF_FINST_MASK))
		s->flags |= SF_FINST_C;

	/* FIXME: we should increase a counter of redirects per server and per backend. */
	srv_inc_sess_ctr(srv);
	srv_set_sess_last(srv);
	return;

  fail:
	/* If an error occurred, remove the incomplete HTTP response from the
	 * buffer */
	channel_htx_truncate(res, htx);
}


static double shp_area (SHPObject *feat) {
  double area = 0.0;
  if (feat->nParts == 0) {
    area = area2d_polygon (feat->nVertices, feat->padfX, feat->padfY);
  }
  else {
    for (int part = 0; part < feat->nParts; part++) {
      int n;
      if (part < feat->nParts - 1) {
	n = feat->panPartStart[part+1] - feat->panPartStart[part];
      }
      else {
	n = feat->nVertices - feat->panPartStart[part];
      }
      area += area2d_polygon (n, &(feat->padfX[feat->panPartStart[part]]),
			      &(feat->padfY[feat->panPartStart[part]]));
    }
  }
  /* our area function computes in opposite direction */
  return -area;
}


static char *dupstr(const char *src)
{
  char *dst = malloc(strlen(src) + 1);
  if (!dst) {
    fprintf(stderr, "%s:%d: malloc failed!\n", __FILE__, __LINE__);
    exit(EXIT_FAILURE);
  }
  char *cptr = dst;
  while ((*cptr++ = *src++))
    ;
  return dst;
}


R_API RBinJavaStackMapFrame *r_bin_java_build_stack_frame_from_local_variable_table(RBinJavaObj *bin, RBinJavaAttrInfo *attr) {
	RBinJavaStackMapFrame *sf = r_bin_java_default_stack_frame ();
	RBinJavaLocalVariableAttribute *lvattr = NULL;
	RBinJavaVerificationObj *type_item;
	RListIter *iter = NULL;
	ut32 value_cnt = 0;
	ut8 value;
	if (!sf || !bin || !attr || attr->type != R_BIN_JAVA_ATTR_TYPE_LOCAL_VARIABLE_TABLE_ATTR) {
		eprintf ("Attempting to create a stack_map frame from a bad attribute.\n");
		return sf;
	}
	sf->number_of_locals = attr->info.local_variable_table_attr.table_length;
	r_list_foreach (attr->info.local_variable_table_attr.local_variable_table, iter, lvattr) {
		ut32 pos = 0;
		// knock the array Types
		while (lvattr->descriptor[pos] == '[') {
			pos++;
		}
		value = lvattr->descriptor[pos];
		// IFDBG eprintf ("Found the following type value: %c at pos %d in %s\n", value, pos, lvattr->descriptor);
		switch (value) {
		case 'I':
		case 'Z':
		case 'S':
		case 'B':
		case 'C':
			type_item = r_bin_java_verification_info_from_type (bin, R_BIN_JAVA_STACKMAP_INTEGER, 0);
			break;
		case 'F':
			type_item = r_bin_java_verification_info_from_type (bin, R_BIN_JAVA_STACKMAP_FLOAT, 0);
			break;
		case 'D':
			type_item = r_bin_java_verification_info_from_type (bin, R_BIN_JAVA_STACKMAP_DOUBLE, 0);
			break;
		case 'J':
			type_item = r_bin_java_verification_info_from_type (bin, R_BIN_JAVA_STACKMAP_LONG, 0);
			break;
		case 'L':
			// TODO: FIXME write something that will iterate over the CP Pool and find the
			// CONSTANT_Class_info referencing this
		{
			ut16 idx = r_bin_java_find_cp_class_ref_from_name_idx (bin, lvattr->name_idx);
			type_item = r_bin_java_verification_info_from_type (bin, R_BIN_JAVA_STACKMAP_OBJECT, idx);
		}
		break;
		default:
			eprintf ("r_bin_java_build_stack_frame_from_local_variable_table: "
				"not sure how to handle: name: %s, type: %s\n", lvattr->name, lvattr->descriptor);
			type_item = r_bin_java_verification_info_from_type (bin, R_BIN_JAVA_STACKMAP_NULL, 0);
		}
		if (type_item) {
			r_list_append (sf->local_items, (void *) type_item);
		}
		value_cnt++;
	}
	if (value_cnt != attr->info.local_variable_table_attr.table_length) {
		IFDBG eprintf ("r_bin_java_build_stack_frame_from_local_variable_table: "
		"Number of locals not accurate.  Expected %d but got %d",
		attr->info.local_variable_table_attr.table_length, value_cnt);
	}
	return sf;
}


R_API RBinJavaAttrInfo *r_bin_java_enclosing_methods_attr_new(RBinJavaObj *bin, ut8 *buffer, ut64 sz, ut64 buf_offset) {
	ut64 offset = 6;
	if (sz < 8) {
		return NULL;
	}
	RBinJavaAttrInfo *attr = r_bin_java_default_attr_new (bin, buffer, sz, buf_offset);
	if (!attr || sz < 10) {
		free (attr);
		return NULL;
	}
	attr->type = R_BIN_JAVA_ATTR_TYPE_ENCLOSING_METHOD_ATTR;
	attr->info.enclosing_method_attr.class_idx = R_BIN_JAVA_USHORT (buffer, offset);
	offset += 2;
	attr->info.enclosing_method_attr.method_idx = R_BIN_JAVA_USHORT (buffer, offset);
	offset += 2;
	attr->info.enclosing_method_attr.class_name = r_bin_java_get_name_from_bin_cp_list (R_BIN_JAVA_GLOBAL_BIN, attr->info.enclosing_method_attr.class_idx);
	if (attr->info.enclosing_method_attr.class_name == NULL) {
		eprintf ("Could not resolve enclosing class name for the enclosed method.\n");
	}
	attr->info.enclosing_method_attr.method_name = r_bin_java_get_name_from_bin_cp_list (R_BIN_JAVA_GLOBAL_BIN, attr->info.enclosing_method_attr.method_idx);
	if (attr->info.enclosing_method_attr.class_name == NULL) {
		eprintf ("Could not resolve method descriptor for the enclosed method.\n");
	}
	attr->info.enclosing_method_attr.method_descriptor = r_bin_java_get_desc_from_bin_cp_list (R_BIN_JAVA_GLOBAL_BIN, attr->info.enclosing_method_attr.method_idx);
	if (attr->info.enclosing_method_attr.method_name == NULL) {
		eprintf ("Could not resolve method name for the enclosed method.\n");
	}
	attr->size = offset;
	return attr;
}


R_API RList *r_bin_java_find_cp_const_by_val(RBinJavaObj *bin_obj, const ut8 *bytes, ut32 len, const char t) {
	switch (t) {
	case R_BIN_JAVA_CP_UTF8: return r_bin_java_find_cp_const_by_val_utf8 (bin_obj, bytes, len);
	case R_BIN_JAVA_CP_INTEGER: return r_bin_java_find_cp_const_by_val_int (bin_obj, bytes, len);
	case R_BIN_JAVA_CP_FLOAT: return r_bin_java_find_cp_const_by_val_float (bin_obj, bytes, len);
	case R_BIN_JAVA_CP_LONG: return r_bin_java_find_cp_const_by_val_long (bin_obj, bytes, len);
	case R_BIN_JAVA_CP_DOUBLE: return r_bin_java_find_cp_const_by_val_double (bin_obj, bytes, len);
	case R_BIN_JAVA_CP_UNKNOWN:
	default:
		eprintf ("Failed to perform the search for: %s\n", bytes);
		return r_list_new ();
	}
}


R_API RBinJavaAttrInfo *r_bin_java_default_attr_new(RBinJavaObj *bin, ut8 *buffer, ut64 sz, ut64 buf_offset) {
	// NOTE: this function receives the buffer offset in the original buffer,
	// but the buffer is already point to that particular offset.
	// XXX - all the code that relies on this function should probably be modified
	// so that the original buffer pointer is passed in and then the buffer+buf_offset
	// points to the correct location.
	RBinJavaAttrInfo *attr = R_NEW0 (RBinJavaAttrInfo);
	if (!attr) {
		return NULL;
	}
	RBinJavaAttrMetas *type_info = NULL;
	attr->metas = R_NEW0 (RBinJavaMetaInfo);
	if (!attr->metas) {
		free (attr);
		return NULL;
	}
	attr->is_attr_in_old_format = r_bin_java_is_old_format(bin);
	attr->file_offset = buf_offset;
	attr->name_idx = R_BIN_JAVA_USHORT (buffer, 0);
	attr->length = R_BIN_JAVA_UINT (buffer, 2);
	attr->size = R_BIN_JAVA_UINT (buffer, 2) + 6;
	attr->name = r_bin_java_get_utf8_from_bin_cp_list (R_BIN_JAVA_GLOBAL_BIN, attr->name_idx);
	if (!attr->name) {
		// Something bad has happened
		attr->name = r_str_dup (NULL, "NULL");
		eprintf ("r_bin_java_default_attr_new: Unable to find the name for %d index.\n", attr->name_idx);
	}
	type_info = r_bin_java_get_attr_type_by_name (attr->name);
	attr->metas->ord = (R_BIN_JAVA_GLOBAL_BIN->attr_idx++);
	attr->metas->type_info = (void *) type_info;
	// IFDBG eprintf ("	Addrs for type_info [tag=%d]: 0x%08"PFMT64x"\n", type_val, &attr->metas->type_info);
	return attr;
}


R_API ut64 r_bin_java_fieldref_cp_calc_size(RBinJavaCPTypeObj *obj) {
	ut64 size = 0;
	// tag
	size += 1;
	// obj->info.cp_field.class_idx = R_BIN_JAVA_USHORT (buffer, 1);
	size += 2;
	// obj->info.cp_field.name_and_type_idx = R_BIN_JAVA_USHORT (buffer, 3);
	size += 2;
	return size;
}


R_API void r_bin_java_print_local_variable_attr_summary(RBinJavaLocalVariableAttribute *lvattr) {
	if (!lvattr) {
		eprintf ("Attempting to print an invalid RBinJavaLocalVariableAttribute *.\n");
		return;
	}
	printf ("  Local Variable Attribute offset: 0x%08"PFMT64x "\n", lvattr->file_offset);
	printf ("  Local Variable Attribute start_pc: %d\n", lvattr->start_pc);
	printf ("  Local Variable Attribute Length: %d\n", lvattr->length);
	printf ("  Local Variable Attribute name_idx: %d\n", lvattr->name_idx);
	printf ("  Local Variable Attribute name: %s\n", lvattr->name);
	printf ("  Local Variable Attribute descriptor_idx: %d\n", lvattr->descriptor_idx);
	printf ("  Local Variable Attribute descriptor: %s\n", lvattr->descriptor);
	printf ("  Local Variable Attribute index: %d\n", lvattr->index);
}


R_API ut16 r_bin_java_calculate_class_access_value(const char *access_flags_str) {
	return calculate_access_value (access_flags_str, CLASS_ACCESS_FLAGS);
}


R_API DsoJsonObj *r_bin_java_get_class_info_json(RBinJavaObj *bin) {
	RList *classes = r_bin_java_get_classes (bin);
	DsoJsonObj *interfaces_list = dso_json_list_new ();
	DsoJsonObj *class_info_dict = dso_json_dict_new ();
	RBinClass *class_ = r_list_get_n (classes, 0);

	if (class_) {
		int dummy = 0;
		RListIter *iter;
		RBinClass *class_v = NULL;
		// add access flags like in methods
		bool is_public = ((class_->visibility & R_BIN_JAVA_CLASS_ACC_PUBLIC) != 0);
		bool is_final = ((class_->visibility & R_BIN_JAVA_CLASS_ACC_FINAL) != 0);
		bool is_super = ((class_->visibility & R_BIN_JAVA_CLASS_ACC_SUPER) != 0);
		bool is_interface = ((class_->visibility & R_BIN_JAVA_CLASS_ACC_INTERFACE) != 0);
		bool is_abstract = ((class_->visibility & R_BIN_JAVA_CLASS_ACC_ABSTRACT) != 0);
		bool is_synthetic = ((class_->visibility & R_BIN_JAVA_CLASS_ACC_SYNTHETIC) != 0);
		bool is_annotation = ((class_->visibility & R_BIN_JAVA_CLASS_ACC_ANNOTATION) != 0);
		bool is_enum = ((class_->visibility & R_BIN_JAVA_CLASS_ACC_ENUM) != 0);

		dso_json_dict_insert_str_key_num (class_info_dict, "access_flags", class_->visibility);
		dso_json_dict_insert_str_key_num (class_info_dict, "is_public", is_public);
		dso_json_dict_insert_str_key_num (class_info_dict, "is_final", is_final);
		dso_json_dict_insert_str_key_num (class_info_dict, "is_super", is_super);
		dso_json_dict_insert_str_key_num (class_info_dict, "is_interface", is_interface);
		dso_json_dict_insert_str_key_num (class_info_dict, "is_abstract", is_abstract);
		dso_json_dict_insert_str_key_num (class_info_dict, "is_synthetic", is_synthetic);
		dso_json_dict_insert_str_key_num (class_info_dict, "is_annotation", is_annotation);
		dso_json_dict_insert_str_key_num (class_info_dict, "is_enum", is_enum);
		dso_json_dict_insert_str_key_str (class_info_dict, "name", class_->name);

		if (!class_->super) {
			DsoJsonObj *str = dso_json_str_new ();
			if (dso_json_dict_insert_str_key_obj (class_info_dict, "super", str)) {
				dso_json_str_free (str);
			}
		} else {
			dso_json_dict_insert_str_key_str (class_info_dict, "super", class_->super);
		}

		r_list_foreach (classes, iter, class_v) {
			if (!dummy) {
				dummy++;
				continue;
			}
			// enumerate all interface classes and append them to the interfaces
			if ((class_v->visibility & R_BIN_JAVA_CLASS_ACC_INTERFACE) != 0) {
				dso_json_list_append_str (interfaces_list, class_v->name);
			}
		}
	}
	if (dso_json_dict_insert_str_key_obj (class_info_dict, "interfaces", interfaces_list)) {
		// dso_json_list_free (interfaces_list);
		dso_json_obj_del (interfaces_list);
	}
	r_list_free (classes);
	return class_info_dict;
}


R_API RBinJavaCPTypeObj *r_bin_java_find_cp_ref_info_from_name_and_type(RBinJavaObj *bin, ut16 name_idx, ut16 descriptor_idx) {
	RBinJavaCPTypeObj *obj = r_bin_java_find_cp_name_and_type_info (bin, name_idx, descriptor_idx);
	if (obj) {
		return r_bin_java_find_cp_ref_info (bin, obj->metas->ord);
	}
	return NULL;
}


R_API RList *r_bin_java_get_lib_names(RBinJavaObj *bin) {
	RList *lib_names = r_list_newf (free);
	RListIter *iter;
	RBinJavaCPTypeObj *cp_obj = NULL;
	if (!bin) {
		return lib_names;
	}
	r_list_foreach (bin->cp_list, iter, cp_obj) {
		if (cp_obj && cp_obj->tag == R_BIN_JAVA_CP_CLASS &&
		(bin->cf2.this_class != cp_obj->info.cp_class.name_idx || !is_class_interface (bin, cp_obj))) {
			char *name = r_bin_java_get_item_name_from_bin_cp_list (bin, cp_obj);
			if (name) {
				r_list_append (lib_names, name);
			}
		}
	}
	return lib_names;
}


R_API RList *r_bin_java_get_field_definitions(RBinJavaObj *bin) {
	RBinJavaField *fm_type = NULL;
	RList *the_list = r_list_new ();
	if (!the_list) {
		return NULL;
	}
	RListIter *iter = NULL;
	if (!bin) {
		return the_list;
	}
	r_list_foreach (bin->fields_list, iter, fm_type) {
		char *field_def = r_bin_java_get_field_definition (fm_type);
		// eprintf ("Field def: %s, %s, %s, %s\n", fm_type->name, fm_type->descriptor, fm_type->flags_str, field_def);
		r_list_append (the_list, field_def);
	}
	return the_list;
}


R_API ut64 r_bin_java_line_number_table_attr_calc_size(RBinJavaAttrInfo *attr) {
	ut64 size = 6;
	// RBinJavaLineNumberAttribute *lnattr;
	RListIter *iter;
	// RListIter *iter_tmp;
	if (!attr) {
		return 0LL;
	}
	// r_list_foreach_safe (attr->info.line_number_table_attr.line_number_table, iter, iter_tmp, lnattr) {
	r_list_foreach_iter (attr->info.line_number_table_attr.line_number_table, iter) {
		// lnattr->start_pc = R_BIN_JAVA_USHORT (buffer, offset);
		size += 2;
		// lnattr->line_number = R_BIN_JAVA_USHORT (buffer, offset);
		size += 2;
	}
	return size;
}


R_API RBinJavaCPTypeObj *r_bin_java_methodref_cp_new(RBinJavaObj *bin, ut8 *buffer, ut64 sz) {
	ut8 tag = buffer[0];
	RBinJavaCPTypeObj *obj = NULL;
	int quick_check = 0;
	quick_check = r_bin_java_quick_check (R_BIN_JAVA_CP_METHODREF, tag, sz, "MethodRef");
	if (quick_check > 0) {
		return obj;
	}
	obj = (RBinJavaCPTypeObj *) malloc (sizeof (RBinJavaCPTypeObj));
	if (obj) {
		memset (obj, 0, sizeof (RBinJavaCPTypeObj));
		obj->tag = tag;
		obj->metas = R_NEW0 (RBinJavaMetaInfo);
		obj->metas->type_info = (void *) &R_BIN_JAVA_CP_METAS[tag];
		obj->info.cp_method.class_idx = R_BIN_JAVA_USHORT (buffer, 1);
		obj->info.cp_method.name_and_type_idx = R_BIN_JAVA_USHORT (buffer, 3);
	}
	return obj;
}


R_API RBinJavaField *r_bin_java_read_next_field(RBinJavaObj *bin, const ut64 offset, const ut8 *buffer, const ut64 len) {
	RBinJavaAttrInfo *attr;
	ut32 i, idx;
	ut8 buf[8];
	RBinJavaCPTypeObj *item = NULL;
	const ut8 *f_buf = buffer + offset;
	ut64 adv = 0;
	if (!bin || offset + 8 >= len) {
		return NULL;
	}
	RBinJavaField *field = (RBinJavaField *) R_NEW0 (RBinJavaField);
	if (!field) {
		eprintf ("Unable to allocate memory for field information\n");
		return NULL;
	}
	field->metas = (RBinJavaMetaInfo *) R_NEW0 (RBinJavaMetaInfo);
	if (!field->metas) {
		eprintf ("Unable to allocate memory for meta information\n");
		free (field);
		return NULL;
	}
	memcpy (buf, f_buf, 8);
	field->file_offset = offset;
	field->flags = R_BIN_JAVA_USHORT (buf, 0);
	field->flags_str = retrieve_field_access_string (field->flags);
	field->name_idx = R_BIN_JAVA_USHORT (buf, 2);
	field->descriptor_idx = R_BIN_JAVA_USHORT (buf, 4);
	field->attr_count = R_BIN_JAVA_USHORT (buf, 6);
	field->attributes = r_list_newf (r_bin_java_attribute_free);
	field->type = R_BIN_JAVA_FIELD_TYPE_FIELD;
	adv += 8;
	field->metas->ord = bin->field_idx;

	idx = field->name_idx;
	item = r_bin_java_get_item_from_bin_cp_list (bin, idx);
	field->name = r_bin_java_get_utf8_from_bin_cp_list (bin, (ut32) (field->name_idx));
	IFDBG eprintf ("Field name_idx: %d, which is: ord: %d, name: %s, value: %s\n", idx, item->metas->ord, ((RBinJavaCPTypeMetas *)item->metas->type_info)->name, field->name);
	if (!field->name) {
		field->name = (char *) malloc (21);
		snprintf ((char *) field->name, 20, "sym.field_%08x", field->metas->ord);
		IFDBG eprintf ("r_bin_java_read_next_field: Unable to find the name for 0x%02x index.\n", field->name_idx);
	}
	idx = field->descriptor_idx;
	item = r_bin_java_get_item_from_bin_cp_list (bin, idx);
	field->descriptor = r_bin_java_get_utf8_from_bin_cp_list (bin, (ut32) field->descriptor_idx);
	IFDBG eprintf ("Field descriptor_idx: %d, which is: ord: %d, name: %s, value: %s\n", idx, item->metas->ord, ((RBinJavaCPTypeMetas *)item->metas->type_info)->name, field->descriptor);
	if (!field->descriptor) {
		field->descriptor = r_str_dup (NULL, "NULL");
		IFDBG eprintf ("r_bin_java_read_next_field: Unable to find the descriptor for 0x%02x index.\n", field->descriptor_idx);
	}
	IFDBG eprintf ("Looking for a NameAndType CP with name_idx: %d descriptor_idx: %d\n", field->name_idx, field->descriptor_idx);
	field->field_ref_cp_obj = r_bin_java_find_cp_ref_info_from_name_and_type (bin, field->name_idx, field->descriptor_idx);
	if (field->field_ref_cp_obj) {
		IFDBG eprintf ("Found the obj.\n");
		item = r_bin_java_get_item_from_bin_cp_list (bin, field->field_ref_cp_obj->info.cp_field.class_idx);
		IFDBG eprintf ("Field class reference value: %d, which is: ord: %d, name: %s\n", field->field_ref_cp_obj->info.cp_field.class_idx, item->metas->ord, ((RBinJavaCPTypeMetas *)item->metas->type_info)->name);
		field->class_name = r_bin_java_get_item_name_from_bin_cp_list (bin, item);
		IFDBG eprintf ("Field requesting ref_cp_obj the following which is: ord: %d, name: %s\n", field->field_ref_cp_obj->metas->ord, ((RBinJavaCPTypeMetas *)field->field_ref_cp_obj->metas->type_info)->name);
		IFDBG eprintf ("FieldRef class name resolves to: %s\n", field->class_name);
		if (!field->class_name) {
			field->class_name = r_str_dup (NULL, "NULL");
		}
	} else {
		// XXX - default to this class?
		field->field_ref_cp_obj = r_bin_java_get_item_from_bin_cp_list (bin, bin->cf2.this_class);
		field->class_name = r_bin_java_get_item_name_from_bin_cp_list (bin, field->field_ref_cp_obj);
	}
	IFDBG eprintf ("Parsing %s(%s)", field->name, field->descriptor);
	if (field->attr_count > 0) {
		field->attr_offset = adv + offset;
		for (i = 0; i < field->attr_count && offset + adv < len; i++) {
			attr = r_bin_java_read_next_attr (bin, offset + adv, buffer, len);
			if (!attr) {
				eprintf ("[X] r_bin_java: Error unable to parse remainder of classfile after Field Attribute: %d.\n", i);
				free (field->metas);
				free (field);
				return NULL;
			}
			if ((r_bin_java_get_attr_type_by_name (attr->name))->type == R_BIN_JAVA_ATTR_TYPE_CODE_ATTR) {
				// This is necessary for determing the appropriate number of bytes when readin
				// uoffset, ustack, ulocalvar values
				bin->cur_method_code_length = attr->info.code_attr.code_length;
				bin->offset_sz = 2;// (attr->info.code_attr.code_length > 65535) ? 4 : 2;
				bin->ustack_sz = 2;// (attr->info.code_attr.max_stack > 65535) ? 4 : 2;
				bin->ulocalvar_sz = 2;// (attr->info.code_attr.max_locals > 65535) ? 4 : 2;
			}
			r_list_append (field->attributes, attr);
			adv += attr->size;
			if (adv + offset >= len) {
				eprintf ("[X] r_bin_java: Error unable to parse remainder of classfile after Field Attribute: %d.\n", i);
				r_bin_java_fmtype_free (field);
				return NULL;
			}
		}
	}
	field->size = adv;
	return field;
}


R_API ut64 r_bin_java_annotation_default_attr_calc_size(RBinJavaAttrInfo *attr) {
	ut64 size = 0;
	if (attr) {
		// attr = r_bin_java_default_attr_new (buffer, sz, buf_offset);
		size += 6;
		// attr->info.annotation_default_attr.default_value = r_bin_java_element_value_new (buffer+offset, sz-offset, buf_offset+offset);
		size += r_bin_java_element_value_calc_size (attr->info.annotation_default_attr.default_value);
	}
	return size;
}


R_API bool sdb_iterate_build_list(void *user, const char *k, const char *v) {
	RList *bin_objs_list = (RList *) user;
	size_t value = (size_t) sdb_atoi (v);
	RBinJavaObj *bin_obj = NULL;
	IFDBG eprintf ("Found %s == %"PFMT64x " bin_objs db\n", k, (ut64) value);
	if (value != 0 && value != (size_t) -1) {
		bin_obj = (RBinJavaObj *) value;
		r_list_append (bin_objs_list, bin_obj);
	}
	return true;
}


R_API ut64 r_bin_java_get_main(RBinJavaObj *bin) {
	if (bin->main_code_attr) {
		return bin->main_code_attr->info.code_attr.code_offset + bin->loadaddr;
	}
	return 0;
}


R_API void r_bin_java_print_synthetic_attr_summary(RBinJavaAttrInfo *attr) {
	if (attr == NULL) {
		eprintf ("Attempting to print an invalid RBinJavaAttrInfo *Synthetic.\n");
		return;
	}
	printf ("Synthetic Attribute Information:\n");
	printf ("  Attribute Offset: 0x%08"PFMT64x "\n", attr->file_offset);
	printf ("  Attribute Name Index: %d (%s)\n", attr->name_idx, attr->name);
	printf ("  Attribute Length: %d\n", attr->length);
	printf ("  Attribute Index: %d\n", attr->info.source_file_attr.sourcefile_idx);
}


R_API RList *r_bin_java_enum_class_fields(RBinJavaObj *bin, ut16 class_idx) {
	RList *fields = r_list_newf (free);
	RListIter *iter;
	RBinJavaField *fm_type;
	RBinField *field = NULL;
	r_list_foreach (bin->fields_list, iter, fm_type) {
		if (fm_type) {
			if (fm_type && fm_type->field_ref_cp_obj
			&& fm_type->field_ref_cp_obj->metas->ord == class_idx) {
				field = r_bin_java_create_new_rbinfield_from_field (fm_type, bin->loadaddr);
				if (field) {
					r_list_append (fields, field);
				}
			}
		}
	}
	return fields;
}


R_API ut16 r_bin_java_calculate_field_access_value(const char *access_flags_str) {
	return calculate_access_value (access_flags_str, FIELD_ACCESS_FLAGS);
}


R_API ut64 r_bin_java_element_pair_calc_size(RBinJavaElementValuePair *evp) {
	ut64 sz = 2;
	if (evp && evp->value) {
		// evp->element_name_idx = r_bin_java_read_short(bin, bin->b->cur);
		// evp->value = r_bin_java_element_value_new (bin, offset+2);
		sz += r_bin_java_element_value_calc_size (evp->value);
	}
	return sz;
}


R_API ut64 r_bin_java_get_method_start(RBinJavaObj *bin, RBinJavaField *fm_type) {
	return r_bin_java_get_method_code_offset (fm_type) + bin->loadaddr;
}


R_API ut64 r_bin_java_deprecated_attr_calc_size(RBinJavaAttrInfo *attr) {
	return attr ? 6 : 0;
}


R_API bool r_bin_java_is_old_format(RBinJavaObj *bin) {
	return bin->cf.major[1] == 45 && bin->cf.minor[1] <= 2;
}


R_API ut8 *r_bin_java_cp_get_utf8(ut8 tag, ut32 *out_sz, const ut8 *buf, const ut64 len) {
	ut8 *buffer = NULL;
	ut16 sz = 0;
	ut16 t = (ut16) len;
	if (len > 0 && len > (ut16) - 1) {
		*out_sz = 0;
		return NULL;
	}
	sz = R_BIN_JAVA_USHORT (((ut8 *) (ut16 *) &t), 0);
	*out_sz = 3 + t; // tag + sz + bytes
	buffer = malloc (*out_sz + 3);
	if (!buffer) {
		return NULL;
	}
	// XXX - excess bytes are created to ensure null for string operations.
	memset (buffer, 0, *out_sz + 3);
	buffer[0] = tag;
	memcpy (buffer + 1, (const char *) &sz, 2);
	memcpy (buffer + 3, buf, *out_sz - 3);
	return buffer;
}


R_API char *r_bin_java_print_long_cp_stringify(RBinJavaCPTypeObj *obj) {
	ut32 size = 255, consumed = 0;
	char *value = malloc (size);
	if (value) {
		memset (value, 0, size);
		consumed = snprintf (value, size, "%d.0x%04"PFMT64x ".%s.0x%08"PFMT64x "",
			obj->metas->ord,
			obj->file_offset,
			((RBinJavaCPTypeMetas *) obj->metas->type_info)->name,
			r_bin_java_raw_to_long (obj->info.cp_long.bytes.raw, 0));
		if (consumed >= size - 1) {
			free (value);
			size += size >> 1;
			value = malloc (size);
			if (value) {
				memset (value, 0, size);
				(void)snprintf (value, size, "%d.0x%04"PFMT64x ".%s.0x%08"PFMT64x "",
					obj->metas->ord,
					obj->file_offset,
					((RBinJavaCPTypeMetas *) obj->metas->type_info)->name,
					r_bin_java_raw_to_long (obj->info.cp_long.bytes.raw, 0));
			}
		}
	}
	return value;
}


R_API void r_bin_java_line_number_table_attr_free(void /*RBinJavaAttrInfo*/ *a) {
	RBinJavaAttrInfo *attr = a;
	if (attr) {
		free (attr->name);
		free (attr->metas);
		r_list_free (attr->info.line_number_table_attr.line_number_table);
		free (attr);
	}
}


R_API RBinJavaBootStrapArgument *r_bin_java_bootstrap_method_argument_new(ut8 *buffer, ut64 sz, ut64 buf_offset) {
	ut64 offset = 0;
	RBinJavaBootStrapArgument *bsm_arg = (RBinJavaBootStrapArgument *) malloc (sizeof (RBinJavaBootStrapArgument));
	if (!bsm_arg) {
		// TODO eprintf failed to allocate bytes for bootstrap_method.
		return bsm_arg;
	}
	memset (bsm_arg, 0, sizeof (RBinJavaBootStrapArgument));
	bsm_arg->file_offset = buf_offset;
	bsm_arg->argument_info_idx = R_BIN_JAVA_USHORT (buffer, offset);
	offset += 2;
	bsm_arg->argument_info_cp_obj = r_bin_java_clone_cp_idx (R_BIN_JAVA_GLOBAL_BIN, bsm_arg->argument_info_idx);
	bsm_arg->size = offset;
	return bsm_arg;
}


R_API ut64 r_bin_java_string_cp_calc_size(RBinJavaCPTypeObj *obj) {
	ut64 size = 0;
	// tag
	size += 1;
	// obj->info.cp_string.string_idx = R_BIN_JAVA_USHORT (buffer, 1);
	size += 2;
	return size;
}


R_API void U(copy_type_info_to_stack_frame_list)(RList * type_list, RList * sf_list) {
	RListIter *iter, *iter_tmp;
	RBinJavaVerificationObj *ver_obj, *new_ver_obj;
	if (!type_list || !sf_list) {
		return;
	}
	r_list_foreach_safe (type_list, iter, iter_tmp, ver_obj) {
		new_ver_obj = (RBinJavaVerificationObj *) malloc (sizeof (RBinJavaVerificationObj));
		// FIXME: how to handle failed memory allocation?
		if (new_ver_obj && ver_obj) {
			memcpy (new_ver_obj, ver_obj, sizeof (RBinJavaVerificationObj));
			if (!r_list_append (sf_list, (void *) new_ver_obj)) {
				R_FREE (new_ver_obj);
			}
		} else {
			R_FREE (new_ver_obj);
		}
	}
}


R_API void r_bin_java_print_utf8_cp_summary(RBinJavaCPTypeObj *obj) {
	if (!obj) {
		eprintf ("Attempting to print an invalid RBinJavaCPTypeObj*  Utf8.\n");
		return;
	}
	char *str = convert_string ((const char *) obj->info.cp_utf8.bytes, obj->info.cp_utf8.length);
	eprintf ("UTF8 ConstantPool Type (%d) ", obj->metas->ord);
	eprintf ("	Offset: 0x%08"PFMT64x"", obj->file_offset);
	eprintf ("	length = %d\n", obj->info.cp_utf8.length);
	eprintf ("	utf8 = %s\n", str);
	free (str);
}


R_API void r_bin_java_print_deprecated_attr_summary(RBinJavaAttrInfo *attr) {
	if (!attr) {
		eprintf ("Attempting to print an invalid RBinJavaAttrInfo *Deperecated.\n");
		return;
	}
	printf ("Deperecated Attribute Information:\n");
	printf ("  Attribute Offset: 0x%08"PFMT64x "\n", attr->file_offset);
	printf ("  Attribute Name Index: %d (%s)\n", attr->name_idx, attr->name);
	printf ("  Attribute Length: %d\n", attr->length);
}


R_API void r_bin_java_print_bootstrap_method_summary(RBinJavaBootStrapMethod *bsm) {
	RBinJavaBootStrapArgument *bsm_arg = NULL;
	RListIter *iter = NULL, *iter_tmp = NULL;
	if (!bsm) {
		eprintf ("Attempting to print an invalid RBinJavaBootStrapArgument *.\n");
		return;
	}
	eprintf ("Bootstrap Method Information:\n");
	eprintf ("	Offset: 0x%08"PFMT64x"", bsm->file_offset);
	eprintf ("	Method Reference Index = (0x%02x)\n", bsm->bootstrap_method_ref);
	eprintf ("	Number of Method Arguments = (0x%02x)\n", bsm->num_bootstrap_arguments);
	if (bsm->bootstrap_arguments) {
		r_list_foreach_safe (bsm->bootstrap_arguments, iter, iter_tmp, bsm_arg) {
			if (bsm_arg) {
				r_bin_java_print_bootstrap_method_argument_summary (bsm_arg);
			}
		}
	} else {
		eprintf ("	Bootstrap Method Argument: NONE \n");
	}
}


R_API RBinJavaStackMapFrameMetas *r_bin_java_determine_stack_frame_type(ut8 tag) {
	ut8 type_value = 0;
	if (tag < 64) {
		type_value = R_BIN_JAVA_STACK_FRAME_SAME;
	} else if (tag < 128) {
		type_value = R_BIN_JAVA_STACK_FRAME_SAME_LOCALS_1;
	} else if (247 < tag && tag < 251) {
		type_value = R_BIN_JAVA_STACK_FRAME_CHOP;
	} else if (tag == 251) {
		type_value = R_BIN_JAVA_STACK_FRAME_SAME_FRAME_EXTENDED;
	} else if (251 < tag && tag < 255) {
		type_value = R_BIN_JAVA_STACK_FRAME_APPEND;
	} else if (tag == 255) {
		type_value = R_BIN_JAVA_STACK_FRAME_FULL_FRAME;
	} else {
		type_value = R_BIN_JAVA_STACK_FRAME_RESERVED;
	}
	return &R_BIN_JAVA_STACK_MAP_FRAME_METAS[type_value];
}


R_API ut64 rbin_java_verification_info_calc_size(RBinJavaVerificationObj *se) {
	ut64 sz = 1;
	if (!se) {
		return 0;
	}
	// r_buf_read_at (bin->b, offset, (ut8*)(&se->tag), 1)
	switch (se->tag) {
	case R_BIN_JAVA_STACKMAP_OBJECT:
		// r_buf_read_at (bin->b, offset+1, (ut8*)buf, 2)
		sz += 2;
		break;
	case R_BIN_JAVA_STACKMAP_UNINIT:
		// r_buf_read_at (bin->b, offset+1, (ut8*)buf, 2)
		sz += 2;
		break;
	}
	return sz;
}


R_API void r_bin_java_local_variable_type_table_attr_free(void /*RBinJavaAttrInfo*/ *a) {
	RBinJavaAttrInfo *attr = a;
	if (attr) {
		free (attr->name);
		free (attr->metas);
		r_list_free (attr->info.local_variable_type_table_attr.local_variable_table);
		free (attr);
	}
}


R_API void r_bin_java_classes_free(void /*RBinClass*/ *k) {
	RBinClass *klass = k;
	if (klass) {
		r_list_free (klass->methods);
		r_list_free (klass->fields);
		free (klass->name);
		free (klass->super);
		free (klass->visibility_str);
		free (klass);
	}
}


R_API void r_bin_java_print_source_code_file_attr_summary(RBinJavaAttrInfo *attr) {
	if (!attr) {
		eprintf ("Attempting to print an invalid RBinJavaAttrInfo *SourceFile.\n");
		return;
	}
	printf ("Source File Attribute Information:\n");
	printf ("  Attribute Offset: 0x%08"PFMT64x "\n", attr->file_offset);
	printf ("  Attribute Name Index: %d (%s)\n", attr->name_idx, attr->name);
	printf ("  Attribute Length: %d\n", attr->length);
	printf ("  Source File Index: %d\n", attr->info.source_file_attr.sourcefile_idx);
}


R_API void r_bin_java_print_integer_cp_summary(RBinJavaCPTypeObj *obj) {
	ut8 *b = NULL;
	if (obj == NULL) {
		eprintf ("Attempting to print an invalid RBinJavaCPTypeObj*  Integer.\n");
		return;
	}
	b = obj->info.cp_integer.bytes.raw;
	eprintf ("Integer ConstantPool Type (%d) ", obj->metas->ord);
	eprintf ("	Offset: 0x%08"PFMT64x"", obj->file_offset);
	eprintf ("	bytes = %02x %02x %02x %02x\n", b[0], b[1], b[2], b[3]);
	eprintf ("	integer = %d\n", R_BIN_JAVA_UINT (obj->info.cp_integer.bytes.raw, 0));
}


R_API RBinJavaStackMapFrame *r_bin_java_build_stack_frame_from_local_variable_table(RBinJavaObj *bin, RBinJavaAttrInfo *attr) {
	RBinJavaStackMapFrame *sf = r_bin_java_default_stack_frame ();
	RBinJavaLocalVariableAttribute *lvattr = NULL;
	RBinJavaVerificationObj *type_item;
	RListIter *iter = NULL;
	ut32 value_cnt = 0;
	ut8 value;
	if (!sf || !bin || !attr || attr->type != R_BIN_JAVA_ATTR_TYPE_LOCAL_VARIABLE_TABLE_ATTR) {
		eprintf ("Attempting to create a stack_map frame from a bad attribute.\n");
		return sf;
	}
	sf->number_of_locals = attr->info.local_variable_table_attr.table_length;
	r_list_foreach (attr->info.local_variable_table_attr.local_variable_table, iter, lvattr) {
		ut32 pos = 0;
		// knock the array Types
		while (lvattr->descriptor[pos] == '[') {
			pos++;
		}
		value = lvattr->descriptor[pos];
		// IFDBG eprintf ("Found the following type value: %c at pos %d in %s\n", value, pos, lvattr->descriptor);
		switch (value) {
		case 'I':
		case 'Z':
		case 'S':
		case 'B':
		case 'C':
			type_item = r_bin_java_verification_info_from_type (bin, R_BIN_JAVA_STACKMAP_INTEGER, 0);
			break;
		case 'F':
			type_item = r_bin_java_verification_info_from_type (bin, R_BIN_JAVA_STACKMAP_FLOAT, 0);
			break;
		case 'D':
			type_item = r_bin_java_verification_info_from_type (bin, R_BIN_JAVA_STACKMAP_DOUBLE, 0);
			break;
		case 'J':
			type_item = r_bin_java_verification_info_from_type (bin, R_BIN_JAVA_STACKMAP_LONG, 0);
			break;
		case 'L':
			// TODO: FIXME write something that will iterate over the CP Pool and find the
			// CONSTANT_Class_info referencing this
		{
			ut16 idx = r_bin_java_find_cp_class_ref_from_name_idx (bin, lvattr->name_idx);
			type_item = r_bin_java_verification_info_from_type (bin, R_BIN_JAVA_STACKMAP_OBJECT, idx);
		}
		break;
		default:
			eprintf ("r_bin_java_build_stack_frame_from_local_variable_table: "
				"not sure how to handle: name: %s, type: %s\n", lvattr->name, lvattr->descriptor);
			type_item = r_bin_java_verification_info_from_type (bin, R_BIN_JAVA_STACKMAP_NULL, 0);
		}
		if (type_item) {
			r_list_append (sf->local_items, (void *) type_item);
		}
		value_cnt++;
	}
	if (value_cnt != attr->info.local_variable_table_attr.table_length) {
		IFDBG eprintf("r_bin_java_build_stack_frame_from_local_variable_table: "
		"Number of locals not accurate.  Expected %d but got %d",
		attr->info.local_variable_table_attr.table_length, value_cnt);
	}
	return sf;
}


R_API char *r_bin_java_get_item_name_from_cp_item_list(RList *cp_list, RBinJavaCPTypeObj *obj, int depth) {
	/*
	Given a constant poool object Class, FieldRef, MethodRef, or InterfaceMethodRef
	return the actual descriptor string.
	@param cp_list: RList of RBinJavaCPTypeObj *
	@param obj object to look up the name for
	@rvalue ut8* (user frees) or NULL
	*/
	if (obj == NULL || cp_list == NULL || depth < 0) {
		return NULL;
	}
	switch (obj->tag) {
	case R_BIN_JAVA_CP_NAMEANDTYPE:
		return r_bin_java_get_utf8_from_cp_item_list (
			cp_list, obj->info.cp_name_and_type.name_idx);
	case R_BIN_JAVA_CP_CLASS:
		return r_bin_java_get_utf8_from_cp_item_list (
			cp_list, obj->info.cp_class.name_idx);
	// XXX - Probably not good form, but they are the same memory structure
	case R_BIN_JAVA_CP_FIELDREF:
	case R_BIN_JAVA_CP_INTERFACEMETHOD_REF:
	case R_BIN_JAVA_CP_METHODREF:
		obj = r_bin_java_get_item_from_cp_item_list (
			cp_list, obj->info.cp_method.name_and_type_idx);
		return r_bin_java_get_item_name_from_cp_item_list (
			cp_list, obj, depth - 1);
	default:
		return NULL;
	case 0:
		IFDBG eprintf("Invalid 0 tag in the constant pool\n");
		return NULL;
	}
	return NULL;
}


R_API RBinJavaCPTypeObj *r_bin_java_read_next_constant_pool_item(RBinJavaObj *bin, const ut64 offset, const ut8 *buf, ut64 len) {
	RBinJavaCPTypeMetas *java_constant_info = NULL;
	ut8 tag = 0;
	ut64 buf_sz = 0;
	ut8 *cp_buf = NULL;
	ut32 str_len = 0;
	RBinJavaCPTypeObj *java_obj = NULL;
	tag = buf[offset];
	if (tag > R_BIN_JAVA_CP_METAS_SZ) {
		eprintf ("Invalid tag '%d' at offset 0x%08"PFMT64x "\n", tag, (ut64) offset);
		return NULL;
#if 0
		java_obj = r_bin_java_unknown_cp_new (bin, &tag, 1);
		if (java_obj != NULL && java_obj->metas != NULL) {
			java_obj->file_offset = offset;
			java_obj->loadaddr = bin->loadaddr;
		}
		return NULL; // early error to avoid future overflows
		// return java_obj;
#endif
	}
	java_constant_info = &R_BIN_JAVA_CP_METAS[tag];
	if (java_constant_info->tag == 0 || java_constant_info->tag == 2) {
		return java_obj;
	}
	buf_sz += java_constant_info->len;
	if (java_constant_info->tag == 1) {
		if (offset + 32 < len) {
			str_len = R_BIN_JAVA_USHORT (buf, offset + 1);
			buf_sz += str_len;
		} else {
			return NULL;
		}
	}
	cp_buf = calloc (buf_sz, 1);
	if (!cp_buf) {
		return java_obj;
	}
	if (offset + buf_sz < len) {
		memcpy (cp_buf, (ut8 *) buf + offset, buf_sz);
		IFDBG eprintf("Parsed the tag '%d':%s and create object from offset 0x%08"PFMT64x ".\n", tag, R_BIN_JAVA_CP_METAS[tag].name, offset);
		java_obj = (*java_constant_info->allocs->new_obj)(bin, cp_buf, buf_sz);
		if (java_obj != NULL && java_obj->metas != NULL) {
			java_obj->file_offset = offset;
			// IFDBG eprintf ("java_obj->file_offset = 0x%08"PFMT64x".\n",java_obj->file_offset);
		} else if (!java_obj) {
			eprintf ("Unable to parse the tag '%d' and create valid object.\n", tag);
		} else if (!java_obj->metas) {
			eprintf ("Unable to parse the tag '%d' and create valid object.\n", tag);
		} else {
			eprintf ("Failed to set the java_obj->metas-file_offset for '%d' offset is(0x%08"PFMT64x ").\n", tag, offset);
		}
	}
	free (cp_buf);
	return java_obj;
}


R_API void r_bin_java_print_stack_map_table_attr_summary(RBinJavaAttrInfo *attr) {
	RListIter *iter, *iter_tmp;
	RList *ptrList;
	RBinJavaStackMapFrame *frame;
	if (attr == NULL) {
		eprintf ("Attempting to print an invalid RBinJavaStackMapTableAttr*  .\n");
		return;
	}
	Eprintf ("StackMapTable Attribute Information:\n");
	Eprintf ("  Attribute Offset: 0x%08"PFMT64x "\n", attr->file_offset);
	Eprintf ("  Attribute Name Index: %d (%s)\n", attr->name_idx, attr->name);
	Eprintf ("  Attribute Length: %d\n", attr->length);
	Eprintf ("  StackMapTable Method Code Size: 0x%08x\n", attr->info.stack_map_table_attr.code_size);
	Eprintf ("  StackMapTable Frame Entries: 0x%08x\n", attr->info.stack_map_table_attr.number_of_entries);
	Eprintf ("  StackMapTable Frames:\n");
	ptrList = attr->info.stack_map_table_attr.stack_map_frame_entries;
	if (ptrList) {
		r_list_foreach_safe (ptrList, iter, iter_tmp, frame) {
			r_bin_java_print_stack_map_frame_summary (frame);
		}
	}
}


R_API void r_bin_java_print_verification_info_summary(RBinJavaVerificationObj *obj) {
	ut8 tag_value = R_BIN_JAVA_STACKMAP_UNKNOWN;
	if (obj == NULL) {
		eprintf ("Attempting to print an invalid RBinJavaVerificationObj*  .\n");
		return;
	}
	if (obj->tag < R_BIN_JAVA_STACKMAP_UNKNOWN) {
		tag_value = obj->tag;
	}
	Eprintf ("Verification Information\n");
	Eprintf ("  Offset: 0x%08"PFMT64x "", obj->file_offset);
	Eprintf ("  Tag Value = 0x%02x\n", obj->tag);
	Eprintf ("  Name = %s\n", R_BIN_JAVA_VERIFICATION_METAS[tag_value].name);
	if (obj->tag == R_BIN_JAVA_STACKMAP_OBJECT) {
		Eprintf ("  Object Constant Pool Index = 0x%x\n", obj->info.obj_val_cp_idx);
	} else if (obj->tag == R_BIN_JAVA_STACKMAP_UNINIT) {
		Eprintf ("  Uninitialized Object offset in code = 0x%x\n", obj->info.uninit_offset);
	}
}


R_API void r_bin_java_print_synthetic_attr_summary(RBinJavaAttrInfo *attr) {
	if (attr == NULL) {
		eprintf ("Attempting to print an invalid RBinJavaAttrInfo *Synthetic.\n");
		return;
	}
	Eprintf ("Synthetic Attribute Information:\n");
	Eprintf ("  Attribute Offset: 0x%08"PFMT64x "\n", attr->file_offset);
	Eprintf ("  Attribute Name Index: %d (%s)\n", attr->name_idx, attr->name);
	Eprintf ("  Attribute Length: %d\n", attr->length);
	Eprintf ("  Attribute Index: %d\n", attr->info.source_file_attr.sourcefile_idx);
}


R_API double my_pow(ut64 base, int exp) {
	ut8 flag = 0;
	ut64 res = 1;
	if (exp < 0) {
		flag = 1;
		exp *= -1;
	}
	while (exp) {
		if (exp & 1) {
			res *= base;
		}
		exp >>= 1;
		base *= base;
		IFDBG eprintf("Result: %"PFMT64d ", base: %"PFMT64d ", exp: %d\n", res, base, exp);
	}
	if (flag == 0) {
		return 1.0 * res;
	}
	return (1.0 / res);
}


static pyc_object *get_ascii_object(RBuffer *buffer) {
	bool error = false;
	ut32 n = get_ut32 (buffer, &error);
	if (error) {
		return NULL;
	}
	return get_ascii_object_generic (buffer, n, true);
}


R_API void r_io_bank_del(RIO *io, const ut32 bankid) {
	r_id_storage_delete (io->banks, bankid);
	if (io->bank == bankid) {
		io->bank = r_io_bank_first (io);
	}
}


static bool _bank_free_cb(void *user, void *data, ut32 id) {
	r_io_bank_free ((RIOBank *)data);
	return true;
}


R_API bool r_crbtree_delete(RRBTree *tree, void *data, RRBComparator cmp, void *user) {
	r_return_val_if_fail (tree && data && tree->size && tree->root && cmp, false);
	data = r_crbtree_take (tree, data, cmp, user);
	if (tree->free) {
		tree->free (data);
	}
	return !!data;
}


R_API RRBNode *r_rbnode_prev(RRBNode *node) {
	r_return_val_if_fail (node, NULL);
	if (node->link[0]) {
		node = node->link[0];
		while (node->link[1]) {
			node = node->link[1];
		}
		return node;
	}
	RRBNode *parent = node->parent;
	while (parent && parent->link[0] == node) {
		node = parent;
		parent = node->parent;
	}
	return parent;
}


R_API void r_io_bank_free(RIOBank *bank) {
	if (bank) {
		r_queue_free (bank->todo);
		r_list_free (bank->maprefs);
		r_crbtree_free (bank->submaps);
		free (bank->name);
		free (bank);
	}
}


R_API bool r_io_bank_add(RIO *io, RIOBank *bank) {
	r_return_val_if_fail (io && io->banks && bank, false);
	return r_id_storage_add (io->banks, bank, &bank->id);
}


R_API void r_io_bank_clear(RIOBank *bank) {
	r_return_if_fail (bank);
	while (!r_queue_is_empty (bank->todo)) {
		free (r_queue_dequeue (bank->todo));
	}
	bank->last_used = NULL;
	r_crbtree_clear (bank->submaps);
	r_list_purge (bank->maprefs);
}


static int composite_ep0_queue(struct usb_composite_dev *cdev,
		struct usb_request *req, gfp_t gfp_flags)
{
	int ret;

	ret = usb_ep_queue(cdev->gadget->ep0, req, gfp_flags);
	if (ret == 0) {
		if (cdev->req == req)
			cdev->setup_pending = true;
		else if (cdev->os_desc_req == req)
			cdev->os_desc_pending = true;
		else
			WARN(1, "unknown request %p\n", req);
	}

	return ret;
}


static int set_config(struct usb_composite_dev *cdev,
		const struct usb_ctrlrequest *ctrl, unsigned number)
{
	struct usb_gadget	*gadget = cdev->gadget;
	struct usb_configuration *c = NULL;
	int			result = -EINVAL;
	unsigned		power = gadget_is_otg(gadget) ? 8 : 100;
	int			tmp;

	if (number) {
		list_for_each_entry(c, &cdev->configs, list) {
			if (c->bConfigurationValue == number) {
				/*
				 * We disable the FDs of the previous
				 * configuration only if the new configuration
				 * is a valid one
				 */
				if (cdev->config)
					reset_config(cdev);
				result = 0;
				break;
			}
		}
		if (result < 0)
			goto done;
	} else { /* Zero configuration value - need to reset the config */
		if (cdev->config)
			reset_config(cdev);
		result = 0;
	}

	DBG(cdev, "%s config #%d: %s\n",
	    usb_speed_string(gadget->speed),
	    number, c ? c->label : "unconfigured");

	if (!c)
		goto done;

	usb_gadget_set_state(gadget, USB_STATE_CONFIGURED);
	cdev->config = c;

	/* Initialize all interfaces by setting them to altsetting zero. */
	for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) {
		struct usb_function	*f = c->interface[tmp];
		struct usb_descriptor_header **descriptors;

		if (!f)
			break;

		/*
		 * Record which endpoints are used by the function. This is used
		 * to dispatch control requests targeted at that endpoint to the
		 * function's setup callback instead of the current
		 * configuration's setup callback.
		 */
		descriptors = function_descriptors(f, gadget->speed);

		for (; *descriptors; ++descriptors) {
			struct usb_endpoint_descriptor *ep;
			int addr;

			if ((*descriptors)->bDescriptorType != USB_DT_ENDPOINT)
				continue;

			ep = (struct usb_endpoint_descriptor *)*descriptors;
			addr = ((ep->bEndpointAddress & 0x80) >> 3)
			     |  (ep->bEndpointAddress & 0x0f);
			set_bit(addr, f->endpoints);
		}

		result = f->set_alt(f, tmp, 0);
		if (result < 0) {
			DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n",
					tmp, f->name, f, result);

			reset_config(cdev);
			goto done;
		}

		if (result == USB_GADGET_DELAYED_STATUS) {
			DBG(cdev,
			 "%s: interface %d (%s) requested delayed status\n",
					__func__, tmp, f->name);
			cdev->delayed_status++;
			DBG(cdev, "delayed_status count %d\n",
					cdev->delayed_status);
		}
	}

	/* when we return, be sure our power usage is valid */
	if (c->MaxPower || (c->bmAttributes & USB_CONFIG_ATT_SELFPOWER))
		power = c->MaxPower;
	else
		power = CONFIG_USB_GADGET_VBUS_DRAW;

	if (gadget->speed < USB_SPEED_SUPER)
		power = min(power, 500U);
	else
		power = min(power, 900U);
done:
	if (power <= USB_SELF_POWER_VBUS_MAX_DRAW)
		usb_gadget_set_selfpowered(gadget);
	else
		usb_gadget_clear_selfpowered(gadget);

	usb_gadget_vbus_draw(gadget, power);
	if (result >= 0 && cdev->delayed_status)
		result = USB_GADGET_DELAYED_STATUS;
	return result;
}


int usb_add_config_only(struct usb_composite_dev *cdev,
		struct usb_configuration *config)
{
	struct usb_configuration *c;

	if (!config->bConfigurationValue)
		return -EINVAL;

	/* Prevent duplicate configuration identifiers */
	list_for_each_entry(c, &cdev->configs, list) {
		if (c->bConfigurationValue == config->bConfigurationValue)
			return -EBUSY;
	}

	config->cdev = cdev;
	list_add_tail(&config->list, &cdev->configs);

	INIT_LIST_HEAD(&config->functions);
	config->next_interface_id = 0;
	memset(config->interface, 0, sizeof(config->interface));

	return 0;
}


test_gui_tabline_event(dict_T *args UNUSED)
{
#  ifdef FEAT_GUI_TABLINE
    int		tabnr;

    if (dict_find(args, (char_u *)"tabnr", -1) == NULL)
	return FALSE;

    tabnr = (int)dict_get_number(args, (char_u *)"tabnr");

    return send_tabline_event(tabnr);
#  else
    return FALSE;
#  endif
}


f_assert_inrange(typval_T *argvars, typval_T *rettv)
{
    if (check_for_float_or_nr_arg(argvars, 0) == FAIL
	    || check_for_float_or_nr_arg(argvars, 1) == FAIL
	    || check_for_float_or_nr_arg(argvars, 2) == FAIL
	    || check_for_opt_string_arg(argvars, 3) == FAIL)
	return;

    rettv->vval.v_number = assert_inrange(argvars);
}


ga_concat_shorten_esc(garray_T *gap, char_u *str)
{
    char_u  *p;
    char_u  *s;
    int	    c;
    int	    clen;
    char_u  buf[NUMBUFLEN];
    int	    same_len;

    if (str == NULL)
    {
	ga_concat(gap, (char_u *)"NULL");
	return;
    }

    for (p = str; *p != NUL; ++p)
    {
	same_len = 1;
	s = p;
	c = mb_cptr2char_adv(&s);
	clen = s - p;
	while (*s != NUL && c == mb_ptr2char(s))
	{
	    ++same_len;
	    s += clen;
	}
	if (same_len > 20)
	{
	    ga_concat(gap, (char_u *)"\\[");
	    ga_concat_esc(gap, p, clen);
	    ga_concat(gap, (char_u *)" occurs ");
	    vim_snprintf((char *)buf, NUMBUFLEN, "%d", same_len);
	    ga_concat(gap, buf);
	    ga_concat(gap, (char_u *)" times]");
	    p = s - 1;
	}
	else
	    ga_concat_esc(gap, p, clen);
    }
}


f_test_override(typval_T *argvars, typval_T *rettv UNUSED)
{
    char_u *name = (char_u *)"";
    int     val;
    static int save_starting = -1;

    if (in_vim9script()
	    && (check_for_string_arg(argvars, 0) == FAIL
		|| check_for_number_arg(argvars, 1) == FAIL))
	return;

    if (argvars[0].v_type != VAR_STRING
	    || (argvars[1].v_type) != VAR_NUMBER)
	emsg(_(e_invalid_argument));
    else
    {
	name = tv_get_string(&argvars[0]);
	val = (int)tv_get_number(&argvars[1]);

	if (STRCMP(name, (char_u *)"redraw") == 0)
	    disable_redraw_for_testing = val;
	else if (STRCMP(name, (char_u *)"redraw_flag") == 0)
	    ignore_redraw_flag_for_testing = val;
	else if (STRCMP(name, (char_u *)"char_avail") == 0)
	    disable_char_avail_for_testing = val;
	else if (STRCMP(name, (char_u *)"starting") == 0)
	{
	    if (val)
	    {
		if (save_starting < 0)
		    save_starting = starting;
		starting = 0;
	    }
	    else
	    {
		starting = save_starting;
		save_starting = -1;
	    }
	}
	else if (STRCMP(name, (char_u *)"nfa_fail") == 0)
	    nfa_fail_for_testing = val;
	else if (STRCMP(name, (char_u *)"no_query_mouse") == 0)
	    no_query_mouse_for_testing = val;
	else if (STRCMP(name, (char_u *)"no_wait_return") == 0)
	    no_wait_return = val;
	else if (STRCMP(name, (char_u *)"ui_delay") == 0)
	    ui_delay_for_testing = val;
	else if (STRCMP(name, (char_u *)"term_props") == 0)
	    reset_term_props_on_termresponse = val;
	else if (STRCMP(name, (char_u *)"vterm_title") == 0)
	    disable_vterm_title_for_testing = val;
	else if (STRCMP(name, (char_u *)"uptime") == 0)
	    override_sysinfo_uptime = val;
	else if (STRCMP(name, (char_u *)"autoload") == 0)
	    override_autoload = val;
	else if (STRCMP(name, (char_u *)"ALL") == 0)
	{
	    disable_char_avail_for_testing = FALSE;
	    disable_redraw_for_testing = FALSE;
	    ignore_redraw_flag_for_testing = FALSE;
	    nfa_fail_for_testing = FALSE;
	    no_query_mouse_for_testing = FALSE;
	    ui_delay_for_testing = 0;
	    reset_term_props_on_termresponse = FALSE;
	    override_sysinfo_uptime = -1;
	    if (save_starting >= 0)
	    {
		starting = save_starting;
		save_starting = -1;
	    }
	}
	else
	    semsg(_(e_invalid_argument_str), name);
    }
}


assert_match_common(typval_T *argvars, assert_type_T atype)
{
    garray_T	ga;
    char_u	buf1[NUMBUFLEN];
    char_u	buf2[NUMBUFLEN];
    int		called_emsg_before = called_emsg;
    char_u	*pat;
    char_u	*text;

    if (in_vim9script()
	    && (check_for_string_arg(argvars, 0) == FAIL
		|| check_for_string_arg(argvars, 1) == FAIL
		|| check_for_opt_string_arg(argvars, 2) == FAIL))
	return 1;

    pat = tv_get_string_buf_chk(&argvars[0], buf1);
    text = tv_get_string_buf_chk(&argvars[1], buf2);
    if (called_emsg == called_emsg_before
		 && pattern_match(pat, text, FALSE) != (atype == ASSERT_MATCH))
    {
	prepare_assert_error(&ga);
	fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1],
									atype);
	assert_error(&ga);
	ga_clear(&ga);
	return 1;
    }
    return 0;
}


f_assert_report(typval_T *argvars, typval_T *rettv)
{
    garray_T	ga;

    if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
	return;

    prepare_assert_error(&ga);
    ga_concat(&ga, tv_get_string(&argvars[0]));
    assert_error(&ga);
    ga_clear(&ga);
    rettv->vval.v_number = 1;
}


accountingReportDiff(XML_Parser rootParser,
                     unsigned int levelsAwayFromRootParser, const char *before,
                     const char *after, ptrdiff_t bytesMore, int source_line,
                     enum XML_Account account) {
  assert(! rootParser->m_parentParser);

  fprintf(stderr,
          " (+" EXPAT_FMT_PTRDIFF_T("6") " bytes %s|%d, xmlparse.c:%d) %*s\"",
          bytesMore, (account == XML_ACCOUNT_DIRECT) ? "DIR" : "EXP",
          levelsAwayFromRootParser, source_line, 10, "");

  const char ellipis[] = "[..]";
  const size_t ellipsisLength = sizeof(ellipis) /* because compile-time */ - 1;
  const unsigned int contextLength = 10;

  /* Note: Performance is of no concern here */
  const char *walker = before;
  if ((rootParser->m_accounting.debugLevel >= 3)
      || (after - before)
             <= (ptrdiff_t)(contextLength + ellipsisLength + contextLength)) {
    for (; walker < after; walker++) {
      fprintf(stderr, "%s", unsignedCharToPrintable(walker[0]));
    }
  } else {
    for (; walker < before + contextLength; walker++) {
      fprintf(stderr, "%s", unsignedCharToPrintable(walker[0]));
    }
    fprintf(stderr, ellipis);
    walker = after - contextLength;
    for (; walker < after; walker++) {
      fprintf(stderr, "%s", unsignedCharToPrintable(walker[0]));
    }
  }
  fprintf(stderr, "\"\n");
}


processXmlDecl(XML_Parser parser, int isGeneralTextEntity, const char *s,
               const char *next) {
  const char *encodingName = NULL;
  const XML_Char *storedEncName = NULL;
  const ENCODING *newEncoding = NULL;
  const char *version = NULL;
  const char *versionend;
  const XML_Char *storedversion = NULL;
  int standalone = -1;

#ifdef XML_DTD
  if (! accountingDiffTolerated(parser, XML_TOK_XML_DECL, s, next, __LINE__,
                                XML_ACCOUNT_DIRECT)) {
    accountingOnAbort(parser);
    return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
  }
#endif

  if (! (parser->m_ns ? XmlParseXmlDeclNS : XmlParseXmlDecl)(
          isGeneralTextEntity, parser->m_encoding, s, next, &parser->m_eventPtr,
          &version, &versionend, &encodingName, &newEncoding, &standalone)) {
    if (isGeneralTextEntity)
      return XML_ERROR_TEXT_DECL;
    else
      return XML_ERROR_XML_DECL;
  }
  if (! isGeneralTextEntity && standalone == 1) {
    parser->m_dtd->standalone = XML_TRUE;
#ifdef XML_DTD
    if (parser->m_paramEntityParsing
        == XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE)
      parser->m_paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER;
#endif /* XML_DTD */
  }
  if (parser->m_xmlDeclHandler) {
    if (encodingName != NULL) {
      storedEncName = poolStoreString(
          &parser->m_temp2Pool, parser->m_encoding, encodingName,
          encodingName + XmlNameLength(parser->m_encoding, encodingName));
      if (! storedEncName)
        return XML_ERROR_NO_MEMORY;
      poolFinish(&parser->m_temp2Pool);
    }
    if (version) {
      storedversion
          = poolStoreString(&parser->m_temp2Pool, parser->m_encoding, version,
                            versionend - parser->m_encoding->minBytesPerChar);
      if (! storedversion)
        return XML_ERROR_NO_MEMORY;
    }
    parser->m_xmlDeclHandler(parser->m_handlerArg, storedversion, storedEncName,
                             standalone);
  } else if (parser->m_defaultHandler)
    reportDefault(parser, parser->m_encoding, s, next);
  if (parser->m_protocolEncodingName == NULL) {
    if (newEncoding) {
      /* Check that the specified encoding does not conflict with what
       * the parser has already deduced.  Do we have the same number
       * of bytes in the smallest representation of a character?  If
       * this is UTF-16, is it the same endianness?
       */
      if (newEncoding->minBytesPerChar != parser->m_encoding->minBytesPerChar
          || (newEncoding->minBytesPerChar == 2
              && newEncoding != parser->m_encoding)) {
        parser->m_eventPtr = encodingName;
        return XML_ERROR_INCORRECT_ENCODING;
      }
      parser->m_encoding = newEncoding;
    } else if (encodingName) {
      enum XML_Error result;
      if (! storedEncName) {
        storedEncName = poolStoreString(
            &parser->m_temp2Pool, parser->m_encoding, encodingName,
            encodingName + XmlNameLength(parser->m_encoding, encodingName));
        if (! storedEncName)
          return XML_ERROR_NO_MEMORY;
      }
      result = handleUnknownEncoding(parser, storedEncName);
      poolClear(&parser->m_temp2Pool);
      if (result == XML_ERROR_UNKNOWN_ENCODING)
        parser->m_eventPtr = encodingName;
      return result;
    }
  }

  if (storedEncName || storedversion)
    poolClear(&parser->m_temp2Pool);

  return XML_ERROR_NONE;
}


externalEntityContentProcessor(XML_Parser parser, const char *start,
                               const char *end, const char **endPtr) {
  enum XML_Error result
      = doContent(parser, 1, parser->m_encoding, start, end, endPtr,
                  (XML_Bool)! parser->m_parsingStatus.finalBuffer,
                  XML_ACCOUNT_ENTITY_EXPANSION);
  if (result == XML_ERROR_NONE) {
    if (! storeRawNames(parser))
      return XML_ERROR_NO_MEMORY;
  }
  return result;
}


parserCreate(const XML_Char *encodingName,
             const XML_Memory_Handling_Suite *memsuite, const XML_Char *nameSep,
             DTD *dtd) {
  XML_Parser parser;

  if (memsuite) {
    XML_Memory_Handling_Suite *mtemp;
    parser = memsuite->malloc_fcn(sizeof(struct XML_ParserStruct));
    if (parser != NULL) {
      mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem);
      mtemp->malloc_fcn = memsuite->malloc_fcn;
      mtemp->realloc_fcn = memsuite->realloc_fcn;
      mtemp->free_fcn = memsuite->free_fcn;
    }
  } else {
    XML_Memory_Handling_Suite *mtemp;
    parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct));
    if (parser != NULL) {
      mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem);
      mtemp->malloc_fcn = malloc;
      mtemp->realloc_fcn = realloc;
      mtemp->free_fcn = free;
    }
  }

  if (! parser)
    return parser;

  parser->m_buffer = NULL;
  parser->m_bufferLim = NULL;

  parser->m_attsSize = INIT_ATTS_SIZE;
  parser->m_atts
      = (ATTRIBUTE *)MALLOC(parser, parser->m_attsSize * sizeof(ATTRIBUTE));
  if (parser->m_atts == NULL) {
    FREE(parser, parser);
    return NULL;
  }
#ifdef XML_ATTR_INFO
  parser->m_attInfo = (XML_AttrInfo *)MALLOC(
      parser, parser->m_attsSize * sizeof(XML_AttrInfo));
  if (parser->m_attInfo == NULL) {
    FREE(parser, parser->m_atts);
    FREE(parser, parser);
    return NULL;
  }
#endif
  parser->m_dataBuf
      = (XML_Char *)MALLOC(parser, INIT_DATA_BUF_SIZE * sizeof(XML_Char));
  if (parser->m_dataBuf == NULL) {
    FREE(parser, parser->m_atts);
#ifdef XML_ATTR_INFO
    FREE(parser, parser->m_attInfo);
#endif
    FREE(parser, parser);
    return NULL;
  }
  parser->m_dataBufEnd = parser->m_dataBuf + INIT_DATA_BUF_SIZE;

  if (dtd)
    parser->m_dtd = dtd;
  else {
    parser->m_dtd = dtdCreate(&parser->m_mem);
    if (parser->m_dtd == NULL) {
      FREE(parser, parser->m_dataBuf);
      FREE(parser, parser->m_atts);
#ifdef XML_ATTR_INFO
      FREE(parser, parser->m_attInfo);
#endif
      FREE(parser, parser);
      return NULL;
    }
  }

  parser->m_freeBindingList = NULL;
  parser->m_freeTagList = NULL;
  parser->m_freeInternalEntities = NULL;

  parser->m_groupSize = 0;
  parser->m_groupConnector = NULL;

  parser->m_unknownEncodingHandler = NULL;
  parser->m_unknownEncodingHandlerData = NULL;

  parser->m_namespaceSeparator = ASCII_EXCL;
  parser->m_ns = XML_FALSE;
  parser->m_ns_triplets = XML_FALSE;

  parser->m_nsAtts = NULL;
  parser->m_nsAttsVersion = 0;
  parser->m_nsAttsPower = 0;

  parser->m_protocolEncodingName = NULL;

  poolInit(&parser->m_tempPool, &(parser->m_mem));
  poolInit(&parser->m_temp2Pool, &(parser->m_mem));
  parserInit(parser, encodingName);

  if (encodingName && ! parser->m_protocolEncodingName) {
    XML_ParserFree(parser);
    return NULL;
  }

  if (nameSep) {
    parser->m_ns = XML_TRUE;
    parser->m_internalEncoding = XmlGetInternalEncodingNS();
    parser->m_namespaceSeparator = *nameSep;
  } else {
    parser->m_internalEncoding = XmlGetInternalEncoding();
  }

  return parser;
}


externalEntityInitProcessor2(XML_Parser parser, const char *start,
                             const char *end, const char **endPtr) {
  const char *next = start; /* XmlContentTok doesn't always set the last arg */
  int tok = XmlContentTok(parser->m_encoding, start, end, &next);
  switch (tok) {
  case XML_TOK_BOM:
#ifdef XML_DTD
    if (! accountingDiffTolerated(parser, tok, start, next, __LINE__,
                                  XML_ACCOUNT_DIRECT)) {
      accountingOnAbort(parser);
      return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
    }
#endif /* XML_DTD */

    /* If we are at the end of the buffer, this would cause the next stage,
       i.e. externalEntityInitProcessor3, to pass control directly to
       doContent (by detecting XML_TOK_NONE) without processing any xml text
       declaration - causing the error XML_ERROR_MISPLACED_XML_PI in doContent.
    */
    if (next == end && ! parser->m_parsingStatus.finalBuffer) {
      *endPtr = next;
      return XML_ERROR_NONE;
    }
    start = next;
    break;
  case XML_TOK_PARTIAL:
    if (! parser->m_parsingStatus.finalBuffer) {
      *endPtr = start;
      return XML_ERROR_NONE;
    }
    parser->m_eventPtr = start;
    return XML_ERROR_UNCLOSED_TOKEN;
  case XML_TOK_PARTIAL_CHAR:
    if (! parser->m_parsingStatus.finalBuffer) {
      *endPtr = start;
      return XML_ERROR_NONE;
    }
    parser->m_eventPtr = start;
    return XML_ERROR_PARTIAL_CHAR;
  }
  parser->m_processor = externalEntityInitProcessor3;
  return externalEntityInitProcessor3(parser, start, end, endPtr);
}


entityValueInitProcessor(XML_Parser parser, const char *s, const char *end,
                         const char **nextPtr) {
  int tok;
  const char *start = s;
  const char *next = start;
  parser->m_eventPtr = start;

  for (;;) {
    tok = XmlPrologTok(parser->m_encoding, start, end, &next);
    /* Note: Except for XML_TOK_BOM below, these bytes are accounted later in:
             - storeEntityValue
             - processXmlDecl
    */
    parser->m_eventEndPtr = next;
    if (tok <= 0) {
      if (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) {
        *nextPtr = s;
        return XML_ERROR_NONE;
      }
      switch (tok) {
      case XML_TOK_INVALID:
        return XML_ERROR_INVALID_TOKEN;
      case XML_TOK_PARTIAL:
        return XML_ERROR_UNCLOSED_TOKEN;
      case XML_TOK_PARTIAL_CHAR:
        return XML_ERROR_PARTIAL_CHAR;
      case XML_TOK_NONE: /* start == end */
      default:
        break;
      }
      /* found end of entity value - can store it now */
      return storeEntityValue(parser, parser->m_encoding, s, end,
                              XML_ACCOUNT_DIRECT);
    } else if (tok == XML_TOK_XML_DECL) {
      enum XML_Error result;
      result = processXmlDecl(parser, 0, start, next);
      if (result != XML_ERROR_NONE)
        return result;
      /* At this point, m_parsingStatus.parsing cannot be XML_SUSPENDED.  For
       * that to happen, a parameter entity parsing handler must have attempted
       * to suspend the parser, which fails and raises an error.  The parser can
       * be aborted, but can't be suspended.
       */
      if (parser->m_parsingStatus.parsing == XML_FINISHED)
        return XML_ERROR_ABORTED;
      *nextPtr = next;
      /* stop scanning for text declaration - we found one */
      parser->m_processor = entityValueProcessor;
      return entityValueProcessor(parser, next, end, nextPtr);
    }
    /* If we are at the end of the buffer, this would cause XmlPrologTok to
       return XML_TOK_NONE on the next call, which would then cause the
       function to exit with *nextPtr set to s - that is what we want for other
       tokens, but not for the BOM - we would rather like to skip it;
       then, when this routine is entered the next time, XmlPrologTok will
       return XML_TOK_INVALID, since the BOM is still in the buffer
    */
    else if (tok == XML_TOK_BOM && next == end
             && ! parser->m_parsingStatus.finalBuffer) {
#  ifdef XML_DTD
      if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
                                    XML_ACCOUNT_DIRECT)) {
        accountingOnAbort(parser);
        return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
      }
#  endif

      *nextPtr = next;
      return XML_ERROR_NONE;
    }
    /* If we get this token, we have the start of what might be a
       normal tag, but not a declaration (i.e. it doesn't begin with
       "<!").  In a DTD context, that isn't legal.
    */
    else if (tok == XML_TOK_INSTANCE_START) {
      *nextPtr = next;
      return XML_ERROR_SYNTAX;
    }
    start = next;
    parser->m_eventPtr = start;
  }
}


internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
                        const char **nextPtr) {
  ENTITY *entity;
  const char *textStart, *textEnd;
  const char *next;
  enum XML_Error result;
  OPEN_INTERNAL_ENTITY *openEntity = parser->m_openInternalEntities;
  if (! openEntity)
    return XML_ERROR_UNEXPECTED_STATE;

  entity = openEntity->entity;
  textStart = ((const char *)entity->textPtr) + entity->processed;
  textEnd = (const char *)(entity->textPtr + entity->textLen);
  /* Set a safe default value in case 'next' does not get set */
  next = textStart;

#ifdef XML_DTD
  if (entity->is_param) {
    int tok
        = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
    result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,
                      tok, next, &next, XML_FALSE, XML_TRUE,
                      XML_ACCOUNT_ENTITY_EXPANSION);
  } else
#endif /* XML_DTD */
    result = doContent(parser, openEntity->startTagLevel,
                       parser->m_internalEncoding, textStart, textEnd, &next,
                       XML_FALSE, XML_ACCOUNT_ENTITY_EXPANSION);

  if (result != XML_ERROR_NONE)
    return result;
  else if (textEnd != next
           && parser->m_parsingStatus.parsing == XML_SUSPENDED) {
    entity->processed = (int)(next - (const char *)entity->textPtr);
    return result;
  } else {
#ifdef XML_DTD
    entityTrackingOnClose(parser, entity, __LINE__);
#endif
    entity->open = XML_FALSE;
    parser->m_openInternalEntities = openEntity->next;
    /* put openEntity back in list of free instances */
    openEntity->next = parser->m_freeInternalEntities;
    parser->m_freeInternalEntities = openEntity;
  }

#ifdef XML_DTD
  if (entity->is_param) {
    int tok;
    parser->m_processor = prologProcessor;
    tok = XmlPrologTok(parser->m_encoding, s, end, &next);
    return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
                    (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE,
                    XML_ACCOUNT_DIRECT);
  } else
#endif /* XML_DTD */
  {
    parser->m_processor = contentProcessor;
    /* see externalEntityContentProcessor vs contentProcessor */
    return doContent(parser, parser->m_parentParser ? 1 : 0, parser->m_encoding,
                     s, end, nextPtr,
                     (XML_Bool)! parser->m_parsingStatus.finalBuffer,
                     XML_ACCOUNT_DIRECT);
  }
}


contentProcessor(XML_Parser parser, const char *start, const char *end,
                 const char **endPtr) {
  enum XML_Error result = doContent(
      parser, 0, parser->m_encoding, start, end, endPtr,
      (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_DIRECT);
  if (result == XML_ERROR_NONE) {
    if (! storeRawNames(parser))
      return XML_ERROR_NO_MEMORY;
  }
  return result;
}