static struct hh_flow_state *alloc_new_hh(struct list_head *head,
					  struct hhf_sched_data *q)
{
	struct hh_flow_state *flow;
	u32 now = hhf_time_stamp();

	if (!list_empty(head)) {
		/* Find an expired heavy-hitter flow entry. */
		list_for_each_entry(flow, head, flowchain) {
			u32 prev = flow->hit_timestamp + q->hhf_evict_timeout;

			if (hhf_time_before(prev, now))
				return flow;
		}
	}

	if (q->hh_flows_current_cnt >= q->hh_flows_limit) {
		q->hh_flows_overlimit++;
		return NULL;
	}
	/* Create new entry. */
	flow = kzalloc(sizeof(struct hh_flow_state), GFP_ATOMIC);
	if (!flow)
		return NULL;

	q->hh_flows_current_cnt++;
	INIT_LIST_HEAD(&flow->flowchain);
	list_add_tail(&flow->flowchain, head);

	return flow;
}


static int sfb_enqueue(struct sk_buff *skb, struct Qdisc *sch,
		       struct sk_buff **to_free)
{

	struct sfb_sched_data *q = qdisc_priv(sch);
	struct Qdisc *child = q->qdisc;
	struct tcf_proto *fl;
	int i;
	u32 p_min = ~0;
	u32 minqlen = ~0;
	u32 r, sfbhash;
	u32 slot = q->slot;
	int ret = NET_XMIT_SUCCESS | __NET_XMIT_BYPASS;

	if (unlikely(sch->q.qlen >= q->limit)) {
		qdisc_qstats_overlimit(sch);
		q->stats.queuedrop++;
		goto drop;
	}

	if (q->rehash_interval > 0) {
		unsigned long limit = q->rehash_time + q->rehash_interval;

		if (unlikely(time_after(jiffies, limit))) {
			sfb_swap_slot(q);
			q->rehash_time = jiffies;
		} else if (unlikely(!q->double_buffering && q->warmup_time > 0 &&
				    time_after(jiffies, limit - q->warmup_time))) {
			q->double_buffering = true;
		}
	}

	fl = rcu_dereference_bh(q->filter_list);
	if (fl) {
		u32 salt;

		/* If using external classifiers, get result and record it. */
		if (!sfb_classify(skb, fl, &ret, &salt))
			goto other_drop;
		sfbhash = siphash_1u32(salt, &q->bins[slot].perturbation);
	} else {
		sfbhash = skb_get_hash_perturb(skb, &q->bins[slot].perturbation);
	}

	if (!sfbhash)
		sfbhash = 1;
	sfb_skb_cb(skb)->hashes[slot] = sfbhash;

	for (i = 0; i < SFB_LEVELS; i++) {
		u32 hash = sfbhash & SFB_BUCKET_MASK;
		struct sfb_bucket *b = &q->bins[slot].bins[i][hash];

		sfbhash >>= SFB_BUCKET_SHIFT;
		if (b->qlen == 0)
			decrement_prob(b, q);
		else if (b->qlen >= q->bin_size)
			increment_prob(b, q);
		if (minqlen > b->qlen)
			minqlen = b->qlen;
		if (p_min > b->p_mark)
			p_min = b->p_mark;
	}

	slot ^= 1;
	sfb_skb_cb(skb)->hashes[slot] = 0;

	if (unlikely(minqlen >= q->max)) {
		qdisc_qstats_overlimit(sch);
		q->stats.bucketdrop++;
		goto drop;
	}

	if (unlikely(p_min >= SFB_MAX_PROB)) {
		/* Inelastic flow */
		if (q->double_buffering) {
			sfbhash = skb_get_hash_perturb(skb,
			    &q->bins[slot].perturbation);
			if (!sfbhash)
				sfbhash = 1;
			sfb_skb_cb(skb)->hashes[slot] = sfbhash;

			for (i = 0; i < SFB_LEVELS; i++) {
				u32 hash = sfbhash & SFB_BUCKET_MASK;
				struct sfb_bucket *b = &q->bins[slot].bins[i][hash];

				sfbhash >>= SFB_BUCKET_SHIFT;
				if (b->qlen == 0)
					decrement_prob(b, q);
				else if (b->qlen >= q->bin_size)
					increment_prob(b, q);
			}
		}
		if (sfb_rate_limit(skb, q)) {
			qdisc_qstats_overlimit(sch);
			q->stats.penaltydrop++;
			goto drop;
		}
		goto enqueue;
	}

	r = prandom_u32() & SFB_MAX_PROB;

	if (unlikely(r < p_min)) {
		if (unlikely(p_min > SFB_MAX_PROB / 2)) {
			/* If we're marking that many packets, then either
			 * this flow is unresponsive, or we're badly congested.
			 * In either case, we want to start dropping packets.
			 */
			if (r < (p_min - SFB_MAX_PROB / 2) * 2) {
				q->stats.earlydrop++;
				goto drop;
			}
		}
		if (INET_ECN_set_ce(skb)) {
			q->stats.marked++;
		} else {
			q->stats.earlydrop++;
			goto drop;
		}
	}

enqueue:
	ret = qdisc_enqueue(skb, child, to_free);
	if (likely(ret == NET_XMIT_SUCCESS)) {
		qdisc_qstats_backlog_inc(sch, skb);
		sch->q.qlen++;
		increment_qlen(skb, q);
	} else if (net_xmit_drop_count(ret)) {
		q->stats.childdrop++;
		qdisc_qstats_drop(sch);
	}
	return ret;

drop:
	qdisc_drop(skb, sch, to_free);
	return NET_XMIT_CN;
other_drop:
	if (ret & __NET_XMIT_BYPASS)
		qdisc_qstats_drop(sch);
	kfree_skb(skb);
	return ret;
}


__be32 flow_get_u32_src(const struct flow_keys *flow)
{
	switch (flow->control.addr_type) {
	case FLOW_DISSECTOR_KEY_IPV4_ADDRS:
		return flow->addrs.v4addrs.src;
	case FLOW_DISSECTOR_KEY_IPV6_ADDRS:
		return (__force __be32)ipv6_addr_hash(
			&flow->addrs.v6addrs.src);
	case FLOW_DISSECTOR_KEY_TIPC:
		return flow->addrs.tipckey.key;
	default:
		return 0;
	}
}


__u32 __get_hash_from_flowi6(const struct flowi6 *fl6, struct flow_keys *keys)
{
	memset(keys, 0, sizeof(*keys));

	memcpy(&keys->addrs.v6addrs.src, &fl6->saddr,
	    sizeof(keys->addrs.v6addrs.src));
	memcpy(&keys->addrs.v6addrs.dst, &fl6->daddr,
	    sizeof(keys->addrs.v6addrs.dst));
	keys->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
	keys->ports.src = fl6->fl6_sport;
	keys->ports.dst = fl6->fl6_dport;
	keys->keyid.keyid = fl6->fl6_gre_key;
	keys->tags.flow_label = (__force u32)flowi6_get_flowlabel(fl6);
	keys->basic.ip_proto = fl6->flowi6_proto;

	return flow_hash_from_keys(keys);
}


static void fq_tin_filter(struct fq *fq,
			  struct fq_tin *tin,
			  fq_skb_filter_t filter_func,
			  void *filter_data,
			  fq_skb_free_t free_func)
{
	struct fq_flow *flow;

	lockdep_assert_held(&fq->lock);

	list_for_each_entry(flow, &tin->new_flows, flowchain)
		fq_flow_filter(fq, flow, filter_func, filter_data, free_func);
	list_for_each_entry(flow, &tin->old_flows, flowchain)
		fq_flow_filter(fq, flow, filter_func, filter_data, free_func);
}


static inline void slot_queue_init(struct sfq_slot *slot)
{
	memset(slot, 0, sizeof(*slot));
	slot->skblist_prev = slot->skblist_next = (struct sk_buff *)slot;
}


static void __skb_flow_bpf_to_target(const struct bpf_flow_keys *flow_keys,
				     struct flow_dissector *flow_dissector,
				     void *target_container)
{
	struct flow_dissector_key_control *key_control;
	struct flow_dissector_key_basic *key_basic;
	struct flow_dissector_key_addrs *key_addrs;
	struct flow_dissector_key_ports *key_ports;
	struct flow_dissector_key_tags *key_tags;

	key_control = skb_flow_dissector_target(flow_dissector,
						FLOW_DISSECTOR_KEY_CONTROL,
						target_container);
	key_control->thoff = flow_keys->thoff;
	if (flow_keys->is_frag)
		key_control->flags |= FLOW_DIS_IS_FRAGMENT;
	if (flow_keys->is_first_frag)
		key_control->flags |= FLOW_DIS_FIRST_FRAG;
	if (flow_keys->is_encap)
		key_control->flags |= FLOW_DIS_ENCAPSULATION;

	key_basic = skb_flow_dissector_target(flow_dissector,
					      FLOW_DISSECTOR_KEY_BASIC,
					      target_container);
	key_basic->n_proto = flow_keys->n_proto;
	key_basic->ip_proto = flow_keys->ip_proto;

	if (flow_keys->addr_proto == ETH_P_IP &&
	    dissector_uses_key(flow_dissector, FLOW_DISSECTOR_KEY_IPV4_ADDRS)) {
		key_addrs = skb_flow_dissector_target(flow_dissector,
						      FLOW_DISSECTOR_KEY_IPV4_ADDRS,
						      target_container);
		key_addrs->v4addrs.src = flow_keys->ipv4_src;
		key_addrs->v4addrs.dst = flow_keys->ipv4_dst;
		key_control->addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS;
	} else if (flow_keys->addr_proto == ETH_P_IPV6 &&
		   dissector_uses_key(flow_dissector,
				      FLOW_DISSECTOR_KEY_IPV6_ADDRS)) {
		key_addrs = skb_flow_dissector_target(flow_dissector,
						      FLOW_DISSECTOR_KEY_IPV6_ADDRS,
						      target_container);
		memcpy(&key_addrs->v6addrs, &flow_keys->ipv6_src,
		       sizeof(key_addrs->v6addrs));
		key_control->addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS;
	}

	if (dissector_uses_key(flow_dissector, FLOW_DISSECTOR_KEY_PORTS)) {
		key_ports = skb_flow_dissector_target(flow_dissector,
						      FLOW_DISSECTOR_KEY_PORTS,
						      target_container);
		key_ports->src = flow_keys->sport;
		key_ports->dst = flow_keys->dport;
	}

	if (dissector_uses_key(flow_dissector,
			       FLOW_DISSECTOR_KEY_FLOW_LABEL)) {
		key_tags = skb_flow_dissector_target(flow_dissector,
						     FLOW_DISSECTOR_KEY_FLOW_LABEL,
						     target_container);
		key_tags->flow_label = ntohl(flow_keys->flow_label);
	}
}


u32 __skb_get_poff(const struct sk_buff *skb, void *data,
		   const struct flow_keys_basic *keys, int hlen)
{
	u32 poff = keys->control.thoff;

	/* skip L4 headers for fragments after the first */
	if ((keys->control.flags & FLOW_DIS_IS_FRAGMENT) &&
	    !(keys->control.flags & FLOW_DIS_FIRST_FRAG))
		return poff;

	switch (keys->basic.ip_proto) {
	case IPPROTO_TCP: {
		/* access doff as u8 to avoid unaligned access */
		const u8 *doff;
		u8 _doff;

		doff = __skb_header_pointer(skb, poff + 12, sizeof(_doff),
					    data, hlen, &_doff);
		if (!doff)
			return poff;

		poff += max_t(u32, sizeof(struct tcphdr), (*doff & 0xF0) >> 2);
		break;
	}
	case IPPROTO_UDP:
	case IPPROTO_UDPLITE:
		poff += sizeof(struct udphdr);
		break;
	/* For the rest, we do not really care about header
	 * extensions at this point for now.
	 */
	case IPPROTO_ICMP:
		poff += sizeof(struct icmphdr);
		break;
	case IPPROTO_ICMPV6:
		poff += sizeof(struct icmp6hdr);
		break;
	case IPPROTO_IGMP:
		poff += sizeof(struct igmphdr);
		break;
	case IPPROTO_DCCP:
		poff += sizeof(struct dccp_hdr);
		break;
	case IPPROTO_SCTP:
		poff += sizeof(struct sctphdr);
		break;
	}

	return poff;
}


int skb_flow_dissector_prog_query(const union bpf_attr *attr,
				  union bpf_attr __user *uattr)
{
	__u32 __user *prog_ids = u64_to_user_ptr(attr->query.prog_ids);
	u32 prog_id, prog_cnt = 0, flags = 0;
	struct bpf_prog *attached;
	struct net *net;

	if (attr->query.query_flags)
		return -EINVAL;

	net = get_net_ns_by_fd(attr->query.target_fd);
	if (IS_ERR(net))
		return PTR_ERR(net);

	rcu_read_lock();
	attached = rcu_dereference(net->flow_dissector_prog);
	if (attached) {
		prog_cnt = 1;
		prog_id = attached->aux->id;
	}
	rcu_read_unlock();

	put_net(net);

	if (copy_to_user(&uattr->query.attach_flags, &flags, sizeof(flags)))
		return -EFAULT;
	if (copy_to_user(&uattr->query.prog_cnt, &prog_cnt, sizeof(prog_cnt)))
		return -EFAULT;

	if (!attr->query.prog_cnt || !prog_ids || !prog_cnt)
		return 0;

	if (copy_to_user(prog_ids, &prog_id, sizeof(u32)))
		return -EFAULT;

	return 0;
}


static void sfq_destroy(struct Qdisc *sch)
{
	struct sfq_sched_data *q = qdisc_priv(sch);

	tcf_block_put(q->block);
	q->perturb_period = 0;
	del_timer_sync(&q->perturb_timer);
	sfq_free(q->ht);
	sfq_free(q->slots);
	kfree(q->red_parms);
}


void __skb_get_hash(struct sk_buff *skb)
{
	struct flow_keys keys;
	u32 hash;

	__flow_hash_secret_init();

	hash = ___skb_get_hash(skb, &keys, &hashrnd);

	__skb_set_sw_hash(skb, hash, flow_keys_have_l4(&keys));
}


int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
	       struct inode *new_dir, struct dentry *new_dentry,
	       struct inode **delegated_inode, unsigned int flags)
{
	int error;
	bool is_dir = d_is_dir(old_dentry);
	struct inode *source = old_dentry->d_inode;
	struct inode *target = new_dentry->d_inode;
	bool new_is_dir = false;
	unsigned max_links = new_dir->i_sb->s_max_links;
	struct name_snapshot old_name;

	if (source == target)
		return 0;

	error = may_delete(old_dir, old_dentry, is_dir);
	if (error)
		return error;

	if (!target) {
		error = may_create(new_dir, new_dentry);
	} else {
		new_is_dir = d_is_dir(new_dentry);

		if (!(flags & RENAME_EXCHANGE))
			error = may_delete(new_dir, new_dentry, is_dir);
		else
			error = may_delete(new_dir, new_dentry, new_is_dir);
	}
	if (error)
		return error;

	if (!old_dir->i_op->rename)
		return -EPERM;

	/*
	 * If we are going to change the parent - check write permissions,
	 * we'll need to flip '..'.
	 */
	if (new_dir != old_dir) {
		if (is_dir) {
			error = inode_permission(source, MAY_WRITE);
			if (error)
				return error;
		}
		if ((flags & RENAME_EXCHANGE) && new_is_dir) {
			error = inode_permission(target, MAY_WRITE);
			if (error)
				return error;
		}
	}

	error = security_inode_rename(old_dir, old_dentry, new_dir, new_dentry,
				      flags);
	if (error)
		return error;

	take_dentry_name_snapshot(&old_name, old_dentry);
	dget(new_dentry);
	if (!is_dir || (flags & RENAME_EXCHANGE))
		lock_two_nondirectories(source, target);
	else if (target)
		inode_lock(target);

	error = -EBUSY;
	if (is_local_mountpoint(old_dentry) || is_local_mountpoint(new_dentry))
		goto out;

	if (max_links && new_dir != old_dir) {
		error = -EMLINK;
		if (is_dir && !new_is_dir && new_dir->i_nlink >= max_links)
			goto out;
		if ((flags & RENAME_EXCHANGE) && !is_dir && new_is_dir &&
		    old_dir->i_nlink >= max_links)
			goto out;
	}
	if (!is_dir) {
		error = try_break_deleg(source, delegated_inode);
		if (error)
			goto out;
	}
	if (target && !new_is_dir) {
		error = try_break_deleg(target, delegated_inode);
		if (error)
			goto out;
	}
	error = old_dir->i_op->rename(old_dir, old_dentry,
				       new_dir, new_dentry, flags);
	if (error)
		goto out;

	if (!(flags & RENAME_EXCHANGE) && target) {
		if (is_dir) {
			shrink_dcache_parent(new_dentry);
			target->i_flags |= S_DEAD;
		}
		dont_mount(new_dentry);
		detach_mounts(new_dentry);
	}
	if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE)) {
		if (!(flags & RENAME_EXCHANGE))
			d_move(old_dentry, new_dentry);
		else
			d_exchange(old_dentry, new_dentry);
	}
out:
	if (!is_dir || (flags & RENAME_EXCHANGE))
		unlock_two_nondirectories(source, target);
	else if (target)
		inode_unlock(target);
	dput(new_dentry);
	if (!error) {
		fsnotify_move(old_dir, new_dir, &old_name.name, is_dir,
			      !(flags & RENAME_EXCHANGE) ? target : NULL, old_dentry);
		if (flags & RENAME_EXCHANGE) {
			fsnotify_move(new_dir, old_dir, &old_dentry->d_name,
				      new_is_dir, NULL, new_dentry);
		}
	}
	release_dentry_name_snapshot(&old_name);

	return error;
}


SYSCALL_DEFINE2(symlink, const char __user *, oldname, const char __user *, newname)
{
	return do_symlinkat(oldname, AT_FDCWD, newname);
}


static int unlazy_walk(struct nameidata *nd)
{
	struct dentry *parent = nd->path.dentry;

	BUG_ON(!(nd->flags & LOOKUP_RCU));

	nd->flags &= ~LOOKUP_RCU;
	if (unlikely(!legitimize_links(nd)))
		goto out1;
	if (unlikely(!legitimize_path(nd, &nd->path, nd->seq)))
		goto out;
	if (unlikely(!legitimize_root(nd)))
		goto out;
	rcu_read_unlock();
	BUG_ON(nd->inode != parent->d_inode);
	return 0;

out1:
	nd->path.mnt = NULL;
	nd->path.dentry = NULL;
out:
	rcu_read_unlock();
	return -ECHILD;
}


SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname,
		int, newdfd, const char __user *, newname, int, flags)
{
	return do_linkat(olddfd, oldname, newdfd, newname, flags);
}


static inline int may_follow_link(struct nameidata *nd)
{
	const struct inode *inode;
	const struct inode *parent;
	kuid_t puid;

	if (!sysctl_protected_symlinks)
		return 0;

	/* Allowed if owner and follower match. */
	inode = nd->link_inode;
	if (uid_eq(current_cred()->fsuid, inode->i_uid))
		return 0;

	/* Allowed if parent directory not sticky and world-writable. */
	parent = nd->inode;
	if ((parent->i_mode & (S_ISVTX|S_IWOTH)) != (S_ISVTX|S_IWOTH))
		return 0;

	/* Allowed if parent directory and link owner match. */
	puid = parent->i_uid;
	if (uid_valid(puid) && uid_eq(puid, inode->i_uid))
		return 0;

	if (nd->flags & LOOKUP_RCU)
		return -ECHILD;

	audit_inode(nd->name, nd->stack[0].link.dentry, 0);
	audit_log_path_denied(AUDIT_ANOM_LINK, "follow_link");
	return -EACCES;
}


static int unlazy_child(struct nameidata *nd, struct dentry *dentry, unsigned seq)
{
	BUG_ON(!(nd->flags & LOOKUP_RCU));

	nd->flags &= ~LOOKUP_RCU;
	if (unlikely(!legitimize_links(nd)))
		goto out2;
	if (unlikely(!legitimize_mnt(nd->path.mnt, nd->m_seq)))
		goto out2;
	if (unlikely(!lockref_get_not_dead(&nd->path.dentry->d_lockref)))
		goto out1;

	/*
	 * We need to move both the parent and the dentry from the RCU domain
	 * to be properly refcounted. And the sequence number in the dentry
	 * validates *both* dentry counters, since we checked the sequence
	 * number of the parent after we got the child sequence number. So we
	 * know the parent must still be valid if the child sequence number is
	 */
	if (unlikely(!lockref_get_not_dead(&dentry->d_lockref)))
		goto out;
	if (unlikely(read_seqcount_retry(&dentry->d_seq, seq)))
		goto out_dput;
	/*
	 * Sequence counts matched. Now make sure that the root is
	 * still valid and get it if required.
	 */
	if (unlikely(!legitimize_root(nd)))
		goto out_dput;
	rcu_read_unlock();
	return 0;

out2:
	nd->path.mnt = NULL;
out1:
	nd->path.dentry = NULL;
out:
	rcu_read_unlock();
	return -ECHILD;
out_dput:
	rcu_read_unlock();
	dput(dentry);
	return -ECHILD;
}


static int pick_link(struct nameidata *nd, struct path *link,
		     struct inode *inode, unsigned seq)
{
	int error;
	struct saved *last;
	if (unlikely(nd->total_link_count++ >= MAXSYMLINKS)) {
		path_to_nameidata(link, nd);
		return -ELOOP;
	}
	if (!(nd->flags & LOOKUP_RCU)) {
		if (link->mnt == nd->path.mnt)
			mntget(link->mnt);
	}
	error = nd_alloc_stack(nd);
	if (unlikely(error)) {
		if (error == -ECHILD) {
			if (unlikely(!legitimize_path(nd, link, seq))) {
				drop_links(nd);
				nd->depth = 0;
				nd->flags &= ~LOOKUP_RCU;
				nd->path.mnt = NULL;
				nd->path.dentry = NULL;
				rcu_read_unlock();
			} else if (likely(unlazy_walk(nd)) == 0)
				error = nd_alloc_stack(nd);
		}
		if (error) {
			path_put(link);
			return error;
		}
	}

	last = nd->stack + nd->depth++;
	last->link = *link;
	clear_delayed_call(&last->done);
	nd->link_inode = inode;
	last->seq = seq;
	return 1;
}


getln(int fd, char *buf, size_t bufsiz, bool feedback,
    enum tgetpass_errval *errval)
{
    size_t left = bufsiz;
    ssize_t nr = -1;
    char *cp = buf;
    char c = '\0';
    debug_decl(getln, SUDO_DEBUG_CONV);

    *errval = TGP_ERRVAL_NOERROR;

    if (left == 0) {
	*errval = TGP_ERRVAL_READERROR;
	errno = EINVAL;
	debug_return_str(NULL);		/* sanity */
    }

    while (--left) {
	nr = read(fd, &c, 1);
	if (nr != 1 || c == '\n' || c == '\r')
	    break;
	if (feedback) {
	    if (c == sudo_term_eof) {
		nr = 0;
		break;
	    } else if (c == sudo_term_kill) {
		while (cp > buf) {
		    if (write(fd, "\b \b", 3) == -1)
			break;
		    cp--;
		}
		cp = buf;
		left = bufsiz;
		continue;
	    } else if (c == sudo_term_erase) {
		if (cp > buf) {
		    ignore_result(write(fd, "\b \b", 3));
		    cp--;
		    left++;
		}
		continue;
	    }
	    ignore_result(write(fd, "*", 1));
	}
	*cp++ = c;
    }
    *cp = '\0';
    if (feedback) {
	/* erase stars */
	while (cp > buf) {
	    if (write(fd, "\b \b", 3) == -1)
		break;
	    --cp;
	}
    }

    switch (nr) {
    case -1:
	/* Read error */
	if (errno == EINTR) {
	    if (signo[SIGALRM] == 1)
		*errval = TGP_ERRVAL_TIMEOUT;
	} else {
	    *errval = TGP_ERRVAL_READERROR;
	}
	debug_return_str(NULL);
    case 0:
	/* EOF is only an error if no bytes were read. */
	if (left == bufsiz - 1) {
	    *errval = TGP_ERRVAL_NOPASSWORD;
	    debug_return_str(NULL);
	}
	/* FALLTHROUGH */
    default:
	debug_return_str_masked(buf);
    }
}


repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned long long num)
{
  Repokey key;
  key.name = keyname;
  key.type = REPOKEY_TYPE_NUM;
  key.size = 0;
  key.storage = KEY_STORAGE_INCORE;
  if (num >= 0x80000000)
    {
      data->attrnum64data = solv_extend(data->attrnum64data, data->attrnum64datalen, 1, sizeof(unsigned long long), REPODATA_ATTRNUM64DATA_BLOCK);
      data->attrnum64data[data->attrnum64datalen] = num;
      num = 0x80000000 | data->attrnum64datalen++;
    }
  repodata_set(data, solvid, &key, (Id)num);
}


repodata_create_stubs(Repodata *data)
{
  Repo *repo = data->repo;
  Pool *pool = repo->pool;
  Repodata *sdata;
  int *stubdataids;
  Dataiterator di;
  Id xkeyname = 0;
  int i, cnt = 0;

  dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
  while (dataiterator_step(&di))
    if (di.data == data)
      cnt++;
  dataiterator_free(&di);
  if (!cnt)
    return data;
  stubdataids = solv_calloc(cnt, sizeof(*stubdataids));
  for (i = 0; i < cnt; i++)
    {
      sdata = repodata_add_stub(&data);
      stubdataids[i] = sdata - repo->repodata;
    }
  i = 0;
  dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
  sdata = 0;
  while (dataiterator_step(&di))
    {
      if (di.data != data)
	continue;
      if (di.key->name == REPOSITORY_EXTERNAL && !di.nparents)
	{
	  dataiterator_entersub(&di);
	  sdata = repo->repodata + stubdataids[i++];
	  xkeyname = 0;
	  continue;
	}
      repodata_set_kv(sdata, SOLVID_META, di.key->name, di.key->type, &di.kv);
      if (di.key->name == REPOSITORY_KEYS && di.key->type == REPOKEY_TYPE_IDARRAY)
	{
	  if (!xkeyname)
	    {
	      if (!di.kv.eof)
		xkeyname = di.kv.id;
	    }
	  else
	    {
	      repodata_add_stubkey(sdata, xkeyname, di.kv.id);
	      if (xkeyname == SOLVABLE_FILELIST)
	        repodata_set_filelisttype(sdata, REPODATA_FILELIST_EXTENSION);
	      xkeyname = 0;
	    }
	}
    }
  dataiterator_free(&di);
  for (i = 0; i < cnt; i++)
    repodata_internalize(repo->repodata + stubdataids[i]);
  solv_free(stubdataids);
  return data;
}


datamatcher_match(Datamatcher *ma, const char *str)
{
  int l;
  switch ((ma->flags & SEARCH_STRINGMASK))
    {
    case SEARCH_SUBSTRING:
      if (ma->flags & SEARCH_NOCASE)
	return strcasestr(str, ma->match) != 0;
      else
	return strstr(str, ma->match) != 0;
    case SEARCH_STRING:
      if (ma->flags & SEARCH_NOCASE)
	return !strcasecmp(ma->match, str);
      else
	return !strcmp(ma->match, str);
    case SEARCH_STRINGSTART:
      if (ma->flags & SEARCH_NOCASE)
        return !strncasecmp(ma->match, str, strlen(ma->match));
      else
        return !strncmp(ma->match, str, strlen(ma->match));
    case SEARCH_STRINGEND:
      l = strlen(str) - strlen(ma->match);
      if (l < 0)
	return 0;
      if (ma->flags & SEARCH_NOCASE)
	return !strcasecmp(ma->match, str + l);
      else
	return !strcmp(ma->match, str + l);
    case SEARCH_GLOB:
      return !fnmatch(ma->match, str, (ma->flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0);
    case SEARCH_REGEX:
      return !regexec((const regex_t *)ma->matchdata, str, 0, NULL, 0);
    default:
      return 0;
    }
}


dataiterator_setpos(Dataiterator *di)
{
  if (di->kv.eof == 2)
    {
      pool_clear_pos(di->pool);
      return;
    }
  di->pool->pos.solvid = di->solvid;
  di->pool->pos.repo = di->repo;
  di->pool->pos.repodataid = di->data - di->repo->repodata;
  di->pool->pos.schema = di->kv.id;
  di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
}


repodata_setpos_kv(Repodata *data, KeyValue *kv)
{
  Pool *pool = data->repo->pool;
  if (!kv)
    pool_clear_pos(pool);
  else
    {
      pool->pos.repo = data->repo;
      pool->pos.repodataid = data - data->repo->repodata;
      pool->pos.dp = (unsigned char *)kv->str - data->incoredata;
      pool->pos.schema = kv->id;
    }
}


repodata_set_deltalocation(Repodata *data, Id handle, int medianr, const char *dir, const char *file)
{
  int l = 0;
  const char *evr, *suf, *s;

  if (!dir)
    {
      if ((dir = strrchr(file, '/')) != 0)
	{
          l = dir - file;
	  dir = file;
	  file = dir + l + 1;
	  if (!l)
	    l++;
	}
    }
  else
    l = strlen(dir);
  if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
    {
      dir += 2;
      l -= 2;
    }
  if (l == 1 && dir[0] == '.')
    l = 0;
  if (dir && l)
    repodata_set_poolstrn(data, handle, DELTA_LOCATION_DIR, dir, l);
  evr = strchr(file, '-');
  if (evr)
    {
      for (s = evr - 1; s > file; s--)
	if (*s == '-')
	  {
	    evr = s;
	    break;
	  }
    }
  suf = strrchr(file, '.');
  if (suf)
    {
      for (s = suf - 1; s > file; s--)
	if (*s == '.')
	  {
	    suf = s;
	    break;
	  }
      if (!strcmp(suf, ".delta.rpm") || !strcmp(suf, ".patch.rpm"))
	{
	  /* We accept one more item as suffix.  */
	  for (s = suf - 1; s > file; s--)
	    if (*s == '.')
	      {
		suf = s;
	        break;
	      }
	}
    }
  if (!evr)
    suf = 0;
  if (suf && evr && suf < evr)
    suf = 0;
  repodata_set_poolstrn(data, handle, DELTA_LOCATION_NAME, file, evr ? evr - file : strlen(file));
  if (evr)
    repodata_set_poolstrn(data, handle, DELTA_LOCATION_EVR, evr + 1, suf ? suf - evr - 1: strlen(evr + 1));
  if (suf)
    repodata_set_poolstr(data, handle, DELTA_LOCATION_SUFFIX, suf + 1);
}


dataiterator_entersub(Dataiterator *di)
{
  if (di->state == di_nextarrayelement)
    di->state = di_entersub;
}


repodata_translate_dir_slow(Repodata *data, Repodata *fromdata, Id dir, int create, Id *cache)
{
  Id parent, compid;
  if (!dir)
    {
      /* make sure that the dirpool has an entry */
      if (create && !data->dirpool.ndirs)
        dirpool_add_dir(&data->dirpool, 0, 0, create);
      return 0;
    }
  parent = dirpool_parent(&fromdata->dirpool, dir);
  if (parent)
    {
      if (!(parent = repodata_translate_dir(data, fromdata, parent, create, cache)))
	return 0;
    }
  compid = dirpool_compid(&fromdata->dirpool, dir);
  if (compid > 1 && (data->localpool || fromdata->localpool))
    {
      if (!(compid = repodata_translate_id(data, fromdata, compid, create)))
	return 0;
    }
  if (!(compid = dirpool_add_dir(&data->dirpool, parent, compid, create)))
    return 0;
  if (cache)
    {
      cache[(dir & 255) * 2] = dir;
      cache[(dir & 255) * 2 + 1] = compid;
    }
  return compid;
}


forward_to_key(Repodata *data, Id keyid, Id *keyp, unsigned char *dp)
{
  Id k;

  if (!keyid)
    return 0;
  if (data->mainschemaoffsets && dp == data->incoredata + data->mainschemaoffsets[0] && keyp == data->schemadata + data->schemata[data->mainschema])
    {
      int i;
      for (i = 0; (k = *keyp++) != 0; i++)
        if (k == keyid)
	  return data->incoredata + data->mainschemaoffsets[i];
      return 0;
    }
  while ((k = *keyp++) != 0)
    {
      if (k == keyid)
	return dp;
      if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
	{
	  dp = data_skip(dp, REPOKEY_TYPE_ID);	/* skip offset */
	  dp = data_skip(dp, REPOKEY_TYPE_ID);	/* skip length */
	  continue;
	}
      if (data->keys[k].storage != KEY_STORAGE_INCORE)
	continue;
      dp = data_skip_key(data, dp, data->keys + k);
    }
  return 0;
}


dataiterator_strdup(Dataiterator *di)
{
  int l = -1;

  if (!di->kv.str || di->kv.str == di->dupstr)
    return;
  switch (di->key->type)
    {
    case_CHKSUM_TYPES:
    case REPOKEY_TYPE_DIRSTRARRAY:
      if (di->kv.num)	/* was it stringified into tmp space? */
        l = strlen(di->kv.str) + 1;
      break;
    default:
      break;
    }
  if (l < 0 && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
    {
      switch (di->key->type)
	{
	case REPOKEY_TYPE_STR:
	case REPOKEY_TYPE_DIRSTRARRAY:
	  l = strlen(di->kv.str) + 1;
	  break;
	case_CHKSUM_TYPES:
	  l = solv_chksum_len(di->key->type);
	  break;
	case REPOKEY_TYPE_BINARY:
	  l = di->kv.num;
	  break;
	}
    }
  if (l >= 0)
    {
      if (!di->dupstrn || di->dupstrn < l)
	{
	  di->dupstrn = l + 16;
	  di->dupstr = solv_realloc(di->dupstr, di->dupstrn);
	}
      if (l)
        memcpy(di->dupstr, di->kv.str, l);
      di->kv.str = di->dupstr;
    }
}


repodata_lookup_num(Repodata *data, Id solvid, Id keyname, unsigned long long notfound)
{
  unsigned char *dp;
  Repokey *key;
  unsigned int high, low;

  dp = find_key_data(data, solvid, keyname, &key);
  if (!dp)
    return notfound;
  switch (key->type)
    {
    case REPOKEY_TYPE_NUM:
      data_read_num64(dp, &low, &high);
      return (unsigned long long)high << 32 | low;
    case REPOKEY_TYPE_CONSTANT:
      return key->size;
    default:
      return notfound;
    }
}


repodata_set_kv(Repodata *data, Id solvid, Id keyname, Id keytype, KeyValue *kv)
{
  switch (keytype)
    {
    case REPOKEY_TYPE_ID:
      repodata_set_id(data, solvid, keyname, kv->id);
      break;
    case REPOKEY_TYPE_CONSTANTID:
      repodata_set_constantid(data, solvid, keyname, kv->id);
      break;
    case REPOKEY_TYPE_IDARRAY:
      repodata_add_idarray(data, solvid, keyname, kv->id);
      break;
    case REPOKEY_TYPE_STR:
      repodata_set_str(data, solvid, keyname, kv->str);
      break;
    case REPOKEY_TYPE_VOID:
      repodata_set_void(data, solvid, keyname);
      break;
    case REPOKEY_TYPE_NUM:
      repodata_set_num(data, solvid, keyname, SOLV_KV_NUM64(kv));
      break;
    case REPOKEY_TYPE_CONSTANT:
      repodata_set_constant(data, solvid, keyname, kv->num);
      break;
    case REPOKEY_TYPE_DIRNUMNUMARRAY:
      if (kv->id)
        repodata_add_dirnumnum(data, solvid, keyname, kv->id, kv->num, kv->num2);
      break;
    case REPOKEY_TYPE_DIRSTRARRAY:
      repodata_add_dirstr(data, solvid, keyname, kv->id, kv->str);
      break;
    case_CHKSUM_TYPES:
      repodata_set_bin_checksum(data, solvid, keyname, keytype, (const unsigned char *)kv->str);
      break;
    default:
      break;
    }
}


repodata_lookup_void(Repodata *data, Id solvid, Id keyname)
{
  return repodata_lookup_type(data, solvid, keyname) == REPOKEY_TYPE_VOID ? 1 : 0;
}


repodata_set_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
{
  Repokey key;
  int i;

  key.name = keyname;
  key.type = REPOKEY_TYPE_IDARRAY;
  key.size = 0;
  key.storage = KEY_STORAGE_INCORE;
  repodata_set(data, solvid, &key, data->attriddatalen);
  data->attriddata = solv_extend(data->attriddata, data->attriddatalen, q->count + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
  for (i = 0; i < q->count; i++)
    data->attriddata[data->attriddatalen++] = q->elements[i];
  data->attriddata[data->attriddatalen++] = 0;
}


data_skip_key(Repodata *data, unsigned char *dp, Repokey *key)
{
  int nentries, schema;
  switch(key->type)
    {
    case REPOKEY_TYPE_FIXARRAY:
      dp = data_read_id(dp, &nentries);
      if (!nentries)
	return dp;
      dp = data_read_id(dp, &schema);
      while (nentries--)
	dp = data_skip_schema(data, dp, schema);
      return dp;
    case REPOKEY_TYPE_FLEXARRAY:
      dp = data_read_id(dp, &nentries);
      while (nentries--)
	{
	  dp = data_read_id(dp, &schema);
	  dp = data_skip_schema(data, dp, schema);
	}
      return dp;
    default:
      if (key->storage == KEY_STORAGE_INCORE)
        dp = data_skip(dp, key->type);
      else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
	{
	  dp = data_skip(dp, REPOKEY_TYPE_ID);
	  dp = data_skip(dp, REPOKEY_TYPE_ID);
	}
      return dp;
    }
}


find_key_data(Repodata *data, Id solvid, Id keyname, Repokey **keypp)
{
  unsigned char *dp;
  Id schema, *keyp, *kp;
  Repokey *key;

  if (!maybe_load_repodata(data, keyname))
    return 0;
  dp = solvid2data(data, solvid, &schema);
  if (!dp)
    return 0;
  keyp = data->schemadata + data->schemata[schema];
  for (kp = keyp; *kp; kp++)
    if (data->keys[*kp].name == keyname)
      break;
  if (!*kp)
    return 0;
  *keypp = key = data->keys + *kp;
  if (key->type == REPOKEY_TYPE_DELETED)
    return 0;
  if (key->type == REPOKEY_TYPE_VOID || key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID)
    return dp;	/* no need to forward... */
  if (key->storage != KEY_STORAGE_INCORE && key->storage != KEY_STORAGE_VERTICAL_OFFSET)
    return 0;	/* get_data will not work, no need to forward */
  dp = forward_to_key(data, *kp, keyp, dp);
  if (!dp)
    return 0;
  return get_data(data, key, &dp, 0);
}


ins_horscroll(void)
{
    pos_T	tpos;

    undisplay_dollar();
    tpos = curwin->w_cursor;
    if (gui_do_horiz_scroll(scrollbar_value, FALSE))
    {
	start_arrow(&tpos);
# ifdef FEAT_CINDENT
	can_cindent = TRUE;
# endif
    }
}


del_char_after_col(int limit_col UNUSED)
{
    if (enc_utf8 && limit_col >= 0)
    {
	colnr_T ecol = curwin->w_cursor.col + 1;

	// Make sure the cursor is at the start of a character, but
	// skip forward again when going too far back because of a
	// composing character.
	mb_adjust_cursor();
	while (curwin->w_cursor.col < (colnr_T)limit_col)
	{
	    int l = utf_ptr2len(ml_get_cursor());

	    if (l == 0)  // end of line
		break;
	    curwin->w_cursor.col += l;
	}
	if (*ml_get_cursor() == NUL || curwin->w_cursor.col == ecol)
	    return FALSE;
	del_bytes((long)((int)ecol - curwin->w_cursor.col), FALSE, TRUE);
    }
    else
	(void)del_char(FALSE);
    return TRUE;
}


set_last_insert(int c)
{
    char_u	*s;

    vim_free(last_insert);
    last_insert = alloc(MB_MAXBYTES * 3 + 5);
    if (last_insert != NULL)
    {
	s = last_insert;
	// Use the CTRL-V only when entering a special char
	if (c < ' ' || c == DEL)
	    *s++ = Ctrl_V;
	s = add_char2buf(c, s);
	*s++ = ESC;
	*s++ = NUL;
	last_insert_skip = 0;
    }
}


edit_unputchar(void)
{
    if (pc_status != PC_STATUS_UNSET && pc_row >= msg_scrolled)
    {
	if (pc_status == PC_STATUS_RIGHT)
	    ++curwin->w_wcol;
	if (pc_status == PC_STATUS_RIGHT || pc_status == PC_STATUS_LEFT)
	    redrawWinline(curwin, curwin->w_cursor.lnum);
	else
	    screen_puts(pc_bytes, pc_row - msg_scrolled, pc_col, pc_attr);
    }
}


bracketed_paste(paste_mode_T mode, int drop, garray_T *gap)
{
    int		c;
    char_u	buf[NUMBUFLEN + MB_MAXBYTES];
    int		idx = 0;
    char_u	*end = find_termcode((char_u *)"PE");
    int		ret_char = -1;
    int		save_allow_keys = allow_keys;
    int		save_paste = p_paste;

    // If the end code is too long we can't detect it, read everything.
    if (STRLEN(end) >= NUMBUFLEN)
	end = NULL;
    ++no_mapping;
    allow_keys = 0;
    if (!p_paste)
	// Also have the side effects of setting 'paste' to make it work much
	// faster.
	set_option_value((char_u *)"paste", TRUE, NULL, 0);

    for (;;)
    {
	// When the end is not defined read everything there is.
	if (end == NULL && vpeekc() == NUL)
	    break;
	do
	    c = vgetc();
	while (c == K_IGNORE || c == K_VER_SCROLLBAR || c == K_HOR_SCROLLBAR);
	if (c == NUL || got_int || (ex_normal_busy > 0 && c == Ctrl_C))
	    // When CTRL-C was encountered the typeahead will be flushed and we
	    // won't get the end sequence.  Except when using ":normal".
	    break;

	if (has_mbyte)
	    idx += (*mb_char2bytes)(c, buf + idx);
	else
	    buf[idx++] = c;
	buf[idx] = NUL;
	if (end != NULL && STRNCMP(buf, end, idx) == 0)
	{
	    if (end[idx] == NUL)
		break; // Found the end of paste code.
	    continue;
	}
	if (!drop)
	{
	    switch (mode)
	    {
		case PASTE_CMDLINE:
		    put_on_cmdline(buf, idx, TRUE);
		    break;

		case PASTE_EX:
		    if (gap != NULL && ga_grow(gap, idx) == OK)
		    {
			mch_memmove((char *)gap->ga_data + gap->ga_len,
							     buf, (size_t)idx);
			gap->ga_len += idx;
		    }
		    break;

		case PASTE_INSERT:
		    if (stop_arrow() == OK)
		    {
			c = buf[0];
			if (idx == 1 && (c == CAR || c == K_KENTER || c == NL))
			    ins_eol(c);
			else
			{
			    ins_char_bytes(buf, idx);
			    AppendToRedobuffLit(buf, idx);
			}
		    }
		    break;

		case PASTE_ONE_CHAR:
		    if (ret_char == -1)
		    {
			if (has_mbyte)
			    ret_char = (*mb_ptr2char)(buf);
			else
			    ret_char = buf[0];
		    }
		    break;
	    }
	}
	idx = 0;
    }

    --no_mapping;
    allow_keys = save_allow_keys;
    if (!save_paste)
	set_option_value((char_u *)"paste", FALSE, NULL, 0);

    return ret_char;
}


    FStructData startWriteStruct(const FStructData& parent, const char* key,
                                 int struct_flags, const char* type_name=0)
    {
        char buf[CV_FS_MAX_LEN + 1024];
        const char* data = 0;

        if ( type_name && *type_name == '\0' )
            type_name = 0;

        struct_flags = (struct_flags & (FileNode::TYPE_MASK|FileNode::FLOW)) | FileNode::EMPTY;
        if( !FileNode::isCollection(struct_flags))
            CV_Error( CV_StsBadArg,
                     "Some collection type - FileNode::SEQ or FileNode::MAP, must be specified" );

        if (type_name && memcmp(type_name, "binary", 6) == 0)
        {
            /* reset struct flag. in order not to print ']' */
            struct_flags = FileNode::SEQ;
            sprintf(buf, "!!binary |");
            data = buf;
        }
        else if( FileNode::isFlow(struct_flags))
        {
            char c = FileNode::isMap(struct_flags) ? '{' : '[';
            struct_flags |= FileNode::FLOW;

            if( type_name )
                sprintf( buf, "!!%s %c", type_name, c );
            else
            {
                buf[0] = c;
                buf[1] = '\0';
            }
            data = buf;
        }
        else if( type_name )
        {
            sprintf( buf, "!!%s", type_name );
            data = buf;
        }

        writeScalar( key, data );

        FStructData fsd;
        fsd.indent = parent.indent;
        fsd.flags = struct_flags;

        if( !FileNode::isFlow(parent.flags) )
            fsd.indent += CV_YML_INDENT + FileNode::isFlow(struct_flags);

        return fsd;
    }


    void write(const char* key, const char* str, bool quote)
    {
        char buf[CV_FS_MAX_LEN*4+16];
        char* data = (char*)str;
        int i, len;

        if( !str )
            CV_Error( CV_StsNullPtr, "Null string pointer" );

        len = (int)strlen(str);
        if( len > CV_FS_MAX_LEN )
            CV_Error( CV_StsBadArg, "The written string is too long" );

        if( quote || len == 0 || str[0] != str[len-1] || (str[0] != '\"' && str[0] != '\'') )
        {
            int need_quote = quote || len == 0 || str[0] == ' ';
            data = buf;
            *data++ = '\"';
            for( i = 0; i < len; i++ )
            {
                char c = str[i];

                if( !need_quote && !cv_isalnum(c) && c != '_' && c != ' ' && c != '-' &&
                   c != '(' && c != ')' && c != '/' && c != '+' && c != ';' )
                    need_quote = 1;

                if( !cv_isalnum(c) && (!cv_isprint(c) || c == '\\' || c == '\'' || c == '\"') )
                {
                    *data++ = '\\';
                    if( cv_isprint(c) )
                        *data++ = c;
                    else if( c == '\n' )
                        *data++ = 'n';
                    else if( c == '\r' )
                        *data++ = 'r';
                    else if( c == '\t' )
                        *data++ = 't';
                    else
                    {
                        sprintf( data, "x%02x", c );
                        data += 3;
                    }
                }
                else
                    *data++ = c;
            }
            if( !need_quote && (cv_isdigit(str[0]) ||
                                str[0] == '+' || str[0] == '-' || str[0] == '.' ))
                need_quote = 1;

            if( need_quote )
                *data++ = '\"';
            *data++ = '\0';
            data = buf + !need_quote;
        }

        writeScalar( key, data);
    }


    void writeScalar(const char* key, const char* data)
    {
        int i, keylen = 0;
        int datalen = 0;
        char* ptr;

        FStructData& current_struct = fs->getCurrentStruct();

        int struct_flags = current_struct.flags;

        if( key && key[0] == '\0' )
            key = 0;

        if( FileNode::isCollection(struct_flags) )
        {
            if( (FileNode::isMap(struct_flags) ^ (key != 0)) )
                CV_Error( CV_StsBadArg, "An attempt to add element without a key to a map, "
                         "or add element with key to sequence" );
        }
        else
        {
            fs->setNonEmpty();
            struct_flags = FileNode::EMPTY | (key ? FileNode::MAP : FileNode::SEQ);
        }

        if( key )
        {
            keylen = (int)strlen(key);
            if( keylen == 0 )
                CV_Error( CV_StsBadArg, "The key is an empty" );

            if( keylen > CV_FS_MAX_LEN )
                CV_Error( CV_StsBadArg, "The key is too long" );
        }

        if( data )
            datalen = (int)strlen(data);

        if( FileNode::isFlow(struct_flags) )
        {
            ptr = fs->bufferPtr();
            if( !FileNode::isEmptyCollection(struct_flags) )
                *ptr++ = ',';
            int new_offset = (int)(ptr - fs->bufferStart()) + keylen + datalen;
            if( new_offset > fs->wrapMargin() && new_offset - current_struct.indent > 10 )
            {
                fs->setBufferPtr(ptr);
                ptr = fs->flush();
            }
            else
                *ptr++ = ' ';
        }
        else
        {
            ptr = fs->flush();
            if( !FileNode::isMap(struct_flags) )
            {
                *ptr++ = '-';
                if( data )
                    *ptr++ = ' ';
            }
        }

        if( key )
        {
            if( !cv_isalpha(key[0]) && key[0] != '_' )
                CV_Error( CV_StsBadArg, "Key must start with a letter or _" );

            ptr = fs->resizeWriteBuffer( ptr, keylen );

            for( i = 0; i < keylen; i++ )
            {
                char c = key[i];

                ptr[i] = c;
                if( !cv_isalnum(c) && c != '-' && c != '_' && c != ' ' )
                    CV_Error( CV_StsBadArg, "Key names may only contain alphanumeric characters [a-zA-Z0-9], '-', '_' and ' '" );
            }

            ptr += keylen;
            *ptr++ = ':';
            if( !FileNode::isFlow(struct_flags) && data )
                *ptr++ = ' ';
        }

        if( data )
        {
            ptr = fs->resizeWriteBuffer( ptr, datalen );
            memcpy( ptr, data, datalen );
            ptr += datalen;
        }

        fs->setBufferPtr(ptr);
        current_struct.flags &= ~FileNode::EMPTY;
    }


bool SplashUnivariatePattern::testPosition(int x, int y) {
  double xc, yc, t;

  ictm.transform(x, y, &xc, &yc);
  if (! getParameter (xc, yc, &t))
      return false;
  return (t0 < t1) ? (t > t0 && t < t1) : (t > t1 && t < t0);
}


void SplashOutputDev::updateLineJoin(GfxState *state) {
  splash->setLineJoin(state->getLineJoin());
}


static void splashOutBlendHue(SplashColorPtr src, SplashColorPtr dest,
			      SplashColorPtr blend, SplashColorMode cm) {
  unsigned char r0, g0, b0;
#ifdef SPLASH_CMYK
  unsigned char r1, g1, b1;
  int i;
  SplashColor src2, dest2;
#endif

  switch (cm) {
  case splashModeMono1:
  case splashModeMono8:
    blend[0] = dest[0];
    break;
  case splashModeXBGR8:
    src[3] = 255;
    // fallthrough
  case splashModeRGB8:
  case splashModeBGR8:
    setSat(src[0], src[1], src[2], getSat(dest[0], dest[1], dest[2]),
	   &r0, &g0, &b0);
    setLum(r0, g0, b0, getLum(dest[0], dest[1], dest[2]),
	   &blend[0], &blend[1], &blend[2]);
    break;
#ifdef SPLASH_CMYK
  case splashModeCMYK8:
  case splashModeDeviceN8:
    for (i = 0; i < 4; i++) {
      // convert to additive
      src2[i] = 0xff - src[i];
      dest2[i] = 0xff - dest[i];
    }
    // NB: inputs have already been converted to additive mode
    setSat(src2[0], src2[1], src2[2], getSat(dest2[0], dest2[1], dest2[2]),
	   &r0, &g0, &b0);
    setLum(r0, g0, b0, getLum(dest2[0], dest2[1], dest2[2]),
	   &r1, &g1, &b1);
    blend[0] = r1;
    blend[1] = g1;
    blend[2] = b1;
    blend[3] = dest2[3];
    for (i = 0; i < 4; i++) {
      // convert back to subtractive
      blend[i] = 0xff - blend[i];
    }
    break;
#endif
  }
}


void SplashOutputDev::type3D0(GfxState *state, double wx, double wy) {
  if (likely(t3GlyphStack != nullptr)) {
    t3GlyphStack->haveDx = true;
  } else {
    error(errSyntaxWarning, -1, "t3GlyphStack was null in SplashOutputDev::type3D0");
  }
}


void SplashOutputDev::endType3Char(GfxState *state) {
  T3GlyphStack *t3gs;

  if (t3GlyphStack->cacheTag) {
    --nestCount;
    memcpy(t3GlyphStack->cacheData, bitmap->getDataPtr(),
	   t3GlyphStack->cache->glyphSize);
    delete bitmap;
    delete splash;
    bitmap = t3GlyphStack->origBitmap;
    splash = t3GlyphStack->origSplash;
    const double *ctm = state->getCTM();
    state->setCTM(ctm[0], ctm[1], ctm[2], ctm[3],
		  t3GlyphStack->origCTM4, t3GlyphStack->origCTM5);
    updateCTM(state, 0, 0, 0, 0, 0, 0);
    drawType3Glyph(state, t3GlyphStack->cache,
		   t3GlyphStack->cacheTag, t3GlyphStack->cacheData);
  }
  t3gs = t3GlyphStack;
  t3GlyphStack = t3gs->next;
  delete t3gs;
}


SplashRadialPattern::SplashRadialPattern(SplashColorMode colorModeA, GfxState *stateA, GfxRadialShading *shadingA):
  SplashUnivariatePattern(colorModeA, stateA, shadingA)
{
  SplashColor defaultColor;
  GfxColor srcColor;

  shadingA->getCoords(&x0, &y0, &r0, &dx, &dy, &dr);
  dx -= x0;
  dy -= y0;
  dr -= r0;
  a = dx*dx + dy*dy - dr*dr;
  if (fabs(a) > RADIAL_EPSILON)
    inva = 1.0 / a;
  shadingA->getColorSpace()->getDefaultColor(&srcColor);
  convertGfxColor(defaultColor, colorModeA, shadingA->getColorSpace(), &srcColor);
}


void SplashOutputDev::paintTransparencyGroup(GfxState *state, const double *bbox) {
  SplashBitmap *tBitmap;
  SplashTransparencyGroup *transpGroup;
  bool isolated;
  int tx, ty;

  tx = transpGroupStack->tx;
  ty = transpGroupStack->ty;
  tBitmap = transpGroupStack->tBitmap;
  isolated = transpGroupStack->isolated;

  // paint the transparency group onto the parent bitmap
  // - the clip path was set in the parent's state)
  if (tx < bitmap->getWidth() && ty < bitmap->getHeight()) {
    SplashCoord knockoutOpacity = (transpGroupStack->next != nullptr) ? transpGroupStack->next->knockoutOpacity
                                                                   : transpGroupStack->knockoutOpacity;
    splash->setOverprintMask(0xffffffff, false);
    splash->composite(tBitmap, 0, 0, tx, ty,
      tBitmap->getWidth(), tBitmap->getHeight(),
      false, !isolated, transpGroupStack->next != nullptr && transpGroupStack->next->knockout, knockoutOpacity);
    fontEngine->setAA(transpGroupStack->fontAA);
    if (transpGroupStack->next != nullptr && transpGroupStack->next->shape != nullptr) {
      transpGroupStack->next->knockout = true;
    }
  }

  // pop the stack
  transpGroup = transpGroupStack;
  transpGroupStack = transpGroup->next;
  if (transpGroupStack != nullptr && transpGroup->knockoutOpacity < transpGroupStack->knockoutOpacity) {
    transpGroupStack->knockoutOpacity = transpGroup->knockoutOpacity;
  }
  delete transpGroup->shape;
  delete transpGroup;

  delete tBitmap;
}


bool SplashOutputDev::functionShadedFill(GfxState *state, GfxFunctionShading *shading) {
  SplashFunctionPattern *pattern = new SplashFunctionPattern(colorMode, state, shading);
  double xMin, yMin, xMax, yMax;
  bool vaa = getVectorAntialias();
  // restore vector antialias because we support it here
  setVectorAntialias(true);

  bool retVal = false;
  // get the clip region bbox
  if (pattern->getShading()->getHasBBox()) {
    pattern->getShading()->getBBox(&xMin, &yMin, &xMax, &yMax);
  } else {
    state->getClipBBox(&xMin, &yMin, &xMax, &yMax);

    xMin = floor (xMin);
    yMin = floor (yMin);
    xMax = ceil (xMax);
    yMax = ceil (yMax);

    {
      Matrix ctm, ictm;
      double x[4], y[4];
      int i;

      state->getCTM(&ctm);
      ctm.invertTo(&ictm);

      ictm.transform(xMin, yMin, &x[0], &y[0]);
      ictm.transform(xMax, yMin, &x[1], &y[1]);
      ictm.transform(xMin, yMax, &x[2], &y[2]);
      ictm.transform(xMax, yMax, &x[3], &y[3]);

      xMin = xMax = x[0];
      yMin = yMax = y[0];
      for (i = 1; i < 4; i++) {
        xMin = std::min<double>(xMin, x[i]);
        yMin = std::min<double>(yMin, y[i]);
        xMax = std::max<double>(xMax, x[i]);
        yMax = std::max<double>(yMax, y[i]);
      }
    }
  }

  // fill the region
  state->moveTo(xMin, yMin);
  state->lineTo(xMax, yMin);
  state->lineTo(xMax, yMax);
  state->lineTo(xMin, yMax);
  state->closePath();
  SplashPath path = convertPath(state, state->getPath(), true);

#ifdef SPLASH_CMYK
  pattern->getShading()->getColorSpace()->createMapping(bitmap->getSeparationList(), SPOT_NCOMPS);
#endif
  setOverprintMask(pattern->getShading()->getColorSpace(), state->getFillOverprint(),
		   state->getOverprintMode(), nullptr);
  retVal = (splash->shadedFill(&path, pattern->getShading()->getHasBBox(), pattern) == splashOk);
  state->clearPath();
  setVectorAntialias(vaa);

  delete pattern;

  return retVal;
}


static void splashOutBlendHardLight(SplashColorPtr src, SplashColorPtr dest,
				    SplashColorPtr blend, SplashColorMode cm) {
  int i;

#ifdef SPLASH_CMYK
  if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
    for (i = 0; i < splashColorModeNComps[cm]; ++i) {
      dest[i] = 255 - dest[i];
      src[i] = 255 - src[i];
    }
  }
#endif
  {
    for (i = 0; i < splashColorModeNComps[cm]; ++i) {
      blend[i] = src[i] < 0x80
                   ? (dest[i] * 2 * src[i]) / 255
                   : 255 - 2 * ((255 - dest[i]) * (255 - src[i])) / 255;
    }
  }
#ifdef SPLASH_CMYK
  if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
    for (i = 0; i < splashColorModeNComps[cm]; ++i) {
      dest[i] = 255 - dest[i];
      src[i] = 255 - src[i];
      blend[i] = 255 - blend[i];
    }
  }
#endif
}


void SplashOutputDev::getModRegion(int *xMin, int *yMin,
				   int *xMax, int *yMax) {
  splash->getModRegion(xMin, yMin, xMax, yMax);
}


void SplashOutputDev::setPaperColor(SplashColorPtr paperColorA) {
  splashColorCopy(paperColor, paperColorA);
}


static void splashOutBlendLuminosity(SplashColorPtr src, SplashColorPtr dest,
				     SplashColorPtr blend,
				     SplashColorMode cm) {
#ifdef SPLASH_CMYK
  unsigned char r, g, b;
  int i;
  SplashColor src2, dest2;
#endif

  switch (cm) {
  case splashModeMono1:
  case splashModeMono8:
    blend[0] = dest[0];
    break;
  case splashModeXBGR8:
    src[3] = 255;
    // fallthrough
  case splashModeRGB8:
  case splashModeBGR8:
    setLum(dest[0], dest[1], dest[2], getLum(src[0], src[1], src[2]),
	   &blend[0], &blend[1], &blend[2]);
    break;
#ifdef SPLASH_CMYK
  case splashModeCMYK8:
  case splashModeDeviceN8:
    for (i = 0; i < 4; i++) {
      // convert to additive
      src2[i] = 0xff - src[i];
      dest2[i] = 0xff - dest[i];
    }
    setLum(dest2[0], dest2[1], dest2[2], getLum(src2[0], src2[1], src2[2]),
	   &r, &g, &b);
    blend[0] = r;
    blend[1] = g;
    blend[2] = b;
    blend[3] = src2[3];
    for (i = 0; i < 4; i++) {
      // convert back to subtractive
      blend[i] = 0xff - blend[i];
    }
    break;
#endif
  }
}


void SplashOutputDev::updateStrokeColor(GfxState *state) {
  GfxGray gray;
  GfxRGB rgb;
#ifdef SPLASH_CMYK
  GfxCMYK cmyk;
  GfxColor deviceN;
#endif

  switch (colorMode) {
  case splashModeMono1:
  case splashModeMono8:
    state->getStrokeGray(&gray);
    splash->setStrokePattern(getColor(gray));
    break;
  case splashModeXBGR8:
  case splashModeRGB8:
  case splashModeBGR8:
    state->getStrokeRGB(&rgb);
    splash->setStrokePattern(getColor(&rgb));
    break;
#ifdef SPLASH_CMYK
  case splashModeCMYK8:
    state->getStrokeCMYK(&cmyk);
    splash->setStrokePattern(getColor(&cmyk));
    break;
  case splashModeDeviceN8:
    state->getStrokeDeviceN(&deviceN);
    splash->setStrokePattern(getColor(&deviceN));
    break;
#endif
  }
}


static void splashOutBlendMultiply(SplashColorPtr src, SplashColorPtr dest,
				   SplashColorPtr blend, SplashColorMode cm) {
  int i;

#ifdef SPLASH_CMYK
  if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
    for (i = 0; i < splashColorModeNComps[cm]; ++i) {
      dest[i] = 255 - dest[i];
      src[i] = 255 - src[i];
    }
  }
#endif
  {
    for (i = 0; i < splashColorModeNComps[cm]; ++i) {
      blend[i] = (dest[i] * src[i]) / 255;
    }
  }
#ifdef SPLASH_CMYK
  if (cm == splashModeCMYK8 || cm == splashModeDeviceN8) {
    for (i = 0; i < splashColorModeNComps[cm]; ++i) {
      dest[i] = 255 - dest[i];
      src[i] = 255 - src[i];
      blend[i] = 255 - blend[i];
    }
  }
#endif
}


bool SplashOutputDev::imageMaskSrc(void *data, SplashColorPtr line) {
  SplashOutImageMaskData *imgMaskData = (SplashOutImageMaskData *)data;
  unsigned char *p;
  SplashColorPtr q;
  int x;

  if (imgMaskData->y == imgMaskData->height) {
    return false;
  }
  if (!(p = imgMaskData->imgStr->getLine())) {
    return false;
  }
  for (x = 0, q = line; x < imgMaskData->width; ++x) {
    *q++ = *p++ ^ imgMaskData->invert;
  }
  ++imgMaskData->y;
  return true;
}


static void printval(void *value, int type, int level, struct printparam* pp){
	struct node pn, cn;
	struct property *p;
	int i;

	pn.iteration = NULL;
	pn.parent = NULL;
	pn.type = type;
	pn.value =  value;

	printstr(pp, "<item>");
	for(p = datatypes[type].properties; p; ) {
		cn.iteration = NULL;
		cn.parent = &pn;
		cn.type = p->type;
		cn.value =  (*p->e_f)(&pn);
		if(cn.value){
			for(i = 0; i < level; i++) printstr(pp, "\t");
			if(strcmp(p->name, "next")){
				printstr(pp, "<parameter>");
				printstr(pp, "<name>");
				printstr(pp, p->name);
				printstr(pp, "</name>");
				printstr(pp, "<type>");
				printstr(pp, datatypes[p->type].type);
				printstr(pp, "</type>");
				printstr(pp, "<description>");
				printstr(pp, p->description);
				printstr(pp, "</description>");
			}
			if(datatypes[p->type].p_f){
				printstr(pp, "<value><![CDATA[");
				(*datatypes[p->type].p_f)(&cn, stdcbf, pp);
				printstr(pp, "]]></value>\n");
				printstr(pp, "</parameter>");
			}
			else {
				if(!strcmp(p->name, "next")){
/*					printstr(pp, "<!-- -------------------- -->\n"); */
					printstr(pp, "</item>\n<item>");
					p = datatypes[type].properties;
					pn.value = value = cn.value;
					continue;
				}
				else {
					printstr(pp, "\n");
					printval(cn.value, cn.type, level+1, pp);
					printstr(pp, "</parameter>");
				}
			}
		}
		p=p->next;
	}
	printstr(pp, "</item>");
}


smtp_proceed_wiz(struct smtp_session *s, const char *args)
{
	smtp_reply(s, "500 %s %s: this feature is not supported yet ;-)",
	    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
	    esc_description(ESC_INVALID_COMMAND));
}


smtp_proceed_commit(struct smtp_session *s, const char *args)
{
	smtp_message_end(s->tx);
}


smtp_tx_rollback(struct smtp_tx *tx)
{
	m_create(p_queue, IMSG_SMTP_MESSAGE_ROLLBACK, 0, 0, -1);
	m_add_msgid(p_queue, tx->msgid);
	m_close(p_queue);
	smtp_report_tx_rollback(tx->session, tx->msgid);
	smtp_report_tx_reset(tx->session, tx->msgid);
	smtp_filter_data_end(tx->session);
}


smtp_tx_commit(struct smtp_tx *tx)
{
	m_create(p_queue, IMSG_SMTP_MESSAGE_COMMIT, 0, 0, -1);
	m_add_id(p_queue, tx->session->id);
	m_add_msgid(p_queue, tx->msgid);
	m_close(p_queue);
	tree_xset(&wait_queue_commit, tx->session->id, tx->session);
	smtp_filter_data_end(tx->session);
}


smtp_proceed_helo(struct smtp_session *s, const char *args)
{
	(void)strlcpy(s->helo, args, sizeof(s->helo));
	s->flags &= SF_SECURE | SF_AUTHENTICATED | SF_VERIFIED;

	smtp_report_link_identify(s, "HELO", s->helo);

	smtp_enter_state(s, STATE_HELO);

	smtp_reply(s, "250 %s Hello %s %s%s%s, pleased to meet you",
	    s->smtpname,
	    s->helo,
	    s->ss.ss_family == AF_INET6 ? "" : "[",
	    ss_to_text(&s->ss),
	    s->ss.ss_family == AF_INET6 ? "" : "]");
}


smtp_tx_dataline(struct smtp_tx *tx, const char *line)
{
	struct rfc5322_result res;
	int r;

	log_trace(TRACE_SMTP, "<<< [MSG] %s", line);

	if (!strcmp(line, ".")) {
		smtp_report_protocol_client(tx->session, ".");
		log_trace(TRACE_SMTP, "<<< [EOM]");
		if (tx->error)
			return 1;
		line = NULL;
	}
	else {
		/* ignore data line if an error is set */
		if (tx->error)
			return 0;

		/* escape lines starting with a '.' */
		if (line[0] == '.')
			line += 1;
	}

	if (rfc5322_push(tx->parser, line) == -1) {
		log_warnx("failed to push dataline");
		tx->error = TX_ERROR_INTERNAL;
		return 0;
	}

	for(;;) {
		r = rfc5322_next(tx->parser, &res);
		switch (r) {
		case -1:
			if (errno == ENOMEM)
				tx->error = TX_ERROR_INTERNAL;
			else
				tx->error = TX_ERROR_MALFORMED;
			return 0;

		case RFC5322_NONE:
			/* Need more data */
			return 0;

		case RFC5322_HEADER_START:
			/* ignore bcc */
			if (!strcasecmp("Bcc", res.hdr))
				continue;

			if (!strcasecmp("To", res.hdr) ||
			    !strcasecmp("Cc", res.hdr) ||
			    !strcasecmp("From", res.hdr)) {
				rfc5322_unfold_header(tx->parser);
				continue;
			}

			if (!strcasecmp("Received", res.hdr)) {
				if (++tx->rcvcount >= MAX_HOPS_COUNT) {
					log_warnx("warn: loop detected");
					tx->error = TX_ERROR_LOOP;
					return 0;
				}
			}
			else if (!tx->has_date && !strcasecmp("Date", res.hdr))
				tx->has_date = 1;
			else if (!tx->has_message_id &&
			    !strcasecmp("Message-Id", res.hdr))
				tx->has_message_id = 1;

			smtp_message_printf(tx, "%s:%s\n", res.hdr, res.value);
			break;

		case RFC5322_HEADER_CONT:

			if (!strcasecmp("Bcc", res.hdr) ||
			    !strcasecmp("To", res.hdr) ||
			    !strcasecmp("Cc", res.hdr) ||
			    !strcasecmp("From", res.hdr))
				continue;

			smtp_message_printf(tx, "%s\n", res.value);
			break;

		case RFC5322_HEADER_END:
			if (!strcasecmp("To", res.hdr) ||
			    !strcasecmp("Cc", res.hdr) ||
			    !strcasecmp("From", res.hdr))
				header_domain_append_callback(tx, res.hdr,
				    res.value);
			break;

		case RFC5322_END_OF_HEADERS:
			if (tx->session->listener->local ||
			    tx->session->listener->port == 587) {

				if (!tx->has_date) {
					log_debug("debug: %p: adding Date", tx);
					smtp_message_printf(tx, "Date: %s\n",
					    time_to_text(tx->time));
				}

				if (!tx->has_message_id) {
					log_debug("debug: %p: adding Message-ID", tx);
					smtp_message_printf(tx,
					    "Message-ID: <%016"PRIx64"@%s>\n",
					    generate_uid(),
					    tx->session->listener->hostname);
				}
			}
			break;

		case RFC5322_BODY_START:
		case RFC5322_BODY:
			smtp_message_printf(tx, "%s\n", res.value);
			break;

		case RFC5322_END_OF_MESSAGE:
			return 1;

		default:
			fatalx("%s", __func__);
		}
	}
}


smtp_getaddrinfo_cb(void *arg, int gaierrno, struct addrinfo *ai0)
{
	struct smtp_session *s = arg;
	struct addrinfo *ai;
	char fwd[64], rev[64];

	if (gaierrno) {
		if (gaierrno == EAI_NODATA || gaierrno == EAI_NONAME)
			s->fcrdns = 0;
		else {
			log_warnx("getaddrinfo: %s: %s", s->rdns,
			    gai_strerror(gaierrno));
			s->fcrdns = -1;
		}
	}
	else {
		strlcpy(rev, ss_to_text(&s->ss), sizeof(rev));
		for (ai = ai0; ai; ai = ai->ai_next) {
			strlcpy(fwd, sa_to_text(ai->ai_addr), sizeof(fwd));
			if (!strcmp(fwd, rev)) {
				s->fcrdns = 1;
				break;
			}
		}
		freeaddrinfo(ai0);
	}

	smtp_lookup_servername(s);
}


smtp_tx(struct smtp_session *s)
{
	struct smtp_tx *tx;

	tx = calloc(1, sizeof(*tx));
	if (tx == NULL)
		return 0;

	TAILQ_INIT(&tx->rcpts);

	s->tx = tx;
	tx->session = s;

	/* setup the envelope */
	tx->evp.ss = s->ss;
	(void)strlcpy(tx->evp.tag, s->listener->tag, sizeof(tx->evp.tag));
	(void)strlcpy(tx->evp.smtpname, s->smtpname, sizeof(tx->evp.smtpname));
	(void)strlcpy(tx->evp.hostname, s->rdns, sizeof tx->evp.hostname);
	(void)strlcpy(tx->evp.helo, s->helo, sizeof(tx->evp.helo));
	(void)strlcpy(tx->evp.username, s->username, sizeof(tx->evp.username));

	if (s->flags & SF_BOUNCE)
		tx->evp.flags |= EF_BOUNCE;
	if (s->flags & SF_AUTHENTICATED)
		tx->evp.flags |= EF_AUTHENTICATED;

	if ((tx->parser = rfc5322_parser_new()) == NULL) {
		free(tx);
		return 0;
	}

	return 1;
}


smtp_report_tx_reset(struct smtp_session *s, uint32_t msgid)
{
	if (! SESSION_FILTERED(s))
		return;

	report_smtp_tx_reset("smtp-in", s->id, msgid);
}


header_domain_append_callback(struct smtp_tx *tx, const char *hdr,
    const char *val)
{
	size_t			i, j, linelen;
	int			escape, quote, comment, skip;
	char			buffer[APPEND_DOMAIN_BUFFER_SIZE];
	const char *line, *end;

	if (smtp_message_printf(tx, "%s:", hdr) == -1)
		return;

	j = 0;
	escape = quote = comment = skip = 0;
	memset(buffer, 0, sizeof buffer);

	for (line = val; line; line = end) {
		end = strchr(line, '\n');
		if (end) {
			linelen = end - line;
			end++;
		}
		else
			linelen = strlen(line);

		for (i = 0; i < linelen; ++i) {
			if (line[i] == '(' && !escape && !quote)
				comment++;
			if (line[i] == '"' && !escape && !comment)
				quote = !quote;
			if (line[i] == ')' && !escape && !quote && comment)
				comment--;
			if (line[i] == '\\' && !escape && !comment && !quote)
				escape = 1;
			else
				escape = 0;

			/* found a separator, buffer contains a full address */
			if (line[i] == ',' && !escape && !quote && !comment) {
				if (!skip && j + strlen(tx->session->listener->hostname) + 1 < sizeof buffer) {
					header_append_domain_buffer(buffer, tx->session->listener->hostname, sizeof buffer);
					if (tx->session->flags & SF_AUTHENTICATED &&
					    tx->session->listener->sendertable[0] &&
					    tx->session->listener->flags & F_MASQUERADE &&
					    !(strcasecmp(hdr, "From")))
						header_address_rewrite_buffer(buffer, mailaddr_to_text(&tx->evp.sender),
						    sizeof buffer);
				}
				if (smtp_message_printf(tx, "%s,", buffer) == -1)
					return;
				j = 0;
				skip = 0;
				memset(buffer, 0, sizeof buffer);
			}
			else {
				if (skip) {
					if (smtp_message_printf(tx, "%c", line[i]) == -1)
						return;
				}
				else {
					buffer[j++] = line[i];
					if (j == sizeof (buffer) - 1) {
						if (smtp_message_printf(tx, "%s", buffer) == -1)
							return;
						skip = 1;
						j = 0;
						memset(buffer, 0, sizeof buffer);
					}
				}
			}
		}
		if (skip) {
			if (smtp_message_printf(tx, "\n") == -1)
				return;
		}
		else {
			buffer[j++] = '\n';
			if (j == sizeof (buffer) - 1) {
				if (smtp_message_printf(tx, "%s", buffer) == -1)
					return;
				skip = 1;
				j = 0;
				memset(buffer, 0, sizeof buffer);
			}
		}
	}

	/* end of header, if buffer is not empty we'll process it */
	if (buffer[0]) {
		if (j + strlen(tx->session->listener->hostname) + 1 < sizeof buffer) {
			header_append_domain_buffer(buffer, tx->session->listener->hostname, sizeof buffer);
			if (tx->session->flags & SF_AUTHENTICATED &&
			    tx->session->listener->sendertable[0] &&
			    tx->session->listener->flags & F_MASQUERADE &&
			    !(strcasecmp(hdr, "From")))
				header_address_rewrite_buffer(buffer, mailaddr_to_text(&tx->evp.sender),
				    sizeof buffer);
		}
		smtp_message_printf(tx, "%s", buffer);
	}
}


smtp_tx_open_message(struct smtp_tx *tx)
{
	m_create(p_queue, IMSG_SMTP_MESSAGE_OPEN, 0, 0, -1);
	m_add_id(p_queue, tx->session->id);
	m_add_msgid(p_queue, tx->msgid);
	m_close(p_queue);
	tree_xset(&wait_queue_fd, tx->session->id, tx->session);
}


smtp_cert_init_cb(void *arg, int status, const char *name, const void *cert,
    size_t cert_len)
{
	struct smtp_session *s = arg;
	void *ssl, *ssl_ctx;

	tree_pop(&wait_ssl_init, s->id);

	if (status == CA_FAIL) {
		log_info("%016"PRIx64" smtp disconnected "
		    "reason=ca-failure",
		    s->id);
		smtp_free(s, "CA failure");
		return;
	}

	ssl_ctx = dict_get(env->sc_ssl_dict, name);
	ssl = ssl_smtp_init(ssl_ctx, s->listener->flags & F_TLS_VERIFY);
	io_set_read(s->io);
	io_start_tls(s->io, ssl);
}


smtp_filter_begin(struct smtp_session *s)
{
	if (!SESSION_FILTERED(s))
		return;

	m_create(p_lka, IMSG_FILTER_SMTP_BEGIN, 0, 0, -1);
	m_add_id(p_lka, s->id);
	m_add_string(p_lka, s->listener->filter_name);
	m_close(p_lka);
}


smtp_proceed_help(struct smtp_session *s, const char *args)
{
	const char *code = esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS);

	smtp_reply(s, "214-%s This is " SMTPD_NAME, code);
	smtp_reply(s, "214-%s To report bugs in the implementation, "
	    "please contact bugs@openbsd.org", code);
	smtp_reply(s, "214-%s with full details", code);
	smtp_reply(s, "214 %s End of HELP info", code);
}


mbfl_filt_conv_wchar_big5(int c, mbfl_convert_filter *filter)
{
	int k;
	int c1, s, c2;

	s = 0;
	if (c >= ucs_a1_big5_table_min && c < ucs_a1_big5_table_max) {
		s = ucs_a1_big5_table[c - ucs_a1_big5_table_min];
	} else if (c >= ucs_a2_big5_table_min && c < ucs_a2_big5_table_max) {
		s = ucs_a2_big5_table[c - ucs_a2_big5_table_min];
	} else if (c >= ucs_a3_big5_table_min && c < ucs_a3_big5_table_max) {
		s = ucs_a3_big5_table[c - ucs_a3_big5_table_min];
	} else if (c >= ucs_i_big5_table_min && c < ucs_i_big5_table_max) {
		s = ucs_i_big5_table[c - ucs_i_big5_table_min];
	} else if (c >= ucs_pua_big5_table_min && c < ucs_pua_big5_table_max) {
		s = ucs_pua_big5_table[c - ucs_pua_big5_table_min];
	} else if (c >= ucs_r1_big5_table_min && c < ucs_r1_big5_table_max) {
		s = ucs_r1_big5_table[c - ucs_r1_big5_table_min];
	} else if (c >= ucs_r2_big5_table_min && c < ucs_r2_big5_table_max) {
		s = ucs_r2_big5_table[c - ucs_r2_big5_table_min];
	}

	if (filter->to->no_encoding == mbfl_no_encoding_cp950) {
		if (c >= 0xe000 && c <= 0xf848) { /* PUA for CP950 */
			for (k = 0; k < sizeof(cp950_pua_tbl)/(sizeof(unsigned short)*4); k++) {
				if (c <= cp950_pua_tbl[k][1]) {
					break;
				}
			}
			c1 = c - cp950_pua_tbl[k][0];
			if ((cp950_pua_tbl[k][2] & 0xff) == 0x40) {
				c2 = cp950_pua_tbl[k][2] >> 8;
				s = ((c1 / 157) + c2) << 8; c1 %= 157;
				s |= c1 + (c1 >= 0x3f ? 0x62 : 0x40);
			} else {
				s = c1 + cp950_pua_tbl[k][2];
			}
		}

		if (c == 0x80) {
			s = 0x80;
		} else if (c == 0xf8f8) {
			s = 0xff;
		} else if (c == 0x256d) {
			s = 0xa27e;
		} else if (c == 0x256e) {
			s = 0xa2a1;
		} else if (c == 0x256f) {
			s = 0xa2a3;
		} else if (c == 0x2570) {
			s = 0xa2a2;
		}
	}

	if (s <= 0) {
		c1 = c & ~MBFL_WCSPLANE_MASK;
		if (c1 == MBFL_WCSPLANE_BIG5) {
			s = c & MBFL_WCSPLANE_MASK;
		}
		if (c == 0) {
			s = 0;
		} else if (s <= 0) {
			s = -1;
		}
	}
	if (s >= 0) {
		if (s <= 0x80 || s == 0xff) {	/* latin */
			CK((*filter->output_function)(s, filter->data));
		} else {
			CK((*filter->output_function)((s >> 8) & 0xff, filter->data));
			CK((*filter->output_function)(s & 0xff, filter->data));
		}
	} else {
		if (filter->illegal_mode != MBFL_OUTPUTFILTER_ILLEGAL_MODE_NONE) {
			CK(mbfl_filt_conv_illegal_output(c, filter));
		}
	}

	return c;
}


int slirp_inet_aton(const char *cp, struct in_addr *ia)
{
    uint32_t addr = inet_addr(cp);
    if (addr == 0xffffffff) {
        return 0;
    }
    ia->s_addr = addr;
    return 1;
}


int slirp_getpeername_wrap(int sockfd, struct sockaddr *addr, int *addrlen)
{
    int ret;
    ret = getpeername(sockfd, addr, addrlen);
    if (ret < 0) {
        errno = socket_error();
    }
    return ret;
}


int slirp_ioctlsocket_wrap(int fd, int req, void *val)
{
    int ret;
    ret = ioctlsocket(fd, req, val);
    if (ret < 0) {
        errno = socket_error();
    }
    return ret;
}


int slirp_bind_wrap(int sockfd, const struct sockaddr *addr, int addrlen)
{
    int ret;
    ret = bind(sockfd, addr, addrlen);
    if (ret < 0) {
        errno = socket_error();
    }
    return ret;
}


static int fib6_nh_drop_pcpu_from(struct fib6_nh *nh, void *_arg)
{
	struct fib6_nh_pcpu_arg *arg = _arg;

	__fib6_drop_pcpu_from(nh, arg->from, arg->table);
	return 0;
}


static void fib6_start_gc(struct net *net, struct fib6_info *rt)
{
	if (!timer_pending(&net->ipv6.ip6_fib_timer) &&
	    (rt->fib6_flags & RTF_EXPIRES))
		mod_timer(&net->ipv6.ip6_fib_timer,
			  jiffies + net->ipv6.sysctl.ip6_rt_gc_interval);
}


static int inet6_dump_fib(struct sk_buff *skb, struct netlink_callback *cb)
{
	struct rt6_rtnl_dump_arg arg = { .filter.dump_exceptions = true,
					 .filter.dump_routes = true };
	const struct nlmsghdr *nlh = cb->nlh;
	struct net *net = sock_net(skb->sk);
	unsigned int h, s_h;
	unsigned int e = 0, s_e;
	struct fib6_walker *w;
	struct fib6_table *tb;
	struct hlist_head *head;
	int res = 0;

	if (cb->strict_check) {
		int err;

		err = ip_valid_fib_dump_req(net, nlh, &arg.filter, cb);
		if (err < 0)
			return err;
	} else if (nlmsg_len(nlh) >= sizeof(struct rtmsg)) {
		struct rtmsg *rtm = nlmsg_data(nlh);

		if (rtm->rtm_flags & RTM_F_PREFIX)
			arg.filter.flags = RTM_F_PREFIX;
	}

	w = (void *)cb->args[2];
	if (!w) {
		/* New dump:
		 *
		 * 1. hook callback destructor.
		 */
		cb->args[3] = (long)cb->done;
		cb->done = fib6_dump_done;

		/*
		 * 2. allocate and initialize walker.
		 */
		w = kzalloc(sizeof(*w), GFP_ATOMIC);
		if (!w)
			return -ENOMEM;
		w->func = fib6_dump_node;
		cb->args[2] = (long)w;
	}

	arg.skb = skb;
	arg.cb = cb;
	arg.net = net;
	w->args = &arg;

	if (arg.filter.table_id) {
		tb = fib6_get_table(net, arg.filter.table_id);
		if (!tb) {
			if (arg.filter.dump_all_families)
				goto out;

			NL_SET_ERR_MSG_MOD(cb->extack, "FIB table does not exist");
			return -ENOENT;
		}

		if (!cb->args[0]) {
			res = fib6_dump_table(tb, skb, cb);
			if (!res)
				cb->args[0] = 1;
		}
		goto out;
	}

	s_h = cb->args[0];
	s_e = cb->args[1];

	rcu_read_lock();
	for (h = s_h; h < FIB6_TABLE_HASHSZ; h++, s_e = 0) {
		e = 0;
		head = &net->ipv6.fib_table_hash[h];
		hlist_for_each_entry_rcu(tb, head, tb6_hlist) {
			if (e < s_e)
				goto next;
			res = fib6_dump_table(tb, skb, cb);
			if (res != 0)
				goto out_unlock;
next:
			e++;
		}
	}
out_unlock:
	rcu_read_unlock();
	cb->args[1] = e;
	cb->args[0] = h;
out:
	res = res < 0 ? res : skb->len;
	if (res <= 0)
		fib6_dump_end(cb);
	return res;
}


static void node_free_rcu(struct rcu_head *head)
{
	struct fib6_node *fn = container_of(head, struct fib6_node, rcu);

	kmem_cache_free(fib6_node_kmem, fn);
}


static int fib6_walk_continue(struct fib6_walker *w)
{
	struct fib6_node *fn, *pn, *left, *right;

	/* w->root should always be table->tb6_root */
	WARN_ON_ONCE(!(w->root->fn_flags & RTN_TL_ROOT));

	for (;;) {
		fn = w->node;
		if (!fn)
			return 0;

		switch (w->state) {
#ifdef CONFIG_IPV6_SUBTREES
		case FWS_S:
			if (FIB6_SUBTREE(fn)) {
				w->node = FIB6_SUBTREE(fn);
				continue;
			}
			w->state = FWS_L;
#endif
			/* fall through */
		case FWS_L:
			left = rcu_dereference_protected(fn->left, 1);
			if (left) {
				w->node = left;
				w->state = FWS_INIT;
				continue;
			}
			w->state = FWS_R;
			/* fall through */
		case FWS_R:
			right = rcu_dereference_protected(fn->right, 1);
			if (right) {
				w->node = right;
				w->state = FWS_INIT;
				continue;
			}
			w->state = FWS_C;
			w->leaf = rcu_dereference_protected(fn->leaf, 1);
			/* fall through */
		case FWS_C:
			if (w->leaf && fn->fn_flags & RTN_RTINFO) {
				int err;

				if (w->skip) {
					w->skip--;
					goto skip;
				}

				err = w->func(w);
				if (err)
					return err;

				w->count++;
				continue;
			}
skip:
			w->state = FWS_U;
			/* fall through */
		case FWS_U:
			if (fn == w->root)
				return 0;
			pn = rcu_dereference_protected(fn->parent, 1);
			left = rcu_dereference_protected(pn->left, 1);
			right = rcu_dereference_protected(pn->right, 1);
			w->node = pn;
#ifdef CONFIG_IPV6_SUBTREES
			if (FIB6_SUBTREE(pn) == fn) {
				WARN_ON(!(fn->fn_flags & RTN_ROOT));
				w->state = FWS_L;
				continue;
			}
#endif
			if (left == fn) {
				w->state = FWS_R;
				continue;
			}
			if (right == fn) {
				w->state = FWS_C;
				w->leaf = rcu_dereference_protected(w->node->leaf, 1);
				continue;
			}
#if RT6_DEBUG >= 2
			WARN_ON(1);
#endif
		}
	}
}


static void node_free(struct net *net, struct fib6_node *fn)
{
	call_rcu(&fn->rcu, node_free_rcu);
	net->ipv6.rt6_stats->fib_nodes--;
}


static struct fib6_table *ipv6_route_seq_next_table(struct fib6_table *tbl,
						    struct net *net)
{
	unsigned int h;
	struct hlist_node *node;

	if (tbl) {
		h = (tbl->tb6_id & (FIB6_TABLE_HASHSZ - 1)) + 1;
		node = rcu_dereference_bh(hlist_next_rcu(&tbl->tb6_hlist));
	} else {
		h = 0;
		node = NULL;
	}

	while (!node && h < FIB6_TABLE_HASHSZ) {
		node = rcu_dereference_bh(
			hlist_first_rcu(&net->ipv6.fib_table_hash[h++]));
	}
	return hlist_entry_safe(node, struct fib6_table, tb6_hlist);
}


int fib6_tables_dump(struct net *net, struct notifier_block *nb)
{
	struct fib6_dump_arg arg;
	struct fib6_walker *w;
	unsigned int h;

	w = kzalloc(sizeof(*w), GFP_ATOMIC);
	if (!w)
		return -ENOMEM;

	w->func = fib6_node_dump;
	arg.net = net;
	arg.nb = nb;
	w->args = &arg;

	for (h = 0; h < FIB6_TABLE_HASHSZ; h++) {
		struct hlist_head *head = &net->ipv6.fib_table_hash[h];
		struct fib6_table *tb;

		hlist_for_each_entry_rcu(tb, head, tb6_hlist)
			fib6_table_dump(net, tb, w);
	}

	kfree(w);

	return 0;
}


ipmi_fru_query_new_value(uint8_t *data,int offset, size_t len)
{
	bool status = false;
	int ret;
	char answer;

	printf("Would you like to change this value <y/n> ? ");
	ret = scanf("%c", &answer);
	if (ret != 1) {
		return false;
	}

	if( answer == 'y' || answer == 'Y' ){
		int i;
		unsigned int *holder;

		holder = malloc(len);
		printf(
		 "Enter hex values for each of the %d entries (lsb first), "
		 "hit <enter> between entries\n", (int)len);

		/* I can't assign scanf' %x into a single char */
		for( i=0;i<len;i++ ){
			ret = scanf("%x", holder+i);
			if (ret != 1) {
				free_n(&holder);
				return false;
			}
		}
		for( i=0;i<len;i++ ){
			data[offset++] = (unsigned char) *(holder+i);
		}
		/* &data[offset++] */
		free_n(&holder);
		status = true;
	}
	else{
		printf("Entered %c\n",answer);
	}

	return status;
}


ipmi_fru_edit_multirec(struct ipmi_intf * intf, uint8_t id ,
												int argc, char ** argv)
{

	struct ipmi_rs * rsp;
	struct ipmi_rq req;
	struct fru_info fru;
	struct fru_header header;
	uint8_t msg_data[4];

	uint16_t retStatus = 0;
	uint32_t offFruMultiRec;
	uint32_t fruMultiRecSize = 0;
	struct fru_info fruInfo;
	retStatus = ipmi_fru_get_multirec_location_from_fru(intf, id, &fruInfo,
								&offFruMultiRec,
								&fruMultiRecSize);
	if (retStatus != 0) {
		return retStatus;
	}

	lprintf(LOG_DEBUG, "FRU Size        : %lu\n", fruMultiRecSize);
	lprintf(LOG_DEBUG, "Multi Rec offset: %lu\n", offFruMultiRec);

	{

	memset(&fru, 0, sizeof(struct fru_info));
	memset(&header, 0, sizeof(struct fru_header));

	/*
	* get info about this FRU
	*/
	memset(msg_data, 0, 4);
	msg_data[0] = id;

	memset(&req, 0, sizeof(req));
	req.msg.netfn = IPMI_NETFN_STORAGE;
	req.msg.cmd = GET_FRU_INFO;
	req.msg.data = msg_data;
	req.msg.data_len = 1;

	rsp = intf->sendrecv(intf, &req);
	if (!rsp) {
		printf(" Device not present (No Response)\n");
		return -1;
	}
	if (rsp->ccode) {
		printf(" Device not present (%s)\n",
			val2str(rsp->ccode, completion_code_vals));
		return -1;
	}

	memset(&fru, 0, sizeof(fru));
	fru.size = (rsp->data[1] << 8) | rsp->data[0];
	fru.access = rsp->data[2] & 0x1;

	lprintf(LOG_DEBUG, "fru.size = %d bytes (accessed by %s)",
		fru.size, fru.access ? "words" : "bytes");

	if (fru.size < 1) {
		lprintf(LOG_ERR, " Invalid FRU size %d", fru.size);
		return -1;
	}
	}

	{
		uint8_t * fru_data;
		uint32_t i;
		uint32_t offset= offFruMultiRec;
		struct fru_multirec_header * h;
		uint32_t last_off, len;
		uint8_t error=0;

		i = last_off = offset;

		memset(&fru, 0, sizeof(fru));
		fru_data = malloc(fru.size + 1);
		if (!fru_data) {
			lprintf(LOG_ERR, " Out of memory!");
			return -1;
		}
		memset(fru_data, 0, fru.size + 1);

		do {
			h = (struct fru_multirec_header *) (fru_data + i);

			/* read area in (at most) FRU_MULTIREC_CHUNK_SIZE bytes at a time */
			if ((last_off < (i + sizeof(*h))) || (last_off < (i + h->len)))
			{
				len = fru.size - last_off;
				if (len > FRU_MULTIREC_CHUNK_SIZE)
					len = FRU_MULTIREC_CHUNK_SIZE;

				if (read_fru_area(intf, &fru, id, last_off, len, fru_data) < 0)
					break;

				last_off += len;
			}
			if( h->type ==  FRU_RECORD_TYPE_OEM_EXTENSION ){

				struct fru_multirec_oem_header *oh=(struct fru_multirec_oem_header *)
										&fru_data[i + sizeof(struct fru_multirec_header)];
				uint32_t iana = oh->mfg_id[0] | oh->mfg_id[1]<<8 | oh->mfg_id[2]<<16;

				uint32_t suppliedIana = 0 ;
				/* Now makes sure this is really PICMG record */

				/* Default to PICMG for backward compatibility */
				if( argc <=2 ) {
					suppliedIana =  IPMI_OEM_PICMG;
				}  else {
					if( !strncmp( argv[2] , "oem" , 3 )) {
						/* Expect IANA number next */
						if( argc <= 3 ) {
							lprintf(LOG_ERR, "oem iana <record> <format> [<args>]");
							error = 1;
						} else {
							if (str2uint(argv[3], &suppliedIana) == 0) {
								lprintf(LOG_DEBUG,
										"using iana: %d",
										suppliedIana);
							} else {
								lprintf(LOG_ERR,
										"Given IANA '%s' is invalid.",
										argv[3]);
								error = 1;
							}
						}
					}
				}

				if( suppliedIana == iana ) {
					lprintf(LOG_DEBUG, "Matching record found" );

					if( iana == IPMI_OEM_PICMG ){
						if( ipmi_fru_picmg_ext_edit(fru_data,
						i + sizeof(struct fru_multirec_header),
						h->len, h, oh )){
							/* The fru changed */
							write_fru_area(intf,&fru,id, i,i,
						h->len+ sizeof(struct fru_multirec_header), fru_data);
						}
					}
					else if( iana == IPMI_OEM_KONTRON ) {
						if( ipmi_fru_oemkontron_edit( argc,argv,fru_data,
						i + sizeof(struct fru_multirec_header),
						h->len, h, oh )){
							/* The fru changed */
							write_fru_area(intf,&fru,id, i,i,
						h->len+ sizeof(struct fru_multirec_header), fru_data);
						}
					}
					/* FIXME: Add OEM record support here */
					else{
						printf("  OEM IANA (%s) Record not support in this mode\n",
															val2str( iana,  ipmi_oem_info));
						error = 1;
					}
				}
			}
			i += h->len + sizeof (struct fru_multirec_header);
		} while (!(h->format & 0x80) && (error != 1));

		free_n(&fru_data);
	}
	return 0;
}


ipmi_fru_read_internal_use(struct ipmi_intf * intf, uint8_t id, char * pFileName)
{
	struct fru_info fru;
	uint16_t size;
	uint16_t offset;
	int rc = 0;

	rc = ipmi_fru_get_internal_use_info(intf, id, &fru, &size, &offset);

	if(rc == 0)
	{
		uint8_t * frubuf;

		lprintf(LOG_DEBUG, "Internal Use Area Offset: %i", offset);
		printf(          "Internal Use Area Size  : %i\n", size);

		frubuf = malloc( size );
		if(frubuf)
		{
			rc = read_fru_area_section(intf, &fru, id, offset, size, frubuf);

			if(rc == 0)
			{
				if(!pFileName)
				{
					uint16_t counter;
					for(counter = 0; counter < size; counter ++)
					{
						if((counter % 16) == 0)
							printf("\n%02i- ", (counter / 16));
						printf("%02X ", frubuf[counter]);
					}
				}
				else
				{
					FILE * pFile;
					pFile = fopen(pFileName,"wb");
					if (pFile)
					{
						fwrite(frubuf, size, 1, pFile);
						printf("Done\n");
					}
					else
					{
						lprintf(LOG_ERR, "Error opening file %s\n", pFileName);
						free_n(&frubuf);
						return -1;
					}
					fclose(pFile);
				}
			}
			printf("\n");

			free_n(&frubuf);
		}

	}
	else
	{
		lprintf(LOG_ERR, "Cannot access internal use area");
	}
	return 0;
}


ipmi_fru_read_help()
{
	lprintf(LOG_NOTICE, "fru read <fru id> <fru file>");
	lprintf(LOG_NOTICE, "Note: FRU ID and file(incl. full path) must be specified.");
	lprintf(LOG_NOTICE, "Example: ipmitool fru read 0 /root/fru.bin");
} /* ipmi_fru_read_help() */


__ipmi_fru_print(struct ipmi_intf * intf, uint8_t id)
{
	struct ipmi_rs * rsp;
	struct ipmi_rq req;
	struct fru_info fru;
	struct fru_header header;
	uint8_t msg_data[4];

	memset(&fru, 0, sizeof(struct fru_info));
	memset(&header, 0, sizeof(struct fru_header));

	/*
	* get info about this FRU
	*/
	memset(msg_data, 0, 4);
	msg_data[0] = id;

	memset(&req, 0, sizeof(req));
	req.msg.netfn = IPMI_NETFN_STORAGE;
	req.msg.cmd = GET_FRU_INFO;
	req.msg.data = msg_data;
	req.msg.data_len = 1;

	rsp = intf->sendrecv(intf, &req);
	if (!rsp) {
		printf(" Device not present (No Response)\n");
		return -1;
	}
	if (rsp->ccode) {
		printf(" Device not present (%s)\n",
			val2str(rsp->ccode, completion_code_vals));
		return -1;
	}

	memset(&fru, 0, sizeof(fru));
	fru.size = (rsp->data[1] << 8) | rsp->data[0];
	fru.access = rsp->data[2] & 0x1;

	lprintf(LOG_DEBUG, "fru.size = %d bytes (accessed by %s)",
		fru.size, fru.access ? "words" : "bytes");

	if (fru.size < 1) {
		lprintf(LOG_ERR, " Invalid FRU size %d", fru.size);
		return -1;
	}

	/*
	* retrieve the FRU header
	*/
	msg_data[0] = id;
	msg_data[1] = 0;
	msg_data[2] = 0;
	msg_data[3] = 8;

	memset(&req, 0, sizeof(req));
	req.msg.netfn = IPMI_NETFN_STORAGE;
	req.msg.cmd = GET_FRU_DATA;
	req.msg.data = msg_data;
	req.msg.data_len = 4;

	rsp = intf->sendrecv(intf, &req);
	if (!rsp) {
		printf(" Device not present (No Response)\n");
		return 1;
	}
	if (rsp->ccode) {
		printf(" Device not present (%s)\n",
				val2str(rsp->ccode, completion_code_vals));
		return 1;
	}

	if (verbose > 1)
		printbuf(rsp->data, rsp->data_len, "FRU DATA");

	memcpy(&header, rsp->data + 1, 8);

	if (header.version != 1) {
		lprintf(LOG_ERR, " Unknown FRU header version 0x%02x",
			header.version);
		return -1;
	}

	/* offsets need converted to bytes
	* but that conversion is not done to the structure
	* because we may end up with offset > 255
	* which would overflow our 1-byte offset field */

	lprintf(LOG_DEBUG, "fru.header.version:         0x%x",
		header.version);
	lprintf(LOG_DEBUG, "fru.header.offset.internal: 0x%x",
		header.offset.internal * 8);
	lprintf(LOG_DEBUG, "fru.header.offset.chassis:  0x%x",
		header.offset.chassis * 8);
	lprintf(LOG_DEBUG, "fru.header.offset.board:    0x%x",
		header.offset.board * 8);
	lprintf(LOG_DEBUG, "fru.header.offset.product:  0x%x",
		header.offset.product * 8);
	lprintf(LOG_DEBUG, "fru.header.offset.multi:    0x%x",
		header.offset.multi * 8);

	/*
	* rather than reading the entire part
	* only read the areas we'll format
	*/
	/* chassis area */
	if ((header.offset.chassis*8) >= sizeof(struct fru_header))
		fru_area_print_chassis(intf, &fru, id, header.offset.chassis*8);

	/* board area */
	if ((header.offset.board*8) >= sizeof(struct fru_header))
		fru_area_print_board(intf, &fru, id, header.offset.board*8);

	/* product area */
	if ((header.offset.product*8) >= sizeof(struct fru_header))
		fru_area_print_product(intf, &fru, id, header.offset.product*8);

	/* multirecord area */
	if( verbose==0 ) /* scipp parsing multirecord */
		return 0;

	if ((header.offset.multi*8) >= sizeof(struct fru_header))
		fru_area_print_multirec(intf, &fru, id, header.offset.multi*8);

	return 0;
}


ipmi_sdr_print_listentry(struct ipmi_intf *intf, struct sdr_record_list *entry)
{
	int rc = 0;

	switch (entry->type) {
	case SDR_RECORD_TYPE_FULL_SENSOR:
	case SDR_RECORD_TYPE_COMPACT_SENSOR:
		rc = ipmi_sdr_print_sensor_fc(intf, entry->record.common, entry->type);
		break;
	case SDR_RECORD_TYPE_EVENTONLY_SENSOR:
		rc = ipmi_sdr_print_sensor_eventonly(intf,
						     entry->record.eventonly);
		break;
	case SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR:
		rc = ipmi_sdr_print_sensor_generic_locator(entry->record.
							   genloc);
		break;
	case SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR:
		rc = ipmi_sdr_print_sensor_fru_locator(entry->record.fruloc);
		break;
	case SDR_RECORD_TYPE_MC_DEVICE_LOCATOR:
		rc = ipmi_sdr_print_sensor_mc_locator(entry->record.mcloc);
		break;
	case SDR_RECORD_TYPE_ENTITY_ASSOC:
		break;
	case SDR_RECORD_TYPE_OEM:
		rc = ipmi_sdr_print_sensor_oem(entry->record.oem);
		break;
	case SDR_RECORD_TYPE_DEVICE_ENTITY_ASSOC:
	case SDR_RECORD_TYPE_MC_CONFIRMATION:
	case SDR_RECORD_TYPE_BMC_MSG_CHANNEL_INFO:
		/* not implemented yet */
		break;
	}

	return rc;
}


ipmi_sdr_print_sensor_event_status(struct ipmi_intf *intf,
				   uint8_t sensor_num,
				   uint8_t sensor_type,
				   uint8_t event_type, int numeric_fmt,
				   uint8_t target, uint8_t lun, uint8_t channel)
{
	struct ipmi_rs *rsp;
	int i;
	const struct valstr assert_cond_1[] = {
		{0x80, "unc+"},
		{0x40, "unc-"},
		{0x20, "lnr+"},
		{0x10, "lnr-"},
		{0x08, "lcr+"},
		{0x04, "lcr-"},
		{0x02, "lnc+"},
		{0x01, "lnc-"},
		{0x00, NULL},
	};
	const struct valstr assert_cond_2[] = {
		{0x08, "unr+"},
		{0x04, "unr-"},
		{0x02, "ucr+"},
		{0x01, "ucr-"},
		{0x00, NULL},
	};

	rsp = ipmi_sdr_get_sensor_event_status(intf, sensor_num,
						target, lun, channel);

	if (!rsp) {
		lprintf(LOG_DEBUG,
			"Error reading event status for sensor #%02x",
			sensor_num);
		return -1;
	}
	if (rsp->ccode) {
		lprintf(LOG_DEBUG,
			"Error reading event status for sensor #%02x: %s",
			sensor_num, val2str(rsp->ccode, completion_code_vals));
		return -1;
	}
	/* There is an assumption here that data_len >= 1 */
	if (IS_READING_UNAVAILABLE(rsp->data[0])) {
		printf(" Event Status          : Unavailable\n");
		return 0;
	}
	if (IS_SCANNING_DISABLED(rsp->data[0])) {
		//printf(" Event Status          : Scanning Disabled\n");
		//return 0;
	}
	if (IS_EVENT_MSG_DISABLED(rsp->data[0])) {
		printf(" Event Status          : Event Messages Disabled\n");
		//return 0;
	}

	switch (numeric_fmt) {
	case DISCRETE_SENSOR:
		if (rsp->data_len == 2) {
			ipmi_sdr_print_discrete_state(intf, "Assertion Events",
						      sensor_type, event_type,
						      rsp->data[1], 0);
		} else if (rsp->data_len > 2) {
			ipmi_sdr_print_discrete_state(intf, "Assertion Events",
						      sensor_type, event_type,
						      rsp->data[1],
						      rsp->data[2]);
		}
		if (rsp->data_len == 4) {
			ipmi_sdr_print_discrete_state(intf, "Deassertion Events",
						      sensor_type, event_type,
						      rsp->data[3], 0);
		} else if (rsp->data_len > 4) {
			ipmi_sdr_print_discrete_state(intf, "Deassertion Events",
						      sensor_type, event_type,
						      rsp->data[3],
						      rsp->data[4]);
		}
		break;

	case ANALOG_SENSOR:
		printf(" Assertion Events      : ");
		for (i = 0; i < 8; i++) {
			if (rsp->data[1] & (1 << i))
				printf("%s ", val2str(1 << i, assert_cond_1));
		}
		if (rsp->data_len > 2) {
			for (i = 0; i < 4; i++) {
				if (rsp->data[2] & (1 << i))
					printf("%s ",
					       val2str(1 << i, assert_cond_2));
			}
			printf("\n");
			if ((rsp->data_len == 4 && rsp->data[3] != 0) ||
			    (rsp->data_len > 4
			     && (rsp->data[3] != 0 && rsp->data[4] != 0))) {
				printf(" Deassertion Events    : ");
				for (i = 0; i < 8; i++) {
					if (rsp->data[3] & (1 << i))
						printf("%s ",
						       val2str(1 << i,
							       assert_cond_1));
				}
				if (rsp->data_len > 4) {
					for (i = 0; i < 4; i++) {
						if (rsp->data[4] & (1 << i))
							printf("%s ",
							       val2str(1 << i,
								       assert_cond_2));
					}
				}
				printf("\n");
			}
		} else {
			printf("\n");
		}
		break;

	default:
		break;
	}

	return 0;
}


ipmi_sdr_list_empty(void)
{
	struct sdr_record_list *list, *next;

	ipmi_sdr_end(sdr_list_itr);

	for (list = sdr_list_head; list; list = next) {
		switch (list->type) {
		case SDR_RECORD_TYPE_FULL_SENSOR:
		case SDR_RECORD_TYPE_COMPACT_SENSOR:
			if (list->record.common) {
				free(list->record.common);
				list->record.common = NULL;
			}
			break;
		case SDR_RECORD_TYPE_EVENTONLY_SENSOR:
			if (list->record.eventonly) {
				free(list->record.eventonly);
				list->record.eventonly = NULL;
			}
			break;
		case SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR:
			if (list->record.genloc) {
				free(list->record.genloc);
				list->record.genloc = NULL;
			}
			break;
		case SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR:
			if (list->record.fruloc) {
				free(list->record.fruloc);
				list->record.fruloc = NULL;
			}
			break;
		case SDR_RECORD_TYPE_MC_DEVICE_LOCATOR:
			if (list->record.mcloc) {
				free(list->record.mcloc);
				list->record.mcloc = NULL;
			}
			break;
		case SDR_RECORD_TYPE_ENTITY_ASSOC:
			if (list->record.entassoc) {
				free(list->record.entassoc);
				list->record.entassoc = NULL;
			}
			break;
		}
		next = list->next;
		free(list);
		list = NULL;
	}

	sdr_list_head = NULL;
	sdr_list_tail = NULL;
	sdr_list_itr = NULL;
}


ipmi_sdr_get_sensor_event_status(struct ipmi_intf *intf, uint8_t sensor,
				 uint8_t target, uint8_t lun, uint8_t channel)
{
	struct ipmi_rq req;
	struct ipmi_rs *rsp;
	uint8_t  bridged_request = 0;
	uint32_t save_addr;
	uint32_t save_channel;

	if ( BRIDGE_TO_SENSOR(intf, target, channel) ) {
		bridged_request = 1;
		save_addr = intf->target_addr;
		intf->target_addr = target;
		save_channel = intf->target_channel;
		intf->target_channel = channel;
	}
	memset(&req, 0, sizeof (req));
	req.msg.netfn = IPMI_NETFN_SE;
	req.msg.lun = lun;
	req.msg.cmd = GET_SENSOR_EVENT_STATUS;
	req.msg.data = &sensor;
	req.msg.data_len = 1;

	rsp = intf->sendrecv(intf, &req);
	if (bridged_request) {
		intf->target_addr = save_addr;
		intf->target_channel = save_channel;
	}
	return rsp;
}


ipmi_sdr_list_cache_fromfile(const char *ifile)
{
	FILE *fp;
	struct __sdr_header {
		uint16_t id;
		uint8_t version;
		uint8_t type;
		uint8_t length;
	} header;
	struct sdr_record_list *sdrr;
	uint8_t *rec;
	int ret = 0, count = 0, bc = 0;

	if (!ifile) {
		lprintf(LOG_ERR, "No SDR cache filename given");
		return -1;
	}

	fp = ipmi_open_file_read(ifile);
	if (!fp) {
		lprintf(LOG_ERR, "Unable to open SDR cache %s for reading",
			ifile);
		return -1;
	}

	while (feof(fp) == 0) {
		memset(&header, 0, sizeof(header));
		bc = fread(&header, 1, 5, fp);
		if (bc <= 0)
			break;

		if (bc != 5) {
			lprintf(LOG_ERR, "header read %d bytes, expected 5",
				bc);
			ret = -1;
			break;
		}

		if (header.length == 0)
			continue;

		if (header.version != 0x51 &&
		    header.version != 0x01 &&
		    header.version != 0x02) {
			lprintf(LOG_WARN, "invalid sdr header version %02x",
				header.version);
			ret = -1;
			break;
		}

		sdrr = malloc(sizeof (struct sdr_record_list));
		if (!sdrr) {
			lprintf(LOG_ERR, "ipmitool: malloc failure");
			ret = -1;
			break;
		}
		memset(sdrr, 0, sizeof (struct sdr_record_list));

		sdrr->id = header.id;
		sdrr->type = header.type;

		rec = malloc(header.length + 1);
		if (!rec) {
			lprintf(LOG_ERR, "ipmitool: malloc failure");
			ret = -1;
			if (sdrr) {
				free(sdrr);
				sdrr = NULL;
			}
			break;
		}
		memset(rec, 0, header.length + 1);

		bc = fread(rec, 1, header.length, fp);
		if (bc != header.length) {
			lprintf(LOG_ERR,
				"record %04x read %d bytes, expected %d",
				header.id, bc, header.length);
			ret = -1;
			if (sdrr) {
				free(sdrr);
				sdrr = NULL;
			}
			if (rec) {
				free(rec);
				rec = NULL;
			}
			break;
		}

		switch (header.type) {
		case SDR_RECORD_TYPE_FULL_SENSOR:
		case SDR_RECORD_TYPE_COMPACT_SENSOR:
			sdrr->record.common =
			    (struct sdr_record_common_sensor *) rec;
			break;
		case SDR_RECORD_TYPE_EVENTONLY_SENSOR:
			sdrr->record.eventonly =
			    (struct sdr_record_eventonly_sensor *) rec;
			break;
		case SDR_RECORD_TYPE_GENERIC_DEVICE_LOCATOR:
			sdrr->record.genloc =
			    (struct sdr_record_generic_locator *) rec;
			break;
		case SDR_RECORD_TYPE_FRU_DEVICE_LOCATOR:
			sdrr->record.fruloc =
			    (struct sdr_record_fru_locator *) rec;
			break;
		case SDR_RECORD_TYPE_MC_DEVICE_LOCATOR:
			sdrr->record.mcloc =
			    (struct sdr_record_mc_locator *) rec;
			break;
		case SDR_RECORD_TYPE_ENTITY_ASSOC:
			sdrr->record.entassoc =
			    (struct sdr_record_entity_assoc *) rec;
			break;
		default:
			free(rec);
			rec = NULL;
			if (sdrr) {
				free(sdrr);
				sdrr = NULL;
			}
			continue;
		}

		/* add to global record liset */
		if (!sdr_list_head)
			sdr_list_head = sdrr;
		else
			sdr_list_tail->next = sdrr;

		sdr_list_tail = sdrr;

		count++;

		lprintf(LOG_DEBUG, "Read record %04x from file into cache",
			sdrr->id);
	}

	if (!sdr_list_itr) {
		sdr_list_itr = malloc(sizeof (struct ipmi_sdr_iterator));
		if (sdr_list_itr) {
			sdr_list_itr->reservation = 0;
			sdr_list_itr->total = count;
			sdr_list_itr->next = 0xffff;
		}
	}

	fclose(fp);
	return ret;
}


get_offset(uint8_t x)
{
	int i;
	for (i = 0; i < 8; i++)
		if (x >> i == 1)
			return i;
	return 0;
}


ipmi_sdr_get_sensor_reading_ipmb(struct ipmi_intf *intf, uint8_t sensor,
				 uint8_t target, uint8_t lun, uint8_t channel)
{
	struct ipmi_rq req;
	struct ipmi_rs *rsp;
	uint8_t  bridged_request = 0;
	uint32_t save_addr;
	uint32_t save_channel;

	if ( BRIDGE_TO_SENSOR(intf, target, channel) ) {
		lprintf(LOG_DEBUG,
			"Bridge to Sensor "
			"Intf my/%#x tgt/%#x:%#x Sdr tgt/%#x:%#x\n",
			intf->my_addr, intf->target_addr, intf->target_channel,
			target, channel);
		bridged_request = 1;
		save_addr = intf->target_addr;
		intf->target_addr = target;
		save_channel = intf->target_channel;
		intf->target_channel = channel;
	}
	memset(&req, 0, sizeof (req));
	req.msg.netfn = IPMI_NETFN_SE;
	req.msg.lun = lun;
	req.msg.cmd = GET_SENSOR_READING;
	req.msg.data = &sensor;
	req.msg.data_len = 1;

	rsp = intf->sendrecv(intf, &req);
	if (bridged_request) {
		intf->target_addr    = save_addr;
		intf->target_channel = save_channel;
	}
	return rsp;
}


ipmi_lan_alert_set(struct ipmi_intf * intf, uint8_t chan, uint8_t alert,
		   int argc, char ** argv)
{
	struct lan_param * p;
	uint8_t data[32], temp[32];
	int rc = 0;

	if (argc < 2) {
		print_lan_alert_set_usage();
		return (-1);
	}

	if (strncmp(argv[0], "help", 4) == 0 ||
	    strncmp(argv[1], "help", 4) == 0) {
		print_lan_alert_set_usage();
		return 0;
	}

	memset(data, 0, sizeof(data));
	memset(temp, 0, sizeof(temp));

	/* alert destination ip address */
	if (strncasecmp(argv[0], "ipaddr", 6) == 0 &&
	    (get_cmdline_ipaddr(argv[1], temp) == 0)) {
		/* get current parameter */
		p = get_lan_param_select(intf, chan, IPMI_LANP_DEST_ADDR, alert);
		if (!p) {
			return (-1);
		}
		memcpy(data, p->data, __min(p->data_len, sizeof(data)));
		/* set new ipaddr */
		memcpy(data+3, temp, 4);
		printf("Setting LAN Alert %d IP Address to %d.%d.%d.%d\n", alert,
		       data[3], data[4], data[5], data[6]);
		rc = set_lan_param_nowait(intf, chan, IPMI_LANP_DEST_ADDR, data, p->data_len);
	}
	/* alert destination mac address */
	else if (strncasecmp(argv[0], "macaddr", 7) == 0 &&
		 (str2mac(argv[1], temp) == 0)) {
		/* get current parameter */
		p = get_lan_param_select(intf, chan, IPMI_LANP_DEST_ADDR, alert);
		if (!p) {
			return (-1);
		}
		memcpy(data, p->data, __min(p->data_len, sizeof(data)));
		/* set new macaddr */
		memcpy(data+7, temp, 6);
		printf("Setting LAN Alert %d MAC Address to "
		       "%s\n", alert, mac2str(&data[7]));
		rc = set_lan_param_nowait(intf, chan, IPMI_LANP_DEST_ADDR, data, p->data_len);
	}
	/* alert destination gateway selector */
	else if (strncasecmp(argv[0], "gateway", 7) == 0) {
		/* get current parameter */
		p = get_lan_param_select(intf, chan, IPMI_LANP_DEST_ADDR, alert);
		if (!p) {
			return (-1);
		}
		memcpy(data, p->data, __min(p->data_len, sizeof(data)));

		if (strncasecmp(argv[1], "def", 3) == 0 ||
		    strncasecmp(argv[1], "default", 7) == 0) {
			printf("Setting LAN Alert %d to use Default Gateway\n", alert);
			data[2] = 0;
		}
		else if (strncasecmp(argv[1], "bak", 3) == 0 ||
			 strncasecmp(argv[1], "backup", 6) == 0) {
			printf("Setting LAN Alert %d to use Backup Gateway\n", alert);
			data[2] = 1;
		}
		else {
			print_lan_alert_set_usage();
			return -1;
		}

		rc = set_lan_param_nowait(intf, chan, IPMI_LANP_DEST_ADDR, data, p->data_len);
	}
	/* alert acknowledgement */
	else if (strncasecmp(argv[0], "ack", 3) == 0) {
		/* get current parameter */
		p = get_lan_param_select(intf, chan, IPMI_LANP_DEST_TYPE, alert);
		if (!p) {
			return (-1);
		}
		memcpy(data, p->data, __min(p->data_len, sizeof(data)));

		if (strncasecmp(argv[1], "on", 2) == 0 ||
		    strncasecmp(argv[1], "yes", 3) == 0) {
			printf("Setting LAN Alert %d to Acknowledged\n", alert);
			data[1] |= 0x80;
		}
		else if (strncasecmp(argv[1], "off", 3) == 0 ||
			 strncasecmp(argv[1], "no", 2) == 0) {
			printf("Setting LAN Alert %d to Unacknowledged\n", alert);
			data[1] &= ~0x80;
		}
		else {
			print_lan_alert_set_usage();
			return -1;
		}
		rc = set_lan_param_nowait(intf, chan, IPMI_LANP_DEST_TYPE, data, p->data_len);
	}
	/* alert destination type */
	else if (strncasecmp(argv[0], "type", 4) == 0) {
		/* get current parameter */
		p = get_lan_param_select(intf, chan, IPMI_LANP_DEST_TYPE, alert);
		if (!p) {
			return (-1);
		}
		memcpy(data, p->data, __min(p->data_len, sizeof(data)));

		if (strncasecmp(argv[1], "pet", 3) == 0) {
			printf("Setting LAN Alert %d destination to PET Trap\n", alert);
			data[1] &= ~0x07;
		}
		else if (strncasecmp(argv[1], "oem1", 4) == 0) {
			printf("Setting LAN Alert %d destination to OEM 1\n", alert);
			data[1] &= ~0x07;
			data[1] |= 0x06;
		}
		else if (strncasecmp(argv[1], "oem2", 4) == 0) {
			printf("Setting LAN Alert %d destination to OEM 2\n", alert);
			data[1] |= 0x07;
		}
		else {
			print_lan_alert_set_usage();
			return -1;
		}
		rc = set_lan_param_nowait(intf, chan, IPMI_LANP_DEST_TYPE, data, p->data_len);
	}
	/* alert acknowledge timeout or retry interval */
	else if (strncasecmp(argv[0], "time", 4) == 0) {
		/* get current parameter */
		p = get_lan_param_select(intf, chan, IPMI_LANP_DEST_TYPE, alert);
		if (!p) {
			return (-1);
		}
		memcpy(data, p->data, __min(p->data_len, sizeof(data)));

		if (str2uchar(argv[1], &data[2]) != 0) {
			lprintf(LOG_ERR, "Invalid time: %s", argv[1]);
			return (-1);
		}
		printf("Setting LAN Alert %d timeout/retry to %d seconds\n", alert, data[2]);
		rc = set_lan_param_nowait(intf, chan, IPMI_LANP_DEST_TYPE, data, p->data_len);
	}
	/* number of retries */
	else if (strncasecmp(argv[0], "retry", 5) == 0) {
		/* get current parameter */
		p = get_lan_param_select(intf, chan, IPMI_LANP_DEST_TYPE, alert);
		if (!p) {
			return (-1);
		}
		memcpy(data, p->data, __min(p->data_len, sizeof(data)));

		if (str2uchar(argv[1], &data[3]) != 0) {
			lprintf(LOG_ERR, "Invalid retry: %s", argv[1]);
			return (-1);
		}
		data[3] = data[3] & 0x7;
		printf("Setting LAN Alert %d number of retries to %d\n", alert, data[3]);
		rc = set_lan_param_nowait(intf, chan, IPMI_LANP_DEST_TYPE, data, p->data_len);
	}
	else {
		print_lan_alert_set_usage();
		return -1;
	}

	return rc;
}


print_lan_set_auth_usage(void)
{
	lprintf(LOG_NOTICE,
"lan set <channel> auth <level> <type,type,...>");
	lprintf(LOG_NOTICE,
"  level = CALLBACK, USER, OPERATOR, ADMIN");
	lprintf(LOG_NOTICE,
"  types = NONE, MD2, MD5, PASSWORD, OEM");
	lprintf(LOG_NOTICE,
"example: lan set 7 auth ADMIN PASSWORD,MD5");
}


print_lan_set_access_usage(void)
{
	lprintf(LOG_NOTICE,
"lan set access <on|off>");
}


lan_set_arp_generate(struct ipmi_intf * intf,
		     uint8_t chan, uint8_t ctl)
{
	struct lan_param *lp;
	uint8_t data;

	lp = get_lan_param(intf, chan, IPMI_LANP_BMC_ARP);
	if (!lp)
		return -1;
	if (!lp->data)
		return -1;
	data = lp->data[0];

	/* set arp generate bitflag */
	if (ctl == 0)
		data &= ~0x1;
	else
		data |= 0x1;

	printf("%sabling BMC-generated Gratuitous ARPs\n", ctl ? "En" : "Dis");
	return set_lan_param(intf, chan, IPMI_LANP_BMC_ARP, &data, 1);
}


ipmi_lan_stats_clear(struct ipmi_intf * intf, uint8_t chan)
{
	int rc = 0;
	struct ipmi_rs * rsp;
	struct ipmi_rq req;
	uint8_t msg_data[2];

	if (!is_lan_channel(intf, chan)) {
		lprintf(LOG_ERR, "Channel %d is not a LAN channel", chan);
		return -1;
	}

	/* From here, we are ready to get the stats */
	msg_data[0] = chan;
	msg_data[1] = 1;   /* Clear */

	memset(&req, 0, sizeof(req));
	req.msg.netfn    = IPMI_NETFN_TRANSPORT;
	req.msg.cmd      = IPMI_LAN_GET_STAT;
	req.msg.data     = msg_data;
	req.msg.data_len = 2;

	rsp = intf->sendrecv(intf, &req);
	if (!rsp) {
		lprintf(LOG_INFO, "Get LAN Stats command failed");
		return (-1);
	}

	if (rsp->ccode) {
		lprintf(LOG_INFO, "Get LAN Stats command failed: %s",
			val2str(rsp->ccode, completion_code_vals));
		return (-1);
	}

	return rc;
}


print_lan_alert_set_usage(void)
{
	lprintf(LOG_NOTICE,
"");
	lprintf(LOG_NOTICE,
"usage: lan alert set <channel number> <alert destination> <command> <parameter>");
	lprintf(LOG_NOTICE,
"");
	lprintf(LOG_NOTICE,
"    Command/parameter options:");
	lprintf(LOG_NOTICE,
"");
	lprintf(LOG_NOTICE,
"    ipaddr <x.x.x.x>               Set alert IP address");
	lprintf(LOG_NOTICE,
"    macaddr <x:x:x:x:x:x>          Set alert MAC address");
	lprintf(LOG_NOTICE,
"    gateway <default|backup>       Set channel gateway to use for alerts");
	lprintf(LOG_NOTICE,
"    ack <on|off>                   Set Alert Acknowledge on or off");
	lprintf(LOG_NOTICE,
"    type <pet|oem1|oem2>           Set destination type as PET or OEM");
	lprintf(LOG_NOTICE,
"    time <seconds>                 Set ack timeout or unack retry interval");
	lprintf(LOG_NOTICE,
"    retry <number>                 Set number of alert retries");
	lprintf(LOG_NOTICE,
"");
}