static int file_has_perm(const struct cred *cred,
			 struct file *file,
			 u32 av)
{
	struct file_security_struct *fsec = file->f_security;
	struct inode *inode = file_inode(file);
	struct common_audit_data ad;
	u32 sid = cred_sid(cred);
	int rc;

	ad.type = LSM_AUDIT_DATA_FILE;
	ad.u.file = file;

	if (sid != fsec->sid) {
		rc = avc_has_perm(sid, fsec->sid,
				  SECCLASS_FD,
				  FD__USE,
				  &ad);
		if (rc)
			goto out;
	}

	/* av is zero if only checking access to the descriptor. */
	rc = 0;
	if (av)
		rc = inode_has_perm(cred, inode, av, &ad);

out:
	return rc;
}


static void selinux_sock_graft(struct sock *sk, struct socket *parent)
{
	struct inode_security_struct *isec =
		inode_security_novalidate(SOCK_INODE(parent));
	struct sk_security_struct *sksec = sk->sk_security;

	if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 ||
	    sk->sk_family == PF_UNIX)
		isec->sid = sksec->sid;
	sksec->sclass = isec->sclass;
}


static int selinux_capset(struct cred *new, const struct cred *old,
			  const kernel_cap_t *effective,
			  const kernel_cap_t *inheritable,
			  const kernel_cap_t *permitted)
{
	return cred_has_perm(old, new, PROCESS__SETCAP);
}


static struct inode_security_struct *inode_security_rcu(struct inode *inode, bool rcu)
{
	int error;

	error = __inode_security_revalidate(inode, NULL, !rcu);
	if (error)
		return ERR_PTR(error);
	return inode->i_security;
}


static int selinux_task_setnice(struct task_struct *p, int nice)
{
	return current_has_perm(p, PROCESS__SETSCHED);
}


static int selinux_inode_getsecurity(struct inode *inode, const char *name, void **buffer, bool alloc)
{
	u32 size;
	int error;
	char *context = NULL;
	struct inode_security_struct *isec;

	if (strcmp(name, XATTR_SELINUX_SUFFIX))
		return -EOPNOTSUPP;

	/*
	 * If the caller has CAP_MAC_ADMIN, then get the raw context
	 * value even if it is not defined by current policy; otherwise,
	 * use the in-core value under current policy.
	 * Use the non-auditing forms of the permission checks since
	 * getxattr may be called by unprivileged processes commonly
	 * and lack of permission just means that we fall back to the
	 * in-core context value, not a denial.
	 */
	error = cap_capable(current_cred(), &init_user_ns, CAP_MAC_ADMIN,
			    SECURITY_CAP_NOAUDIT);
	if (!error)
		error = cred_has_capability(current_cred(), CAP_MAC_ADMIN,
					    SECURITY_CAP_NOAUDIT, true);
	isec = inode_security(inode);
	if (!error)
		error = security_sid_to_context_force(isec->sid, &context,
						      &size);
	else
		error = security_sid_to_context(isec->sid, &context, &size);
	if (error)
		return error;
	error = size;
	if (alloc) {
		*buffer = context;
		goto out_nofree;
	}
	kfree(context);
out_nofree:
	return error;
}


static void selinux_task_to_inode(struct task_struct *p,
				  struct inode *inode)
{
	struct inode_security_struct *isec = inode->i_security;
	u32 sid = task_sid(p);

	spin_lock(&isec->lock);
	isec->sclass = inode_mode_to_security_class(inode->i_mode);
	isec->sid = sid;
	isec->initialized = LABEL_INITIALIZED;
	spin_unlock(&isec->lock);
}


static unsigned int selinux_ipv4_forward(void *priv,
					 struct sk_buff *skb,
					 const struct nf_hook_state *state)
{
	return selinux_ip_forward(skb, state->in, PF_INET);
}


static long aio_read_events_ring(struct kioctx *ctx,
				 struct io_event __user *event, long nr)
{
	struct aio_ring *ring;
	unsigned head, tail, pos;
	long ret = 0;
	int copy_ret;

	/*
	 * The mutex can block and wake us up and that will cause
	 * wait_event_interruptible_hrtimeout() to schedule without sleeping
	 * and repeat. This should be rare enough that it doesn't cause
	 * peformance issues. See the comment in read_events() for more detail.
	 */
	sched_annotate_sleep();
	mutex_lock(&ctx->ring_lock);

	/* Access to ->ring_pages here is protected by ctx->ring_lock. */
	ring = kmap_atomic(ctx->ring_pages[0]);
	head = ring->head;
	tail = ring->tail;
	kunmap_atomic(ring);

	/*
	 * Ensure that once we've read the current tail pointer, that
	 * we also see the events that were stored up to the tail.
	 */
	smp_rmb();

	pr_debug("h%u t%u m%u\n", head, tail, ctx->nr_events);

	if (head == tail)
		goto out;

	head %= ctx->nr_events;
	tail %= ctx->nr_events;

	while (ret < nr) {
		long avail;
		struct io_event *ev;
		struct page *page;

		avail = (head <= tail ?  tail : ctx->nr_events) - head;
		if (head == tail)
			break;

		avail = min(avail, nr - ret);
		avail = min_t(long, avail, AIO_EVENTS_PER_PAGE -
			    ((head + AIO_EVENTS_OFFSET) % AIO_EVENTS_PER_PAGE));

		pos = head + AIO_EVENTS_OFFSET;
		page = ctx->ring_pages[pos / AIO_EVENTS_PER_PAGE];
		pos %= AIO_EVENTS_PER_PAGE;

		ev = kmap(page);
		copy_ret = copy_to_user(event + ret, ev + pos,
					sizeof(*ev) * avail);
		kunmap(page);

		if (unlikely(copy_ret)) {
			ret = -EFAULT;
			goto out;
		}

		ret += avail;
		head += avail;
		head %= ctx->nr_events;
	}

	ring = kmap_atomic(ctx->ring_pages[0]);
	ring->head = head;
	kunmap_atomic(ring);
	flush_dcache_page(ctx->ring_pages[0]);

	pr_debug("%li  h%u t%u\n", ret, head, tail);
out:
	mutex_unlock(&ctx->ring_lock);

	return ret;
}


static bool get_reqs_available(struct kioctx *ctx)
{
	struct kioctx_cpu *kcpu;
	bool ret = false;
	unsigned long flags;

	local_irq_save(flags);
	kcpu = this_cpu_ptr(ctx->cpu);
	if (!kcpu->reqs_available) {
		int old, avail = atomic_read(&ctx->reqs_available);

		do {
			if (avail < ctx->req_batch)
				goto out;

			old = avail;
			avail = atomic_cmpxchg(&ctx->reqs_available,
					       avail, avail - ctx->req_batch);
		} while (avail != old);

		kcpu->reqs_available += ctx->req_batch;
	}

	ret = true;
	kcpu->reqs_available--;
out:
	local_irq_restore(flags);
	return ret;
}


static void put_reqs_available(struct kioctx *ctx, unsigned nr)
{
	struct kioctx_cpu *kcpu;
	unsigned long flags;

	local_irq_save(flags);
	kcpu = this_cpu_ptr(ctx->cpu);
	kcpu->reqs_available += nr;

	while (kcpu->reqs_available >= ctx->req_batch * 2) {
		kcpu->reqs_available -= ctx->req_batch;
		atomic_add(ctx->req_batch, &ctx->reqs_available);
	}

	local_irq_restore(flags);
}


void sctp_transport_walk_stop(struct rhashtable_iter *iter)
{
	rhashtable_walk_stop(iter);
	rhashtable_walk_exit(iter);
}


static int sctp_getsockopt_assoc_stats(struct sock *sk, int len,
				       char __user *optval,
				       int __user *optlen)
{
	struct sctp_assoc_stats sas;
	struct sctp_association *asoc = NULL;

	/* User must provide at least the assoc id */
	if (len < sizeof(sctp_assoc_t))
		return -EINVAL;

	/* Allow the struct to grow and fill in as much as possible */
	len = min_t(size_t, len, sizeof(sas));

	if (copy_from_user(&sas, optval, len))
		return -EFAULT;

	asoc = sctp_id2assoc(sk, sas.sas_assoc_id);
	if (!asoc)
		return -EINVAL;

	sas.sas_rtxchunks = asoc->stats.rtxchunks;
	sas.sas_gapcnt = asoc->stats.gapcnt;
	sas.sas_outofseqtsns = asoc->stats.outofseqtsns;
	sas.sas_osacks = asoc->stats.osacks;
	sas.sas_isacks = asoc->stats.isacks;
	sas.sas_octrlchunks = asoc->stats.octrlchunks;
	sas.sas_ictrlchunks = asoc->stats.ictrlchunks;
	sas.sas_oodchunks = asoc->stats.oodchunks;
	sas.sas_iodchunks = asoc->stats.iodchunks;
	sas.sas_ouodchunks = asoc->stats.ouodchunks;
	sas.sas_iuodchunks = asoc->stats.iuodchunks;
	sas.sas_idupchunks = asoc->stats.idupchunks;
	sas.sas_opackets = asoc->stats.opackets;
	sas.sas_ipackets = asoc->stats.ipackets;

	/* New high max rto observed, will return 0 if not a single
	 * RTO update took place. obs_rto_ipaddr will be bogus
	 * in such a case
	 */
	sas.sas_maxrto = asoc->stats.max_obs_rto;
	memcpy(&sas.sas_obs_rto_ipaddr, &asoc->stats.obs_rto_ipaddr,
		sizeof(struct sockaddr_storage));

	/* Mark beginning of a new observation period */
	asoc->stats.max_obs_rto = asoc->rto_min;

	if (put_user(len, optlen))
		return -EFAULT;

	pr_debug("%s: len:%d, assoc_id:%d\n", __func__, len, sas.sas_assoc_id);

	if (copy_to_user(optval, &sas, len))
		return -EFAULT;

	return 0;
}


static int sctp_setsockopt_peer_primary_addr(struct sock *sk, char __user *optval,
					     unsigned int optlen)
{
	struct net *net = sock_net(sk);
	struct sctp_sock	*sp;
	struct sctp_association	*asoc = NULL;
	struct sctp_setpeerprim	prim;
	struct sctp_chunk	*chunk;
	struct sctp_af		*af;
	int 			err;

	sp = sctp_sk(sk);

	if (!net->sctp.addip_enable)
		return -EPERM;

	if (optlen != sizeof(struct sctp_setpeerprim))
		return -EINVAL;

	if (copy_from_user(&prim, optval, optlen))
		return -EFAULT;

	asoc = sctp_id2assoc(sk, prim.sspp_assoc_id);
	if (!asoc)
		return -EINVAL;

	if (!asoc->peer.asconf_capable)
		return -EPERM;

	if (asoc->peer.addip_disabled_mask & SCTP_PARAM_SET_PRIMARY)
		return -EPERM;

	if (!sctp_state(asoc, ESTABLISHED))
		return -ENOTCONN;

	af = sctp_get_af_specific(prim.sspp_addr.ss_family);
	if (!af)
		return -EINVAL;

	if (!af->addr_valid((union sctp_addr *)&prim.sspp_addr, sp, NULL))
		return -EADDRNOTAVAIL;

	if (!sctp_assoc_lookup_laddr(asoc, (union sctp_addr *)&prim.sspp_addr))
		return -EADDRNOTAVAIL;

	/* Create an ASCONF chunk with SET_PRIMARY parameter	*/
	chunk = sctp_make_asconf_set_prim(asoc,
					  (union sctp_addr *)&prim.sspp_addr);
	if (!chunk)
		return -ENOMEM;

	err = sctp_send_asconf(asoc, chunk);

	pr_debug("%s: we set peer primary addr primitively\n", __func__);

	return err;
}


static struct sock *sctp_accept(struct sock *sk, int flags, int *err)
{
	struct sctp_sock *sp;
	struct sctp_endpoint *ep;
	struct sock *newsk = NULL;
	struct sctp_association *asoc;
	long timeo;
	int error = 0;

	lock_sock(sk);

	sp = sctp_sk(sk);
	ep = sp->ep;

	if (!sctp_style(sk, TCP)) {
		error = -EOPNOTSUPP;
		goto out;
	}

	if (!sctp_sstate(sk, LISTENING)) {
		error = -EINVAL;
		goto out;
	}

	timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);

	error = sctp_wait_for_accept(sk, timeo);
	if (error)
		goto out;

	/* We treat the list of associations on the endpoint as the accept
	 * queue and pick the first association on the list.
	 */
	asoc = list_entry(ep->asocs.next, struct sctp_association, asocs);

	newsk = sp->pf->create_accept_sk(sk, asoc);
	if (!newsk) {
		error = -ENOMEM;
		goto out;
	}

	/* Populate the fields of the newsk from the oldsk and migrate the
	 * asoc to the newsk.
	 */
	sctp_sock_migrate(sk, newsk, asoc, SCTP_SOCKET_TCP);

out:
	release_sock(sk);
	*err = error;
	return newsk;
}


static int sctp_getsockopt_associnfo(struct sock *sk, int len,
				     char __user *optval,
				     int __user *optlen)
{

	struct sctp_assocparams assocparams;
	struct sctp_association *asoc;
	struct list_head *pos;
	int cnt = 0;

	if (len < sizeof (struct sctp_assocparams))
		return -EINVAL;

	len = sizeof(struct sctp_assocparams);

	if (copy_from_user(&assocparams, optval, len))
		return -EFAULT;

	asoc = sctp_id2assoc(sk, assocparams.sasoc_assoc_id);

	if (!asoc && assocparams.sasoc_assoc_id && sctp_style(sk, UDP))
		return -EINVAL;

	/* Values correspoinding to the specific association */
	if (asoc) {
		assocparams.sasoc_asocmaxrxt = asoc->max_retrans;
		assocparams.sasoc_peer_rwnd = asoc->peer.rwnd;
		assocparams.sasoc_local_rwnd = asoc->a_rwnd;
		assocparams.sasoc_cookie_life = ktime_to_ms(asoc->cookie_life);

		list_for_each(pos, &asoc->peer.transport_addr_list) {
			cnt++;
		}

		assocparams.sasoc_number_peer_destinations = cnt;
	} else {
		/* Values corresponding to the endpoint */
		struct sctp_sock *sp = sctp_sk(sk);

		assocparams.sasoc_asocmaxrxt = sp->assocparams.sasoc_asocmaxrxt;
		assocparams.sasoc_peer_rwnd = sp->assocparams.sasoc_peer_rwnd;
		assocparams.sasoc_local_rwnd = sp->assocparams.sasoc_local_rwnd;
		assocparams.sasoc_cookie_life =
					sp->assocparams.sasoc_cookie_life;
		assocparams.sasoc_number_peer_destinations =
					sp->assocparams.
					sasoc_number_peer_destinations;
	}

	if (put_user(len, optlen))
		return -EFAULT;

	if (copy_to_user(optval, &assocparams, len))
		return -EFAULT;

	return 0;
}


int sctp_inet_listen(struct socket *sock, int backlog)
{
	struct sock *sk = sock->sk;
	struct sctp_endpoint *ep = sctp_sk(sk)->ep;
	int err = -EINVAL;

	if (unlikely(backlog < 0))
		return err;

	lock_sock(sk);

	/* Peeled-off sockets are not allowed to listen().  */
	if (sctp_style(sk, UDP_HIGH_BANDWIDTH))
		goto out;

	if (sock->state != SS_UNCONNECTED)
		goto out;

	/* If backlog is zero, disable listening. */
	if (!backlog) {
		if (sctp_sstate(sk, CLOSED))
			goto out;

		err = 0;
		sctp_unhash_endpoint(ep);
		sk->sk_state = SCTP_SS_CLOSED;
		if (sk->sk_reuse)
			sctp_sk(sk)->bind_hash->fastreuse = 1;
		goto out;
	}

	/* If we are already listening, just update the backlog */
	if (sctp_sstate(sk, LISTENING))
		sk->sk_max_ack_backlog = backlog;
	else {
		err = sctp_listen_start(sk, backlog);
		if (err)
			goto out;
	}

	err = 0;
out:
	release_sock(sk);
	return err;
}


static int sctp_bind(struct sock *sk, struct sockaddr *addr, int addr_len)
{
	int retval = 0;

	lock_sock(sk);

	pr_debug("%s: sk:%p, addr:%p, addr_len:%d\n", __func__, sk,
		 addr, addr_len);

	/* Disallow binding twice. */
	if (!sctp_sk(sk)->ep->base.bind_addr.port)
		retval = sctp_do_bind(sk, (union sctp_addr *)addr,
				      addr_len);
	else
		retval = -EINVAL;

	release_sock(sk);

	return retval;
}


static int sctp_setsockopt_maxseg(struct sock *sk, char __user *optval, unsigned int optlen)
{
	struct sctp_assoc_value params;
	struct sctp_association *asoc;
	struct sctp_sock *sp = sctp_sk(sk);
	int val;

	if (optlen == sizeof(int)) {
		pr_warn_ratelimited(DEPRECATED
				    "%s (pid %d) "
				    "Use of int in maxseg socket option.\n"
				    "Use struct sctp_assoc_value instead\n",
				    current->comm, task_pid_nr(current));
		if (copy_from_user(&val, optval, optlen))
			return -EFAULT;
		params.assoc_id = 0;
	} else if (optlen == sizeof(struct sctp_assoc_value)) {
		if (copy_from_user(&params, optval, optlen))
			return -EFAULT;
		val = params.assoc_value;
	} else
		return -EINVAL;

	if ((val != 0) && ((val < 8) || (val > SCTP_MAX_CHUNK_LEN)))
		return -EINVAL;

	asoc = sctp_id2assoc(sk, params.assoc_id);
	if (!asoc && params.assoc_id && sctp_style(sk, UDP))
		return -EINVAL;

	if (asoc) {
		if (val == 0) {
			val = asoc->pathmtu;
			val -= sp->pf->af->net_header_len;
			val -= sizeof(struct sctphdr) +
					sizeof(struct sctp_data_chunk);
		}
		asoc->user_frag = val;
		asoc->frag_point = sctp_frag_point(asoc, asoc->pathmtu);
	} else {
		sp->user_frag = val;
	}

	return 0;
}


int sctp_transport_lookup_process(int (*cb)(struct sctp_transport *, void *),
				  struct net *net,
				  const union sctp_addr *laddr,
				  const union sctp_addr *paddr, void *p)
{
	struct sctp_transport *transport;
	int err;

	rcu_read_lock();
	transport = sctp_addrs_lookup_transport(net, laddr, paddr);
	rcu_read_unlock();
	if (!transport)
		return -ENOENT;

	err = cb(transport, p);
	sctp_transport_put(transport);

	return err;
}


struct sctp_transport *sctp_transport_get_idx(struct net *net,
					      struct rhashtable_iter *iter,
					      int pos)
{
	void *obj = SEQ_START_TOKEN;

	while (pos && (obj = sctp_transport_get_next(net, iter)) &&
	       !IS_ERR(obj))
		pos--;

	return obj;
}


static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
{
	struct sctp_bind_hashbucket *head; /* hash list */
	struct sctp_bind_bucket *pp;
	unsigned short snum;
	int ret;

	snum = ntohs(addr->v4.sin_port);

	pr_debug("%s: begins, snum:%d\n", __func__, snum);

	local_bh_disable();

	if (snum == 0) {
		/* Search for an available port. */
		int low, high, remaining, index;
		unsigned int rover;
		struct net *net = sock_net(sk);

		inet_get_local_port_range(net, &low, &high);
		remaining = (high - low) + 1;
		rover = prandom_u32() % remaining + low;

		do {
			rover++;
			if ((rover < low) || (rover > high))
				rover = low;
			if (inet_is_local_reserved_port(net, rover))
				continue;
			index = sctp_phashfn(sock_net(sk), rover);
			head = &sctp_port_hashtable[index];
			spin_lock(&head->lock);
			sctp_for_each_hentry(pp, &head->chain)
				if ((pp->port == rover) &&
				    net_eq(sock_net(sk), pp->net))
					goto next;
			break;
		next:
			spin_unlock(&head->lock);
		} while (--remaining > 0);

		/* Exhausted local port range during search? */
		ret = 1;
		if (remaining <= 0)
			goto fail;

		/* OK, here is the one we will use.  HEAD (the port
		 * hash table list entry) is non-NULL and we hold it's
		 * mutex.
		 */
		snum = rover;
	} else {
		/* We are given an specific port number; we verify
		 * that it is not being used. If it is used, we will
		 * exahust the search in the hash list corresponding
		 * to the port number (snum) - we detect that with the
		 * port iterator, pp being NULL.
		 */
		head = &sctp_port_hashtable[sctp_phashfn(sock_net(sk), snum)];
		spin_lock(&head->lock);
		sctp_for_each_hentry(pp, &head->chain) {
			if ((pp->port == snum) && net_eq(pp->net, sock_net(sk)))
				goto pp_found;
		}
	}
	pp = NULL;
	goto pp_not_found;
pp_found:
	if (!hlist_empty(&pp->owner)) {
		/* We had a port hash table hit - there is an
		 * available port (pp != NULL) and it is being
		 * used by other socket (pp->owner not empty); that other
		 * socket is going to be sk2.
		 */
		int reuse = sk->sk_reuse;
		struct sock *sk2;

		pr_debug("%s: found a possible match\n", __func__);

		if (pp->fastreuse && sk->sk_reuse &&
			sk->sk_state != SCTP_SS_LISTENING)
			goto success;

		/* Run through the list of sockets bound to the port
		 * (pp->port) [via the pointers bind_next and
		 * bind_pprev in the struct sock *sk2 (pp->sk)]. On each one,
		 * we get the endpoint they describe and run through
		 * the endpoint's list of IP (v4 or v6) addresses,
		 * comparing each of the addresses with the address of
		 * the socket sk. If we find a match, then that means
		 * that this port/socket (sk) combination are already
		 * in an endpoint.
		 */
		sk_for_each_bound(sk2, &pp->owner) {
			struct sctp_endpoint *ep2;
			ep2 = sctp_sk(sk2)->ep;

			if (sk == sk2 ||
			    (reuse && sk2->sk_reuse &&
			     sk2->sk_state != SCTP_SS_LISTENING))
				continue;

			if (sctp_bind_addr_conflict(&ep2->base.bind_addr, addr,
						 sctp_sk(sk2), sctp_sk(sk))) {
				ret = (long)sk2;
				goto fail_unlock;
			}
		}

		pr_debug("%s: found a match\n", __func__);
	}
pp_not_found:
	/* If there was a hash table miss, create a new port.  */
	ret = 1;
	if (!pp && !(pp = sctp_bucket_create(head, sock_net(sk), snum)))
		goto fail_unlock;

	/* In either case (hit or miss), make sure fastreuse is 1 only
	 * if sk->sk_reuse is too (that is, if the caller requested
	 * SO_REUSEADDR on this socket -sk-).
	 */
	if (hlist_empty(&pp->owner)) {
		if (sk->sk_reuse && sk->sk_state != SCTP_SS_LISTENING)
			pp->fastreuse = 1;
		else
			pp->fastreuse = 0;
	} else if (pp->fastreuse &&
		(!sk->sk_reuse || sk->sk_state == SCTP_SS_LISTENING))
		pp->fastreuse = 0;

	/* We are set, so fill up all the data in the hash table
	 * entry, tie the socket list information with the rest of the
	 * sockets FIXME: Blurry, NPI (ipg).
	 */
success:
	if (!sctp_sk(sk)->bind_hash) {
		inet_sk(sk)->inet_num = snum;
		sk_add_bind_node(sk, &pp->owner);
		sctp_sk(sk)->bind_hash = pp;
	}
	ret = 0;

fail_unlock:
	spin_unlock(&head->lock);

fail:
	local_bh_enable();
	return ret;
}


static void sctp_wake_up_waiters(struct sock *sk,
				 struct sctp_association *asoc)
{
	struct sctp_association *tmp = asoc;

	/* We do accounting for the sndbuf space per association,
	 * so we only need to wake our own association.
	 */
	if (asoc->ep->sndbuf_policy)
		return __sctp_write_space(asoc);

	/* If association goes down and is just flushing its
	 * outq, then just normally notify others.
	 */
	if (asoc->base.dead)
		return sctp_write_space(sk);

	/* Accounting for the sndbuf space is per socket, so we
	 * need to wake up others, try to be fair and in case of
	 * other associations, let them have a go first instead
	 * of just doing a sctp_write_space() call.
	 *
	 * Note that we reach sctp_wake_up_waiters() only when
	 * associations free up queued chunks, thus we are under
	 * lock and the list of associations on a socket is
	 * guaranteed not to change.
	 */
	for (tmp = list_next_entry(tmp, asocs); 1;
	     tmp = list_next_entry(tmp, asocs)) {
		/* Manually skip the head element. */
		if (&tmp->asocs == &((sctp_sk(sk))->ep->asocs))
			continue;
		/* Wake up association. */
		__sctp_write_space(tmp);
		/* We've reached the end. */
		if (tmp == asoc)
			break;
	}
}


static int sctp_setsockopt_default_prinfo(struct sock *sk,
					  char __user *optval,
					  unsigned int optlen)
{
	struct sctp_default_prinfo info;
	struct sctp_association *asoc;
	int retval = -EINVAL;

	if (optlen != sizeof(info))
		goto out;

	if (copy_from_user(&info, optval, sizeof(info))) {
		retval = -EFAULT;
		goto out;
	}

	if (info.pr_policy & ~SCTP_PR_SCTP_MASK)
		goto out;

	if (info.pr_policy == SCTP_PR_SCTP_NONE)
		info.pr_value = 0;

	asoc = sctp_id2assoc(sk, info.pr_assoc_id);
	if (asoc) {
		SCTP_PR_SET_POLICY(asoc->default_flags, info.pr_policy);
		asoc->default_timetolive = info.pr_value;
	} else if (!info.pr_assoc_id) {
		struct sctp_sock *sp = sctp_sk(sk);

		SCTP_PR_SET_POLICY(sp->default_flags, info.pr_policy);
		sp->default_timetolive = info.pr_value;
	} else {
		goto out;
	}

	retval = 0;

out:
	return retval;
}


static int sctp_wait_for_connect(struct sctp_association *asoc, long *timeo_p)
{
	struct sock *sk = asoc->base.sk;
	int err = 0;
	long current_timeo = *timeo_p;
	DEFINE_WAIT(wait);

	pr_debug("%s: asoc:%p, timeo:%ld\n", __func__, asoc, *timeo_p);

	/* Increment the association's refcnt.  */
	sctp_association_hold(asoc);

	for (;;) {
		prepare_to_wait_exclusive(&asoc->wait, &wait,
					  TASK_INTERRUPTIBLE);
		if (!*timeo_p)
			goto do_nonblock;
		if (sk->sk_shutdown & RCV_SHUTDOWN)
			break;
		if (sk->sk_err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING ||
		    asoc->base.dead)
			goto do_error;
		if (signal_pending(current))
			goto do_interrupted;

		if (sctp_state(asoc, ESTABLISHED))
			break;

		/* Let another process have a go.  Since we are going
		 * to sleep anyway.
		 */
		release_sock(sk);
		current_timeo = schedule_timeout(current_timeo);
		lock_sock(sk);

		*timeo_p = current_timeo;
	}

out:
	finish_wait(&asoc->wait, &wait);

	/* Release the association's refcnt.  */
	sctp_association_put(asoc);

	return err;

do_error:
	if (asoc->init_err_counter + 1 > asoc->max_init_attempts)
		err = -ETIMEDOUT;
	else
		err = -ECONNREFUSED;
	goto out;

do_interrupted:
	err = sock_intr_errno(*timeo_p);
	goto out;

do_nonblock:
	err = -EINPROGRESS;
	goto out;
}


static char *
list_transform (xc, v, list, itype, quoted)
     int xc;
     SHELL_VAR *v;
     WORD_LIST *list;
     int itype, quoted;
{
  WORD_LIST *new, *l;
  WORD_DESC *w;
  char *tword;

  for (new = (WORD_LIST *)NULL, l = list; l; l = l->next)
    {
      tword = string_transform (xc, v, l->word->word);
      w = alloc_word_desc ();
      w->word = tword ? tword : savestring ("");	/* XXX */
      new = make_word_list (w, new);
    }

  l = REVERSE_LIST (new, WORD_LIST *);
  tword = string_list_pos_params (itype, l, quoted);
  dispose_words (l);

  return (tword);


prog_complete_return (text, matchnum)
     const char *text;
     int matchnum;
{
  static int ind;

  if (matchnum == 0)
    ind = 0;

  if (prog_complete_matches == 0 || prog_complete_matches[ind] == 0)
    return (char *)NULL;
  return (prog_complete_matches[ind++]);
}


char *
string_list_internal (list, sep)
     WORD_LIST *list;
     char *sep;
{
  register WORD_LIST *t;
  char *result, *r;
  size_t word_len, sep_len, result_size;

  if (list == 0)
    return ((char *)NULL);

  /* Short-circuit quickly if we don't need to separate anything. */
  if (list->next == 0)
    return (savestring (list->word->word));

  /* This is nearly always called with either sep[0] == 0 or sep[1] == 0. */
  sep_len = STRLEN (sep);
  result_size = 0;

  for (t = list; t; t = t->next)
    {
      if (t != list)
	result_size += sep_len;
      result_size += strlen (t->word->word);
    }

  r = result = (char *)xmalloc (result_size + 1);

  for (t = list; t; t = t->next)
    {
      if (t != list && sep_len)
	{
	  if (sep_len > 1)
	    {
	      FASTCOPY (sep, r, sep_len);
	      r += sep_len;
	    }
	  else
	    *r++ = sep[0];
	}

      word_len = strlen (t->word->word);
      FASTCOPY (t->word->word, r, word_len);
      r += word_len;
    }

  *r = '\0';
  return (result);


shell_expand_line (count, ignore)
     int count, ignore;
{
  char *new_line;
  WORD_LIST *expanded_string;

  new_line = 0;
#if defined (BANG_HISTORY)
  new_line = history_expand_line_internal (rl_line_buffer);
#endif

#if defined (ALIAS)
  if (new_line)
    {
      char *alias_line;

      alias_line = alias_expand (new_line);
      free (new_line);
      new_line = alias_line;
    }
#endif /* ALIAS */

  if (new_line)
    {
      int old_point = rl_point;
      int at_end = rl_point == rl_end;

      /* If the line was history and alias expanded, then make that
	 be one thing to undo. */
      maybe_make_readline_line (new_line);
      free (new_line);

      /* If there is variable expansion to perform, do that as a separate
	 operation to be undone. */
      new_line = savestring (rl_line_buffer);
      expanded_string = expand_string (new_line, 0);
      FREE (new_line);
      if (expanded_string == 0)
	{
	  new_line = (char *)xmalloc (1);
	  new_line[0] = '\0';
	}
      else
	{
	  new_line = string_list (expanded_string);
	  dispose_words (expanded_string);
	}

      maybe_make_readline_line (new_line);
      free (new_line);

      /* Place rl_point where we think it should go. */
      if (at_end)
	rl_point = rl_end;
      else if (old_point < rl_end)
	{
	  rl_point = old_point;
	  if (!whitespace (rl_line_buffer[rl_point]))
	    rl_forward_word (1, 0);
	}
      return 0;
    }
  else
    {
      cleanup_expansion_error ();
      return 1;
    }
}


putx(c)
     int c;
{
  int x;
  x = putc (c, rl_outstream);
#ifndef _MINIX
  return x;
#endif
}


edit_and_execute_command (count, c, editing_mode, edit_command)
     int count, c, editing_mode;
     char *edit_command;
{
  char *command, *metaval;
  int r, rrs, metaflag;
  sh_parser_state_t ps;

  rrs = rl_readline_state;
  saved_command_line_count = current_command_line_count;

  /* Accept the current line. */
  rl_newline (1, c);

  if (rl_explicit_arg)
    {
      command = (char *)xmalloc (strlen (edit_command) + 8);
      sprintf (command, "%s %d", edit_command, count);
    }
  else
    {
      /* Take the command we were just editing, add it to the history file,
	 then call fc to operate on it.  We have to add a dummy command to
	 the end of the history because fc ignores the last command (assumes
	 it's supposed to deal with the command before the `fc'). */
      /* This breaks down when using command-oriented history and are not
	 finished with the command, so we should not ignore the last command */
      using_history ();
      current_command_line_count++;	/* for rl_newline above */
      bash_add_history (rl_line_buffer);
      current_command_line_count = 0;	/* for dummy history entry */
      bash_add_history ("");
      history_lines_this_session++;
      using_history ();
      command = savestring (edit_command);
    }

  metaval = rl_variable_value ("input-meta");
  metaflag = RL_BOOLEAN_VARIABLE_VALUE (metaval);

  if (rl_deprep_term_function)
    (*rl_deprep_term_function) ();
  save_parser_state (&ps);
  r = parse_and_execute (command, (editing_mode == VI_EDITING_MODE) ? "v" : "C-xC-e", SEVAL_NOHIST);
  restore_parser_state (&ps);
  if (rl_prep_term_function)
    (*rl_prep_term_function) (metaflag);

  current_command_line_count = saved_command_line_count;

  /* Now erase the contents of the current line and undo the effects of the
     rl_accept_line() above.  We don't even want to make the text we just
     executed available for undoing. */
  rl_line_buffer[0] = '\0';	/* XXX */
  rl_point = rl_end = 0;
  rl_done = 0;
  rl_readline_state = rrs;

  rl_forced_update_display ();

  return r;
}


WORD_LIST *
expand_word (word, quoted)
     WORD_DESC *word;
     int quoted;
{
  WORD_LIST *result, *tresult;

  tresult = call_expand_word_internal (word, quoted, 0, (int *)NULL, (int *)NULL);
  result = word_list_split (tresult);
  dispose_words (tresult);
  return (result ? dequote_list (result) : result);


tcsh_magic_space (count, ignore)
     int count, ignore;
{
  int dist_from_end, old_point;

  old_point = rl_point;
  dist_from_end = rl_end - rl_point;
  if (history_expand_line (count, ignore) == 0)
    {
      /* Try a simple heuristic from Stephen Gildea <gildea@intouchsys.com>.
	 This works if all expansions were before rl_point or if no expansions
	 were performed. */
      rl_point = (old_point == 0) ? old_point : rl_end - dist_from_end;
      rl_insert (1, ' ');
      return (0);
    }
  else
    return (1);
}


static char *
quoted_strchr (s, c, flags)
     char *s;
     int c, flags;
{
  register char *p;

  for (p = s; *p; p++)
    {
      if (((flags & ST_BACKSL) && *p == '\\')
	    || ((flags & ST_CTLESC) && *p == CTLESC))
	{
	  p++;
	  if (*p == '\0')
	    return ((char *)NULL);
	  continue;
	}
      else if (*p == c)
	return p;
    }
  return ((char *)NULL);


bash_backward_kill_shellword (count, key)
     int count, key;
{
  int p;

  if (count < 0)
    return (bash_kill_shellword (-count, key));

  p = rl_point;
  bash_backward_shellword (count, key);

  if (rl_point != p)
    rl_kill_text (p, rl_point);

  if (rl_editing_mode == 1)	/* 1 == emacs_mode */
    rl_mark = rl_point;

  return 0;
}


bash_complete_filename_internal (what_to_do)
     int what_to_do;
{
  rl_compentry_func_t *orig_func;
  rl_completion_func_t *orig_attempt_func;
  rl_icppfunc_t *orig_dir_func;
  rl_compignore_func_t *orig_ignore_func;
  /*const*/ char *orig_rl_completer_word_break_characters;
  int r;

  orig_func = rl_completion_entry_function;
  orig_attempt_func = rl_attempted_completion_function;
  orig_ignore_func = rl_ignore_some_completions_function;
  orig_rl_completer_word_break_characters = rl_completer_word_break_characters;

  orig_dir_func = save_directory_hook ();

  rl_completion_entry_function = rl_filename_completion_function;
  rl_attempted_completion_function = (rl_completion_func_t *)NULL;
  rl_ignore_some_completions_function = filename_completion_ignore;
  rl_completer_word_break_characters = " \t\n\"\'";

  r = rl_complete_internal (what_to_do);

  rl_completion_entry_function = orig_func;
  rl_attempted_completion_function = orig_attempt_func;
  rl_ignore_some_completions_function = orig_ignore_func;
  rl_completer_word_break_characters = orig_rl_completer_word_break_characters;

  restore_directory_hook (orig_dir_func);

  return r;
}


bash_possible_filename_completions (ignore, ignore2)
     int ignore, ignore2;
{
  return bash_complete_filename_internal ('?');
}


display_shell_version (count, c)
     int count, c;
{
  rl_crlf ();
  show_shell_version (0);
  putc ('\r', rl_outstream);
  fflush (rl_outstream);
  rl_on_new_line ();
  rl_redisplay ();
  return 0;
}


char *
get_dollar_var_value (ind)
     intmax_t ind;
{
  char *temp;
  WORD_LIST *p;

  if (ind < 10)
    temp = dollar_vars[ind] ? savestring (dollar_vars[ind]) : (char *)NULL;
  else	/* We want something like ${11} */
    {
      ind -= 10;
      for (p = rest_of_args; p && ind--; p = p->next)
	;
      temp = p ? savestring (p->word->word) : (char *)NULL;
    }
  return (temp);


static char *
remove_upattern (param, pattern, op)
     char *param, *pattern;
     int op;
{
  register size_t len;
  register char *end;
  register char *p, *ret, c;

  len = STRLEN (param);
  end = param + len;

  switch (op)
    {
      case RP_LONG_LEFT:	/* remove longest match at start */
	for (p = end; p >= param; p--)
	  {
	    c = *p; *p = '\0';
	    if (strmatch (pattern, param, FNMATCH_EXTFLAG) != FNM_NOMATCH)
	      {
		*p = c;
		return (savestring (p));
	      }
	    *p = c;

	  }
	break;

      case RP_SHORT_LEFT:	/* remove shortest match at start */
	for (p = param; p <= end; p++)
	  {
	    c = *p; *p = '\0';
	    if (strmatch (pattern, param, FNMATCH_EXTFLAG) != FNM_NOMATCH)
	      {
		*p = c;
		return (savestring (p));
	      }
	    *p = c;
	  }
	break;

      case RP_LONG_RIGHT:	/* remove longest match at end */
	for (p = param; p <= end; p++)
	  {
	    if (strmatch (pattern, p, FNMATCH_EXTFLAG) != FNM_NOMATCH)
	      {
		c = *p; *p = '\0';
		ret = savestring (param);
		*p = c;
		return (ret);
	      }
	  }
	break;

      case RP_SHORT_RIGHT:	/* remove shortest match at end */
	for (p = end; p >= param; p--)
	  {
	    if (strmatch (pattern, p, FNMATCH_EXTFLAG) != FNM_NOMATCH)
	      {
		c = *p; *p = '\0';
		ret = savestring (param);
		*p = c;
		return (ret);
	      }
	  }
	break;
    }

  return (param);	/* no match, return original string */


restore_tilde (val, directory_part)
     char *val, *directory_part;
{
  int l, vl, dl2, xl;
  char *dh2, *expdir, *ret;

  vl = strlen (val);

  /* We need to duplicate the expansions readline performs on the directory
     portion before passing it to our completion function. */
  dh2 = directory_part ? bash_dequote_filename (directory_part, 0) : 0;
  bash_directory_expansion (&dh2);
  dl2 = strlen (dh2);

  expdir = bash_tilde_expand (directory_part, 0);
  xl = strlen (expdir);
  free (expdir);

  /*
     dh2 = unexpanded but dequoted tilde-prefix
     dl2 = length of tilde-prefix
     expdir = tilde-expanded tilde-prefix
     xl = length of expanded tilde-prefix
     l = length of remainder after tilde-prefix
  */
  l = (vl - xl) + 1;

  ret = (char *)xmalloc (dl2 + 2 + l);
  strcpy (ret, dh2);
  strcpy (ret + dl2, val + xl);

  free (dh2);
  return (ret);
}


static char *
extract_dollar_brace_string (string, sindex, quoted, flags)
     char *string;
     int *sindex, quoted, flags;
{
  register int i, c;
  size_t slen;
  int pass_character, nesting_level, si, dolbrace_state;
  char *result, *t;
  DECLARE_MBSTATE;

  pass_character = 0;
  nesting_level = 1;
  slen = strlen (string + *sindex) + *sindex;

  /* The handling of dolbrace_state needs to agree with the code in parse.y:
     parse_matched_pair().  The different initial value is to handle the
     case where this function is called to parse the word in
     ${param op word} (SX_WORD). */
  dolbrace_state = (flags & SX_WORD) ? DOLBRACE_WORD : DOLBRACE_PARAM;
  if ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) && (flags & SX_POSIXEXP))
    dolbrace_state = DOLBRACE_QUOTE;

  i = *sindex;
  while (c = string[i])
    {
      if (pass_character)
	{
	  pass_character = 0;
	  ADVANCE_CHAR (string, slen, i);
	  continue;
	}

      /* CTLESCs and backslashes quote the next character. */
      if (c == CTLESC || c == '\\')
	{
	  pass_character++;
	  i++;
	  continue;
	}

      if (string[i] == '$' && string[i+1] == LBRACE)
	{
	  nesting_level++;
	  i += 2;
	  continue;
	}

      if (c == RBRACE)
	{
	  nesting_level--;
	  if (nesting_level == 0)
	    break;
	  i++;
	  continue;
	}

      /* Pass the contents of old-style command substitutions through
	 verbatim. */
      if (c == '`')
	{
	  si = i + 1;
	  t = string_extract (string, &si, "`", flags|SX_NOALLOC);

	  CHECK_STRING_OVERRUN (i, si, slen, c);

	  i = si + 1;
	  continue;
	}

      /* Pass the contents of new-style command substitutions and
	 arithmetic substitutions through verbatim. */
      if (string[i] == '$' && string[i+1] == LPAREN)
	{
	  si = i + 2;
	  t = extract_command_subst (string, &si, flags|SX_NOALLOC);
	  i = si + 1;
	  continue;
	}

      /* Pass the contents of double-quoted strings through verbatim. */
      if (c == '"')
	{
	  si = i + 1;
	  i = skip_double_quoted (string, slen, si, 0);
	  /* skip_XXX_quoted leaves index one past close quote */
	  continue;
	}

      if (c == '\'')
	{
/*itrace("extract_dollar_brace_string: c == single quote flags = %d quoted = %d dolbrace_state = %d", flags, quoted, dolbrace_state);*/
	  if (posixly_correct && shell_compatibility_level > 42 && dolbrace_state != DOLBRACE_QUOTE && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
	    ADVANCE_CHAR (string, slen, i);
	  else
	    {
	      si = i + 1;
	      i = skip_single_quoted (string, slen, si, 0);
	    }

          continue;
	}

      /* move past this character, which was not special. */
      ADVANCE_CHAR (string, slen, i);

      /* This logic must agree with parse.y:parse_matched_pair, since they
	 share the same defines. */
      if (dolbrace_state == DOLBRACE_PARAM && c == '%' && (i - *sindex) > 1)
	dolbrace_state = DOLBRACE_QUOTE;
      else if (dolbrace_state == DOLBRACE_PARAM && c == '#' && (i - *sindex) > 1)
        dolbrace_state = DOLBRACE_QUOTE;
      else if (dolbrace_state == DOLBRACE_PARAM && c == '/' && (i - *sindex) > 1)
        dolbrace_state = DOLBRACE_QUOTE2;	/* XXX */
      else if (dolbrace_state == DOLBRACE_PARAM && c == '^' && (i - *sindex) > 1)
        dolbrace_state = DOLBRACE_QUOTE;
      else if (dolbrace_state == DOLBRACE_PARAM && c == ',' && (i - *sindex) > 1)
        dolbrace_state = DOLBRACE_QUOTE;
      /* This is intended to handle all of the [:]op expansions and the substring/
	 length/pattern removal/pattern substitution expansions. */
      else if (dolbrace_state == DOLBRACE_PARAM && strchr ("#%^,~:-=?+/", c) != 0)
	dolbrace_state = DOLBRACE_OP;
      else if (dolbrace_state == DOLBRACE_OP && strchr ("#%^,~:-=?+/", c) == 0)
	dolbrace_state = DOLBRACE_WORD;
    }

  if (c == 0 && nesting_level)
    {
      if (no_longjmp_on_fatal_error == 0)
	{			/* { */
	  last_command_exit_value = EXECUTION_FAILURE;
	  report_error (_("bad substitution: no closing `%s' in %s"), "}", string);
	  exp_jump_to_top_level (DISCARD);
	}
      else
	{
	  *sindex = i;
	  return ((char *)NULL);
	}
    }

  result = (flags & SX_NOALLOC) ? (char *)NULL : substring (string, *sindex, i);
  *sindex = i;

  return (result);


posix_readline_initialize (on_or_off)
     int on_or_off;
{
  if (on_or_off)
    rl_variable_bind ("comment-begin", "#");
#if defined (VI_MODE)
  rl_bind_key_in_map (CTRL ('I'), on_or_off ? rl_insert : rl_complete, vi_insertion_keymap);
#endif
}


set_directory_hook ()
{
  if (dircomplete_expand)
    {
      rl_directory_completion_hook = bash_directory_completion_hook;
      rl_directory_rewrite_hook = (rl_icppfunc_t *)0;
    }
  else
    {
      rl_directory_rewrite_hook = bash_directory_completion_hook;
      rl_directory_completion_hook = (rl_icppfunc_t *)0;
    }
}


char *
string_list_pos_params (pchar, list, quoted)
     int pchar;
     WORD_LIST *list;
     int quoted;
{
  char *ret;
  WORD_LIST *tlist;

  if (pchar == '*' && (quoted & Q_DOUBLE_QUOTES))
    {
      tlist = quote_list (list);
      word_list_remove_quoted_nulls (tlist);
      ret = string_list_dollar_star (tlist);
    }
  else if (pchar == '*' && (quoted & Q_HERE_DOCUMENT))
    {
      tlist = quote_list (list);
      word_list_remove_quoted_nulls (tlist);
      ret = string_list (tlist);
    }
  else if (pchar == '*')
    {
      /* Even when unquoted, string_list_dollar_star does the right thing
	 making sure that the first character of $IFS is used as the
	 separator. */
      ret = string_list_dollar_star (list);
    }
  else if (pchar == '@' && (quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)))
    /* We use string_list_dollar_at, but only if the string is quoted, since
       that quotes the escapes if it's not, which we don't want.  We could
       use string_list (the old code did), but that doesn't do the right
       thing if the first character of $IFS is not a space.  We use
       string_list_dollar_star if the string is unquoted so we make sure that
       the elements of $@ are separated by the first character of $IFS for
       later splitting. */
    ret = string_list_dollar_at (list, quoted, 0);
  else if (pchar == '@')
    ret = string_list_dollar_star (list);
  else
    ret = string_list ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) ? quote_list (list) : list);

  return ret;


void
unlink_fifo (fd)
     int fd;
{
  if (dev_fd_list[fd])
    {
      close (fd);
      dev_fd_list[fd] = 0;
      nfds--;
    }


void
unquote_bang (string)
     char *string;
{
  register int i, j;
  register char *temp;

  temp = (char *)xmalloc (1 + strlen (string));

  for (i = 0, j = 0; (temp[j] = string[i]); i++, j++)
    {
      if (string[i] == '\\' && string[i + 1] == '!')
	{
	  temp[j] = '!';
	  i++;
	}
    }
  strcpy (string, temp);
  free (temp);


WORD_LIST *
word_split (w, ifs_chars)
     WORD_DESC *w;
     char *ifs_chars;
{
  WORD_LIST *result;

  if (w)
    {
      char *xifs;

      xifs = ((w->flags & W_QUOTED) || ifs_chars == 0) ? "" : ifs_chars;
      result = list_string (w->word, xifs, w->flags & W_QUOTED);
    }
  else
    result = (WORD_LIST *)NULL;

  return (result);


char *
extract_process_subst (string, starter, sindex, xflags)
     char *string;
     char *starter;
     int *sindex;
     int xflags;
{
#if 0
  return (extract_delimited_string (string, sindex, starter, "(", ")", SX_COMMAND));
#else
  xflags |= (no_longjmp_on_fatal_error ? SX_NOLONGJMP : 0);
  return (xparse_dolparen (string, string+*sindex, sindex, xflags));
#endif


char *
copy_fifo_list (sizep)
     int *sizep;
{
  if (sizep)
    *sizep = 0;
  return (char *)NULL;


bash_directory_completion_hook (dirname)
     char **dirname;
{
  char *local_dirname, *new_dirname, *t;
  int return_value, should_expand_dirname, nextch, closer, changed;
  size_t local_dirlen;
  WORD_LIST *wl;
  struct stat sb;

  return_value = should_expand_dirname = nextch = closer = 0;
  local_dirname = *dirname;

  if (t = mbschr (local_dirname, '$'))
    {
      should_expand_dirname = '$';
      nextch = t[1];
      /* Deliberately does not handle the deprecated $[...] arithmetic
	 expansion syntax */
      if (nextch == '(')
	closer = ')';
      else if (nextch == '{')
	closer = '}';
      else
	nextch = 0;
    }
  else if (local_dirname[0] == '~')
    should_expand_dirname = '~';
  else
    {
      t = mbschr (local_dirname, '`');
      if (t && unclosed_pair (local_dirname, strlen (local_dirname), "`") == 0)
	should_expand_dirname = '`';
    }

  if (should_expand_dirname && directory_exists (local_dirname, 1))
    should_expand_dirname = 0;

  if (should_expand_dirname)
    {
      new_dirname = savestring (local_dirname);
      wl = expand_prompt_string (new_dirname, 0, W_NOCOMSUB|W_NOPROCSUB|W_COMPLETE);	/* does the right thing */
      if (wl)
	{
	  *dirname = string_list (wl);
	  /* Tell the completer to replace the directory name only if we
	     actually expanded something. */
	  return_value = STREQ (local_dirname, *dirname) == 0;
	  free (local_dirname);
	  free (new_dirname);
	  dispose_words (wl);
	  local_dirname = *dirname;
	  /* XXX - change rl_filename_quote_characters here based on
	     should_expand_dirname/nextch/closer.  This is the only place
	     custom_filename_quote_characters is modified. */
	  if (rl_filename_quote_characters && *rl_filename_quote_characters)
	    {
	      int i, j, c;
	      i = strlen (default_filename_quote_characters);
	      custom_filename_quote_characters = xrealloc (custom_filename_quote_characters, i+1);
	      for (i = j = 0; c = default_filename_quote_characters[i]; i++)
		{
		  if (c == should_expand_dirname || c == nextch || c == closer)
		    continue;
		  custom_filename_quote_characters[j++] = c;
		}
	      custom_filename_quote_characters[j] = '\0';
	      rl_filename_quote_characters = custom_filename_quote_characters;
	      set_filename_bstab (rl_filename_quote_characters);
	    }
	}
      else
	{
	  free (new_dirname);
	  free (local_dirname);
	  *dirname = (char *)xmalloc (1);
	  **dirname = '\0';
	  return 1;
	}
    }
  else
    {
      /* Dequote the filename even if we don't expand it. */
      new_dirname = bash_dequote_filename (local_dirname, rl_completion_quote_character);
      return_value = STREQ (local_dirname, new_dirname) == 0;
      free (local_dirname);
      local_dirname = *dirname = new_dirname;
    }

  /* no_symbolic_links == 0 -> use (default) logical view of the file system.
     local_dirname[0] == '.' && local_dirname[1] == '/' means files in the
     current directory (./).
     local_dirname[0] == '.' && local_dirname[1] == 0 means relative pathnames
     in the current directory (e.g., lib/sh).
     XXX - should we do spelling correction on these? */

  /* This is test as it was in bash-4.2: skip relative pathnames in current
     directory.  Change test to
      (local_dirname[0] != '.' || (local_dirname[1] && local_dirname[1] != '/'))
     if we want to skip paths beginning with ./ also. */
  if (no_symbolic_links == 0 && (local_dirname[0] != '.' || local_dirname[1]))
    {
      char *temp1, *temp2;
      int len1, len2;

      /* If we have a relative path
      		(local_dirname[0] != '/' && local_dirname[0] != '.')
	 that is canonical after appending it to the current directory, then
	 	temp1 = temp2+'/'
	 That is,
	 	strcmp (temp1, temp2) == 0
	 after adding a slash to temp2 below.  It should be safe to not
	 change those.
      */
      t = get_working_directory ("symlink-hook");
      temp1 = make_absolute (local_dirname, t);
      free (t);
      temp2 = sh_canonpath (temp1, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);

      /* Try spelling correction if initial canonicalization fails.  Make
	 sure we are set to replace the directory name with the results so
	 subsequent directory checks don't fail. */
      if (temp2 == 0 && dircomplete_spelling && dircomplete_expand)
	{
	  temp2 = dirspell (temp1);
	  if (temp2)
	    {
	      free (temp1);
	      temp1 = temp2;
	      temp2 = sh_canonpath (temp1, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
	      return_value |= temp2 != 0;
	    }
	}
      /* If we can't canonicalize, bail. */
      if (temp2 == 0)
	{
	  free (temp1);
	  return return_value;
	}
      len1 = strlen (temp1);
      if (temp1[len1 - 1] == '/')
	{
	  len2 = strlen (temp2);
	  if (len2 > 2)		/* don't append `/' to `/' or `//' */
	    {
	      temp2 = (char *)xrealloc (temp2, len2 + 2);
	      temp2[len2] = '/';
	      temp2[len2 + 1] = '\0';
	    }
	}

      /* dircomplete_expand_relpath == 0 means we want to leave relative
	 pathnames that are unchanged by canonicalization alone.
	 *local_dirname != '/' && *local_dirname != '.' == relative pathname
	 (consistent with general.c:absolute_pathname())
	 temp1 == temp2 (after appending a slash to temp2) means the pathname
	 is not changed by canonicalization as described above. */
      if (dircomplete_expand_relpath || ((local_dirname[0] != '/' && local_dirname[0] != '.') && STREQ (temp1, temp2) == 0))
	return_value |= STREQ (local_dirname, temp2) == 0;
      free (local_dirname);
      *dirname = temp2;
      free (temp1);
    }

  return (return_value);
}


bash_directory_completion_matches (text)
     const char *text;
{
  char **m1;
  char *dfn;
  int qc;

  qc = rl_dispatching ? rl_completion_quote_character : 0;
  /* If rl_completion_found_quote != 0, rl_completion_matches will call the
     filename dequoting function, causing the directory name to be dequoted
     twice. */
  if (rl_dispatching && rl_completion_found_quote == 0)
    dfn = bash_dequote_filename ((char *)text, qc);
  else
    dfn = (char *)text;
  m1 = rl_completion_matches (dfn, rl_filename_completion_function);
  if (dfn != text)
    free (dfn);

  if (m1 == 0 || m1[0] == 0)
    return m1;
  /* We don't bother recomputing the lcd of the matches, because it will just
     get thrown away by the programmable completion code and recomputed
     later. */
  (void)bash_ignore_filenames (m1);
  return m1;
}


WORD_LIST *
list_rest_of_args ()
{
  register WORD_LIST *list, *args;
  int i;

  /* Break out of the loop as soon as one of the dollar variables is null. */
  for (i = 1, list = (WORD_LIST *)NULL; i < 10 && dollar_vars[i]; i++)
    list = make_word_list (make_bare_word (dollar_vars[i]), list);

  for (args = rest_of_args; args; args = args->next)
    list = make_word_list (make_bare_word (args->word->word), list);

  return (REVERSE_LIST (list, WORD_LIST *));


static char *
extract_delimited_string (string, sindex, opener, alt_opener, closer, flags)
     char *string;
     int *sindex;
     char *opener, *alt_opener, *closer;
     int flags;
{
  int i, c, si;
  size_t slen;
  char *t, *result;
  int pass_character, nesting_level, in_comment;
  int len_closer, len_opener, len_alt_opener;
  DECLARE_MBSTATE;

  slen = strlen (string + *sindex) + *sindex;
  len_opener = STRLEN (opener);
  len_alt_opener = STRLEN (alt_opener);
  len_closer = STRLEN (closer);

  pass_character = in_comment = 0;

  nesting_level = 1;
  i = *sindex;

  while (nesting_level)
    {
      c = string[i];

      /* If a recursive call or a call to ADVANCE_CHAR leaves the index beyond
	 the end of the string, catch it and cut the loop. */
      if (i > slen)
	{
	  i = slen;
	  c = string[i = slen];
	  break;
	}

      if (c == 0)
	break;

      if (in_comment)
	{
	  if (c == '\n')
	    in_comment = 0;
	  ADVANCE_CHAR (string, slen, i);
	  continue;
	}

      if (pass_character)	/* previous char was backslash */
	{
	  pass_character = 0;
	  ADVANCE_CHAR (string, slen, i);
	  continue;
	}

      /* Not exactly right yet; should handle shell metacharacters and
	 multibyte characters, too.  See COMMENT_BEGIN define in parse.y */
      if ((flags & SX_COMMAND) && c == '#' && (i == 0 || string[i - 1] == '\n' || shellblank (string[i - 1])))
	{
          in_comment = 1;
          ADVANCE_CHAR (string, slen, i);
          continue;
	}

      if (c == CTLESC || c == '\\')
	{
	  pass_character++;
	  i++;
	  continue;
	}

      /* Process a nested command substitution, but only if we're parsing an
	 arithmetic substitution. */
      if ((flags & SX_COMMAND) && string[i] == '$' && string[i+1] == LPAREN)
        {
          si = i + 2;
          t = extract_command_subst (string, &si, flags|SX_NOALLOC);
          i = si + 1;
          continue;
        }

      /* Process a nested OPENER. */
      if (STREQN (string + i, opener, len_opener))
	{
	  si = i + len_opener;
	  t = extract_delimited_string (string, &si, opener, alt_opener, closer, flags|SX_NOALLOC);
	  i = si + 1;
	  continue;
	}

      /* Process a nested ALT_OPENER */
      if (len_alt_opener && STREQN (string + i, alt_opener, len_alt_opener))
	{
	  si = i + len_alt_opener;
	  t = extract_delimited_string (string, &si, alt_opener, alt_opener, closer, flags|SX_NOALLOC);
	  i = si + 1;
	  continue;
	}

      /* If the current substring terminates the delimited string, decrement
	 the nesting level. */
      if (STREQN (string + i, closer, len_closer))
	{
	  i += len_closer - 1;	/* move to last byte of the closer */
	  nesting_level--;
	  if (nesting_level == 0)
	    break;
	}

      /* Pass old-style command substitution through verbatim. */
      if (c == '`')
	{
	  si = i + 1;
	  t = string_extract (string, &si, "`", flags|SX_NOALLOC);
	  i = si + 1;
	  continue;
	}

      /* Pass single-quoted and double-quoted strings through verbatim. */
      if (c == '\'' || c == '"')
	{
	  si = i + 1;
	  i = (c == '\'') ? skip_single_quoted (string, slen, si, 0)
			  : skip_double_quoted (string, slen, si, 0);
	  continue;
	}

      /* move past this character, which was not special. */
      ADVANCE_CHAR (string, slen, i);
    }

  if (c == 0 && nesting_level)
    {
      if (no_longjmp_on_fatal_error == 0)
	{
	  last_command_exit_value = EXECUTION_FAILURE;
	  report_error (_("bad substitution: no closing `%s' in %s"), closer, string);
	  exp_jump_to_top_level (DISCARD);
	}
      else
	{
	  *sindex = i;
	  return (char *)NULL;
	}
    }

  si = i - *sindex - len_closer + 1;
  if (flags & SX_NOALLOC)
    result = (char *)NULL;
  else
    {
      result = (char *)xmalloc (1 + si);
      strncpy (result, string + *sindex, si);
      result[si] = '\0';
    }
  *sindex = i;

  return (result);


write_vim_spell(spellinfo_T *spin, char_u *fname)
{
    FILE	*fd;
    int		regionmask;
    int		round;
    wordnode_T	*tree;
    int		nodecount;
    int		i;
    int		l;
    garray_T	*gap;
    fromto_T	*ftp;
    char_u	*p;
    int		rr;
    int		retval = OK;
    size_t	fwv = 1;  /* collect return value of fwrite() to avoid
			     warnings from picky compiler */

    fd = mch_fopen((char *)fname, "w");
    if (fd == NULL)
    {
	EMSG2(_(e_notopen), fname);
	return FAIL;
    }

    /* <HEADER>: <fileID> <versionnr> */
							    /* <fileID> */
    fwv &= fwrite(VIMSPELLMAGIC, VIMSPELLMAGICL, (size_t)1, fd);
    if (fwv != (size_t)1)
	/* Catch first write error, don't try writing more. */
	goto theend;

    putc(VIMSPELLVERSION, fd);				    /* <versionnr> */

    /*
     * <SECTIONS>: <section> ... <sectionend>
     */

    /* SN_INFO: <infotext> */
    if (spin->si_info != NULL)
    {
	putc(SN_INFO, fd);				/* <sectionID> */
	putc(0, fd);					/* <sectionflags> */

	i = (int)STRLEN(spin->si_info);
	put_bytes(fd, (long_u)i, 4);			/* <sectionlen> */
	fwv &= fwrite(spin->si_info, (size_t)i, (size_t)1, fd); /* <infotext> */
    }

    /* SN_REGION: <regionname> ...
     * Write the region names only if there is more than one. */
    if (spin->si_region_count > 1)
    {
	putc(SN_REGION, fd);				/* <sectionID> */
	putc(SNF_REQUIRED, fd);				/* <sectionflags> */
	l = spin->si_region_count * 2;
	put_bytes(fd, (long_u)l, 4);			/* <sectionlen> */
	fwv &= fwrite(spin->si_region_name, (size_t)l, (size_t)1, fd);
							/* <regionname> ... */
	regionmask = (1 << spin->si_region_count) - 1;
    }
    else
	regionmask = 0;

    /* SN_CHARFLAGS: <charflagslen> <charflags> <folcharslen> <folchars>
     *
     * The table with character flags and the table for case folding.
     * This makes sure the same characters are recognized as word characters
     * when generating an when using a spell file.
     * Skip this for ASCII, the table may conflict with the one used for
     * 'encoding'.
     * Also skip this for an .add.spl file, the main spell file must contain
     * the table (avoids that it conflicts).  File is shorter too.
     */
    if (!spin->si_ascii && !spin->si_add)
    {
	char_u	folchars[128 * 8];
	int	flags;

	putc(SN_CHARFLAGS, fd);				/* <sectionID> */
	putc(SNF_REQUIRED, fd);				/* <sectionflags> */

	/* Form the <folchars> string first, we need to know its length. */
	l = 0;
	for (i = 128; i < 256; ++i)
	{
#ifdef FEAT_MBYTE
	    if (has_mbyte)
		l += mb_char2bytes(spelltab.st_fold[i], folchars + l);
	    else
#endif
		folchars[l++] = spelltab.st_fold[i];
	}
	put_bytes(fd, (long_u)(1 + 128 + 2 + l), 4);	/* <sectionlen> */

	fputc(128, fd);					/* <charflagslen> */
	for (i = 128; i < 256; ++i)
	{
	    flags = 0;
	    if (spelltab.st_isw[i])
		flags |= CF_WORD;
	    if (spelltab.st_isu[i])
		flags |= CF_UPPER;
	    fputc(flags, fd);				/* <charflags> */
	}

	put_bytes(fd, (long_u)l, 2);			/* <folcharslen> */
	fwv &= fwrite(folchars, (size_t)l, (size_t)1, fd); /* <folchars> */
    }

    /* SN_MIDWORD: <midword> */
    if (spin->si_midword != NULL)
    {
	putc(SN_MIDWORD, fd);				/* <sectionID> */
	putc(SNF_REQUIRED, fd);				/* <sectionflags> */

	i = (int)STRLEN(spin->si_midword);
	put_bytes(fd, (long_u)i, 4);			/* <sectionlen> */
	fwv &= fwrite(spin->si_midword, (size_t)i, (size_t)1, fd);
							/* <midword> */
    }

    /* SN_PREFCOND: <prefcondcnt> <prefcond> ... */
    if (spin->si_prefcond.ga_len > 0)
    {
	putc(SN_PREFCOND, fd);				/* <sectionID> */
	putc(SNF_REQUIRED, fd);				/* <sectionflags> */

	l = write_spell_prefcond(NULL, &spin->si_prefcond);
	put_bytes(fd, (long_u)l, 4);			/* <sectionlen> */

	write_spell_prefcond(fd, &spin->si_prefcond);
    }

    /* SN_REP: <repcount> <rep> ...
     * SN_SAL: <salflags> <salcount> <sal> ...
     * SN_REPSAL: <repcount> <rep> ... */

    /* round 1: SN_REP section
     * round 2: SN_SAL section (unless SN_SOFO is used)
     * round 3: SN_REPSAL section */
    for (round = 1; round <= 3; ++round)
    {
	if (round == 1)
	    gap = &spin->si_rep;
	else if (round == 2)
	{
	    /* Don't write SN_SAL when using a SN_SOFO section */
	    if (spin->si_sofofr != NULL && spin->si_sofoto != NULL)
		continue;
	    gap = &spin->si_sal;
	}
	else
	    gap = &spin->si_repsal;

	/* Don't write the section if there are no items. */
	if (gap->ga_len == 0)
	    continue;

	/* Sort the REP/REPSAL items. */
	if (round != 2)
	    qsort(gap->ga_data, (size_t)gap->ga_len,
					       sizeof(fromto_T), rep_compare);

	i = round == 1 ? SN_REP : (round == 2 ? SN_SAL : SN_REPSAL);
	putc(i, fd);					/* <sectionID> */

	/* This is for making suggestions, section is not required. */
	putc(0, fd);					/* <sectionflags> */

	/* Compute the length of what follows. */
	l = 2;	    /* count <repcount> or <salcount> */
	for (i = 0; i < gap->ga_len; ++i)
	{
	    ftp = &((fromto_T *)gap->ga_data)[i];
	    l += 1 + (int)STRLEN(ftp->ft_from);  /* count <*fromlen> and <*from> */
	    l += 1 + (int)STRLEN(ftp->ft_to);    /* count <*tolen> and <*to> */
	}
	if (round == 2)
	    ++l;	/* count <salflags> */
	put_bytes(fd, (long_u)l, 4);			/* <sectionlen> */

	if (round == 2)
	{
	    i = 0;
	    if (spin->si_followup)
		i |= SAL_F0LLOWUP;
	    if (spin->si_collapse)
		i |= SAL_COLLAPSE;
	    if (spin->si_rem_accents)
		i |= SAL_REM_ACCENTS;
	    putc(i, fd);			/* <salflags> */
	}

	put_bytes(fd, (long_u)gap->ga_len, 2);	/* <repcount> or <salcount> */
	for (i = 0; i < gap->ga_len; ++i)
	{
	    /* <rep> : <repfromlen> <repfrom> <reptolen> <repto> */
	    /* <sal> : <salfromlen> <salfrom> <saltolen> <salto> */
	    ftp = &((fromto_T *)gap->ga_data)[i];
	    for (rr = 1; rr <= 2; ++rr)
	    {
		p = rr == 1 ? ftp->ft_from : ftp->ft_to;
		l = (int)STRLEN(p);
		putc(l, fd);
		if (l > 0)
		    fwv &= fwrite(p, l, (size_t)1, fd);
	    }
	}

    }

    /* SN_SOFO: <sofofromlen> <sofofrom> <sofotolen> <sofoto>
     * This is for making suggestions, section is not required. */
    if (spin->si_sofofr != NULL && spin->si_sofoto != NULL)
    {
	putc(SN_SOFO, fd);				/* <sectionID> */
	putc(0, fd);					/* <sectionflags> */

	l = (int)STRLEN(spin->si_sofofr);
	put_bytes(fd, (long_u)(l + STRLEN(spin->si_sofoto) + 4), 4);
							/* <sectionlen> */

	put_bytes(fd, (long_u)l, 2);			/* <sofofromlen> */
	fwv &= fwrite(spin->si_sofofr, l, (size_t)1, fd); /* <sofofrom> */

	l = (int)STRLEN(spin->si_sofoto);
	put_bytes(fd, (long_u)l, 2);			/* <sofotolen> */
	fwv &= fwrite(spin->si_sofoto, l, (size_t)1, fd); /* <sofoto> */
    }

    /* SN_WORDS: <word> ...
     * This is for making suggestions, section is not required. */
    if (spin->si_commonwords.ht_used > 0)
    {
	putc(SN_WORDS, fd);				/* <sectionID> */
	putc(0, fd);					/* <sectionflags> */

	/* round 1: count the bytes
	 * round 2: write the bytes */
	for (round = 1; round <= 2; ++round)
	{
	    int		todo;
	    int		len = 0;
	    hashitem_T	*hi;

	    todo = (int)spin->si_commonwords.ht_used;
	    for (hi = spin->si_commonwords.ht_array; todo > 0; ++hi)
		if (!HASHITEM_EMPTY(hi))
		{
		    l = (int)STRLEN(hi->hi_key) + 1;
		    len += l;
		    if (round == 2)			/* <word> */
			fwv &= fwrite(hi->hi_key, (size_t)l, (size_t)1, fd);
		    --todo;
		}
	    if (round == 1)
		put_bytes(fd, (long_u)len, 4);		/* <sectionlen> */
	}
    }

    /* SN_MAP: <mapstr>
     * This is for making suggestions, section is not required. */
    if (spin->si_map.ga_len > 0)
    {
	putc(SN_MAP, fd);				/* <sectionID> */
	putc(0, fd);					/* <sectionflags> */
	l = spin->si_map.ga_len;
	put_bytes(fd, (long_u)l, 4);			/* <sectionlen> */
	fwv &= fwrite(spin->si_map.ga_data, (size_t)l, (size_t)1, fd);
							/* <mapstr> */
    }

    /* SN_SUGFILE: <timestamp>
     * This is used to notify that a .sug file may be available and at the
     * same time allows for checking that a .sug file that is found matches
     * with this .spl file.  That's because the word numbers must be exactly
     * right. */
    if (!spin->si_nosugfile
	    && (spin->si_sal.ga_len > 0
		     || (spin->si_sofofr != NULL && spin->si_sofoto != NULL)))
    {
	putc(SN_SUGFILE, fd);				/* <sectionID> */
	putc(0, fd);					/* <sectionflags> */
	put_bytes(fd, (long_u)8, 4);			/* <sectionlen> */

	/* Set si_sugtime and write it to the file. */
	spin->si_sugtime = time(NULL);
	put_time(fd, spin->si_sugtime);			/* <timestamp> */
    }

    /* SN_NOSPLITSUGS: nothing
     * This is used to notify that no suggestions with word splits are to be
     * made. */
    if (spin->si_nosplitsugs)
    {
	putc(SN_NOSPLITSUGS, fd);			/* <sectionID> */
	putc(0, fd);					/* <sectionflags> */
	put_bytes(fd, (long_u)0, 4);			/* <sectionlen> */
    }

    /* SN_NOCOMPUNDSUGS: nothing
     * This is used to notify that no suggestions with compounds are to be
     * made. */
    if (spin->si_nocompoundsugs)
    {
	putc(SN_NOCOMPOUNDSUGS, fd);			/* <sectionID> */
	putc(0, fd);					/* <sectionflags> */
	put_bytes(fd, (long_u)0, 4);			/* <sectionlen> */
    }

    /* SN_COMPOUND: compound info.
     * We don't mark it required, when not supported all compound words will
     * be bad words. */
    if (spin->si_compflags != NULL)
    {
	putc(SN_COMPOUND, fd);				/* <sectionID> */
	putc(0, fd);					/* <sectionflags> */

	l = (int)STRLEN(spin->si_compflags);
	for (i = 0; i < spin->si_comppat.ga_len; ++i)
	    l += (int)STRLEN(((char_u **)(spin->si_comppat.ga_data))[i]) + 1;
	put_bytes(fd, (long_u)(l + 7), 4);		/* <sectionlen> */

	putc(spin->si_compmax, fd);			/* <compmax> */
	putc(spin->si_compminlen, fd);			/* <compminlen> */
	putc(spin->si_compsylmax, fd);			/* <compsylmax> */
	putc(0, fd);		/* for Vim 7.0b compatibility */
	putc(spin->si_compoptions, fd);			/* <compoptions> */
	put_bytes(fd, (long_u)spin->si_comppat.ga_len, 2);
							/* <comppatcount> */
	for (i = 0; i < spin->si_comppat.ga_len; ++i)
	{
	    p = ((char_u **)(spin->si_comppat.ga_data))[i];
	    putc((int)STRLEN(p), fd);			/* <comppatlen> */
	    fwv &= fwrite(p, (size_t)STRLEN(p), (size_t)1, fd);
							/* <comppattext> */
	}
							/* <compflags> */
	fwv &= fwrite(spin->si_compflags, (size_t)STRLEN(spin->si_compflags),
							       (size_t)1, fd);
    }

    /* SN_NOBREAK: NOBREAK flag */
    if (spin->si_nobreak)
    {
	putc(SN_NOBREAK, fd);				/* <sectionID> */
	putc(0, fd);					/* <sectionflags> */

	/* It's empty, the presence of the section flags the feature. */
	put_bytes(fd, (long_u)0, 4);			/* <sectionlen> */
    }

    /* SN_SYLLABLE: syllable info.
     * We don't mark it required, when not supported syllables will not be
     * counted. */
    if (spin->si_syllable != NULL)
    {
	putc(SN_SYLLABLE, fd);				/* <sectionID> */
	putc(0, fd);					/* <sectionflags> */

	l = (int)STRLEN(spin->si_syllable);
	put_bytes(fd, (long_u)l, 4);			/* <sectionlen> */
	fwv &= fwrite(spin->si_syllable, (size_t)l, (size_t)1, fd);
							/* <syllable> */
    }

    /* end of <SECTIONS> */
    putc(SN_END, fd);					/* <sectionend> */

    /*
     * <LWORDTREE>  <KWORDTREE>  <PREFIXTREE>
     */
    spin->si_memtot = 0;
    for (round = 1; round <= 3; ++round)
    {
	if (round == 1)
	    tree = spin->si_foldroot->wn_sibling;
	else if (round == 2)
	    tree = spin->si_keeproot->wn_sibling;
	else
	    tree = spin->si_prefroot->wn_sibling;

	/* Clear the index and wnode fields in the tree. */
	clear_node(tree);

	/* Count the number of nodes.  Needed to be able to allocate the
	 * memory when reading the nodes.  Also fills in index for shared
	 * nodes. */
	nodecount = put_node(NULL, tree, 0, regionmask, round == 3);

	/* number of nodes in 4 bytes */
	put_bytes(fd, (long_u)nodecount, 4);	/* <nodecount> */
	spin->si_memtot += nodecount + nodecount * sizeof(int);

	/* Write the nodes. */
	(void)put_node(fd, tree, 0, regionmask, round == 3);
    }

    /* Write another byte to check for errors (file system full). */
    if (putc(0, fd) == EOF)
	retval = FAIL;
theend:
    if (fclose(fd) == EOF)
	retval = FAIL;

    if (fwv != (size_t)1)
	retval = FAIL;
    if (retval == FAIL)
	EMSG(_(e_write));

    return retval;
}


aff_check_string(char_u *spinval, char_u *affval, char *name)
{
    if (spinval != NULL && STRCMP(spinval, affval) != 0)
	smsg((char_u *)_("%s value differs from what is used in another .aff file"), name);
}


write_spell_prefcond(FILE *fd, garray_T *gap)
{
    int		i;
    char_u	*p;
    int		len;
    int		totlen;
    size_t	x = 1;  /* collect return value of fwrite() */

    if (fd != NULL)
	put_bytes(fd, (long_u)gap->ga_len, 2);	    /* <prefcondcnt> */

    totlen = 2 + gap->ga_len; /* length of <prefcondcnt> and <condlen> bytes */

    for (i = 0; i < gap->ga_len; ++i)
    {
	/* <prefcond> : <condlen> <condstr> */
	p = ((char_u **)gap->ga_data)[i];
	if (p != NULL)
	{
	    len = (int)STRLEN(p);
	    if (fd != NULL)
	    {
		fputc(len, fd);
		x &= fwrite(p, (size_t)len, (size_t)1, fd);
	    }
	    totlen += len;
	}
	else if (fd != NULL)
	    fputc(0, fd);
    }

    return totlen;
}


read_sal_section(FILE *fd, slang_T *slang)
{
    int		i;
    int		cnt;
    garray_T	*gap;
    salitem_T	*smp;
    int		ccnt;
    char_u	*p;
    int		c = NUL;

    slang->sl_sofo = FALSE;

    i = getc(fd);				/* <salflags> */
    if (i & SAL_F0LLOWUP)
	slang->sl_followup = TRUE;
    if (i & SAL_COLLAPSE)
	slang->sl_collapse = TRUE;
    if (i & SAL_REM_ACCENTS)
	slang->sl_rem_accents = TRUE;

    cnt = get2c(fd);				/* <salcount> */
    if (cnt < 0)
	return SP_TRUNCERROR;

    gap = &slang->sl_sal;
    ga_init2(gap, sizeof(salitem_T), 10);
    if (ga_grow(gap, cnt + 1) == FAIL)
	return SP_OTHERERROR;

    /* <sal> : <salfromlen> <salfrom> <saltolen> <salto> */
    for (; gap->ga_len < cnt; ++gap->ga_len)
    {
	smp = &((salitem_T *)gap->ga_data)[gap->ga_len];
	ccnt = getc(fd);			/* <salfromlen> */
	if (ccnt < 0)
	    return SP_TRUNCERROR;
	if ((p = alloc(ccnt + 2)) == NULL)
	    return SP_OTHERERROR;
	smp->sm_lead = p;

	/* Read up to the first special char into sm_lead. */
	for (i = 0; i < ccnt; ++i)
	{
	    c = getc(fd);			/* <salfrom> */
	    if (vim_strchr((char_u *)"0123456789(-<^$", c) != NULL)
		break;
	    *p++ = c;
	}
	smp->sm_leadlen = (int)(p - smp->sm_lead);
	*p++ = NUL;

	/* Put (abc) chars in sm_oneof, if any. */
	if (c == '(')
	{
	    smp->sm_oneof = p;
	    for (++i; i < ccnt; ++i)
	    {
		c = getc(fd);			/* <salfrom> */
		if (c == ')')
		    break;
		*p++ = c;
	    }
	    *p++ = NUL;
	    if (++i < ccnt)
		c = getc(fd);
	}
	else
	    smp->sm_oneof = NULL;

	/* Any following chars go in sm_rules. */
	smp->sm_rules = p;
	if (i < ccnt)
	    /* store the char we got while checking for end of sm_lead */
	    *p++ = c;
	for (++i; i < ccnt; ++i)
	    *p++ = getc(fd);			/* <salfrom> */
	*p++ = NUL;

	/* <saltolen> <salto> */
	smp->sm_to = read_cnt_string(fd, 1, &ccnt);
	if (ccnt < 0)
	{
	    vim_free(smp->sm_lead);
	    return ccnt;
	}

#ifdef FEAT_MBYTE
	if (has_mbyte)
	{
	    /* convert the multi-byte strings to wide char strings */
	    smp->sm_lead_w = mb_str2wide(smp->sm_lead);
	    smp->sm_leadlen = mb_charlen(smp->sm_lead);
	    if (smp->sm_oneof == NULL)
		smp->sm_oneof_w = NULL;
	    else
		smp->sm_oneof_w = mb_str2wide(smp->sm_oneof);
	    if (smp->sm_to == NULL)
		smp->sm_to_w = NULL;
	    else
		smp->sm_to_w = mb_str2wide(smp->sm_to);
	    if (smp->sm_lead_w == NULL
		    || (smp->sm_oneof_w == NULL && smp->sm_oneof != NULL)
		    || (smp->sm_to_w == NULL && smp->sm_to != NULL))
	    {
		vim_free(smp->sm_lead);
		vim_free(smp->sm_to);
		vim_free(smp->sm_lead_w);
		vim_free(smp->sm_oneof_w);
		vim_free(smp->sm_to_w);
		return SP_OTHERERROR;
	    }
	}
#endif
    }

    if (gap->ga_len > 0)
    {
	/* Add one extra entry to mark the end with an empty sm_lead.  Avoids
	 * that we need to check the index every time. */
	smp = &((salitem_T *)gap->ga_data)[gap->ga_len];
	if ((p = alloc(1)) == NULL)
	    return SP_OTHERERROR;
	p[0] = NUL;
	smp->sm_lead = p;
	smp->sm_leadlen = 0;
	smp->sm_oneof = NULL;
	smp->sm_rules = p;
	smp->sm_to = NULL;
#ifdef FEAT_MBYTE
	if (has_mbyte)
	{
	    smp->sm_lead_w = mb_str2wide(smp->sm_lead);
	    smp->sm_leadlen = 0;
	    smp->sm_oneof_w = NULL;
	    smp->sm_to_w = NULL;
	}
#endif
	++gap->ga_len;
    }

    /* Fill the first-index table. */
    set_sal_first(slang);

    return 0;
}


mb_str2wide(char_u *s)
{
    int		*res;
    char_u	*p;
    int		i = 0;

    res = (int *)alloc(sizeof(int) * (mb_charlen(s) + 1));
    if (res != NULL)
    {
	for (p = s; *p != NUL; )
	    res[i++] = mb_ptr2char_adv(&p);
	res[i] = NUL;
    }
    return res;
}


spell_info_item(char_u *s)
{
    return STRCMP(s, "NAME") == 0
	|| STRCMP(s, "HOME") == 0
	|| STRCMP(s, "VERSION") == 0
	|| STRCMP(s, "AUTHOR") == 0
	|| STRCMP(s, "EMAIL") == 0
	|| STRCMP(s, "COPYRIGHT") == 0;
}


void ip_cmsg_recv_offset(struct msghdr *msg, struct sock *sk,
			 struct sk_buff *skb, int tlen, int offset)
{
	struct inet_sock *inet = inet_sk(sk);
	unsigned int flags = inet->cmsg_flags;

	/* Ordered by supposed usage frequency */
	if (flags & IP_CMSG_PKTINFO) {
		ip_cmsg_recv_pktinfo(msg, skb);

		flags &= ~IP_CMSG_PKTINFO;
		if (!flags)
			return;
	}

	if (flags & IP_CMSG_TTL) {
		ip_cmsg_recv_ttl(msg, skb);

		flags &= ~IP_CMSG_TTL;
		if (!flags)
			return;
	}

	if (flags & IP_CMSG_TOS) {
		ip_cmsg_recv_tos(msg, skb);

		flags &= ~IP_CMSG_TOS;
		if (!flags)
			return;
	}

	if (flags & IP_CMSG_RECVOPTS) {
		ip_cmsg_recv_opts(msg, skb);

		flags &= ~IP_CMSG_RECVOPTS;
		if (!flags)
			return;
	}

	if (flags & IP_CMSG_RETOPTS) {
		ip_cmsg_recv_retopts(msg, skb);

		flags &= ~IP_CMSG_RETOPTS;
		if (!flags)
			return;
	}

	if (flags & IP_CMSG_PASSSEC) {
		ip_cmsg_recv_security(msg, skb);

		flags &= ~IP_CMSG_PASSSEC;
		if (!flags)
			return;
	}

	if (flags & IP_CMSG_ORIGDSTADDR) {
		ip_cmsg_recv_dstaddr(msg, skb);

		flags &= ~IP_CMSG_ORIGDSTADDR;
		if (!flags)
			return;
	}

	if (flags & IP_CMSG_CHECKSUM)
		ip_cmsg_recv_checksum(msg, skb, tlen, offset);

	if (flags & IP_CMSG_RECVFRAGSIZE)
		ip_cmsg_recv_fragsize(msg, skb);
}


void *iova_to_vaddr(struct rxe_mem *mem, u64 iova, int length)
{
	size_t offset;
	int m, n;
	void *addr;

	if (mem->state != RXE_MEM_STATE_VALID) {
		pr_warn("mem not in valid state\n");
		addr = NULL;
		goto out;
	}

	if (!mem->map) {
		addr = (void *)(uintptr_t)iova;
		goto out;
	}

	if (mem_check_range(mem, iova, length)) {
		pr_warn("range violation\n");
		addr = NULL;
		goto out;
	}

	lookup_iova(mem, iova, &m, &n, &offset);

	if (offset + length > mem->map[m]->buf[n].size) {
		pr_warn("crosses page boundary\n");
		addr = NULL;
		goto out;
	}

	addr = (void *)(uintptr_t)mem->map[m]->buf[n].addr + offset;

out:
	return addr;
}


int rxe_mem_init_user(struct rxe_dev *rxe, struct rxe_pd *pd, u64 start,
		      u64 length, u64 iova, int access, struct ib_udata *udata,
		      struct rxe_mem *mem)
{
	int			entry;
	struct rxe_map		**map;
	struct rxe_phys_buf	*buf = NULL;
	struct ib_umem		*umem;
	struct scatterlist	*sg;
	int			num_buf;
	void			*vaddr;
	int err;

	umem = ib_umem_get(pd->ibpd.uobject->context, start, length, access, 0);
	if (IS_ERR(umem)) {
		pr_warn("err %d from rxe_umem_get\n",
			(int)PTR_ERR(umem));
		err = -EINVAL;
		goto err1;
	}

	mem->umem = umem;
	num_buf = umem->nmap;

	rxe_mem_init(access, mem);

	err = rxe_mem_alloc(rxe, mem, num_buf);
	if (err) {
		pr_warn("err %d from rxe_mem_alloc\n", err);
		ib_umem_release(umem);
		goto err1;
	}

	WARN_ON(!is_power_of_2(umem->page_size));

	mem->page_shift		= ilog2(umem->page_size);
	mem->page_mask		= umem->page_size - 1;

	num_buf			= 0;
	map			= mem->map;
	if (length > 0) {
		buf = map[0]->buf;

		for_each_sg(umem->sg_head.sgl, sg, umem->nmap, entry) {
			vaddr = page_address(sg_page(sg));
			if (!vaddr) {
				pr_warn("null vaddr\n");
				err = -ENOMEM;
				goto err1;
			}

			buf->addr = (uintptr_t)vaddr;
			buf->size = umem->page_size;
			num_buf++;
			buf++;

			if (num_buf >= RXE_BUF_PER_MAP) {
				map++;
				buf = map[0]->buf;
				num_buf = 0;
			}
		}
	}

	mem->pd			= pd;
	mem->umem		= umem;
	mem->access		= access;
	mem->length		= length;
	mem->iova		= iova;
	mem->va			= start;
	mem->offset		= ib_umem_offset(umem);
	mem->state		= RXE_MEM_STATE_VALID;
	mem->type		= RXE_MEM_TYPE_MR;

	return 0;

err1:
	return err;
}


static void test_date_frac()
{
  int        rc;

  myheader("test_date");

  rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_date");
  myquery(rc);

  rc= mysql_query(mysql, "CREATE TABLE test_date(c1 TIMESTAMP(6), \
                                                 c2 TIME(6), \
                                                 c3 DATETIME(6), \
                                                 c4 DATE)");

  myquery(rc);

  bind_date_conv(5,TRUE);
}


static void test_long_data_bin()
{
  MYSQL_STMT *stmt;
  int        rc;
  char       data[255];
  long       length;
  MYSQL_RES  *result;
  MYSQL_BIND my_bind[2];
  char query[MAX_TEST_QUERY_LENGTH];

  myheader("test_long_data_bin");

  rc= mysql_autocommit(mysql, TRUE);
  myquery(rc);

  rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_long_data_bin");
  myquery(rc);

  rc= mysql_query(mysql, "CREATE TABLE test_long_data_bin(id int, longbin long varbinary)");
  myquery(rc);

  strmov(query, "INSERT INTO test_long_data_bin VALUES(?, ?)");
  stmt= mysql_simple_prepare(mysql, query);
  check_stmt(stmt);

  verify_param_count(stmt, 2);

  /* Always memset all members of bind parameter */
  memset(my_bind, 0, sizeof(my_bind));

  my_bind[0].buffer= (void *)&length;
  my_bind[0].buffer_type= MYSQL_TYPE_LONG;
  length= 0;

  my_bind[1].buffer= data;           /* string data */
  my_bind[1].buffer_type= MYSQL_TYPE_LONG_BLOB;
  rc= mysql_stmt_bind_param(stmt, my_bind);
  check_execute(stmt, rc);

  length= 10;
  strmov(data, "MySQL AB");

  /* supply data in pieces */
  {
    int i;
    for (i= 0; i < 100; i++)
    {
      rc= mysql_stmt_send_long_data(stmt, 1, (char *)data, 4);
      check_execute(stmt, rc);
    }
  }
  /* execute */
  rc= mysql_stmt_execute(stmt);
  if (!opt_silent)
    fprintf(stdout, " mysql_stmt_execute() returned %d\n", rc);
  check_execute(stmt, rc);

  mysql_stmt_close(stmt);

  rc= mysql_commit(mysql);
  myquery(rc);

  /* now fetch the results ..*/
  rc= mysql_query(mysql, "SELECT LENGTH(longbin), longbin FROM test_long_data_bin");
  myquery(rc);

  /* get the result */
  result= mysql_store_result(mysql);
  mytest(result);

  rc= my_process_result_set(result);
  DIE_UNLESS(rc == 1);
  mysql_free_result(result);
}


MYSQL * STDCALL
mysql_init(MYSQL *mysql)
{
  if (mysql_server_init(0, NULL, NULL))
    return 0;
  if (!mysql)
  {
    if (!(mysql=(MYSQL*) my_malloc(sizeof(*mysql),MYF(MY_WME | MY_ZEROFILL))))
    {
      set_mysql_error(NULL, CR_OUT_OF_MEMORY, unknown_sqlstate);
      return 0;
    }
    mysql->free_me=1;
  }
  else
    memset(mysql, 0, sizeof(*(mysql)));
  mysql->charset=default_client_charset_info;
  strmov(mysql->net.sqlstate, not_error_sqlstate);

  /*
    Only enable LOAD DATA INFILE by default if configured with
    --enable-local-infile
  */

#if defined(ENABLED_LOCAL_INFILE) && !defined(MYSQL_SERVER)
  mysql->options.client_flag|= CLIENT_LOCAL_FILES;
#endif

#ifdef HAVE_SMEM
  mysql->options.shared_memory_base_name= (char*) def_shared_memory_base_name;
#endif

  mysql->options.methods_to_use= MYSQL_OPT_GUESS_CONNECTION;
  mysql->options.report_data_truncation= TRUE;  /* default */

  /*
    By default we don't reconnect because it could silently corrupt data (after
    reconnection you potentially lose table locks, user variables, session
    variables (transactions but they are specifically dealt with in
    mysql_reconnect()).
    This is a change: < 5.0.3 mysql->reconnect was set to 1 by default.
    How this change impacts existing apps:
    - existing apps which relyed on the default will see a behaviour change;
    they will have to set reconnect=1 after mysql_real_connect().
    - existing apps which explicitely asked for reconnection (the only way they
    could do it was by setting mysql.reconnect to 1 after mysql_real_connect())
    will not see a behaviour change.
    - existing apps which explicitely asked for no reconnection
    (mysql.reconnect=0) will not see a behaviour change.
  */
  mysql->reconnect= 0;

  mysql->options.secure_auth= TRUE;

  return mysql;


static void test_bug17512527()
{
  MYSQL *conn1, *conn2;
  MYSQL_STMT *stmt1, *stmt2;
  const char *stmt1_txt= "SELECT NOW();";
  const char *stmt2_txt= "SELECT 1;";
  unsigned long thread_id;
  char query[MAX_TEST_QUERY_LENGTH];
  int rc;

  conn1= client_connect(0, MYSQL_PROTOCOL_DEFAULT, 1);
  conn2= client_connect(0, MYSQL_PROTOCOL_DEFAULT, 0);

  stmt1 = mysql_stmt_init(conn1);
  check_stmt(stmt1);
  rc= mysql_stmt_prepare(stmt1, stmt1_txt, strlen(stmt1_txt));
  check_execute(stmt1, rc);

  thread_id= mysql_thread_id(conn1);
  sprintf(query, "KILL %lu", thread_id);
  if (thread_query(query))
    exit(1);

  /*
    After the connection is killed, the connection is
    re-established due to the reconnect flag.
  */
  stmt2 = mysql_stmt_init(conn1);
  check_stmt(stmt2);

  rc= mysql_stmt_prepare(stmt2, stmt2_txt, strlen(stmt2_txt));
  check_execute(stmt1, rc);

  mysql_stmt_close(stmt2);
  mysql_stmt_close(stmt1);

  mysql_close(conn1);
  mysql_close(conn2);
}


static void test_datetime_ranges()
{
  const char *stmt_text;
  int rc, i;
  MYSQL_STMT *stmt;
  MYSQL_BIND my_bind[6];
  MYSQL_TIME tm[6];

  myheader("test_datetime_ranges");

  stmt_text= "drop table if exists t1";
  rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
  myquery(rc);

  stmt_text= "create table t1 (year datetime, month datetime, day datetime, "
                              "hour datetime, min datetime, sec datetime)";
  rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
  myquery(rc);

  stmt= mysql_simple_prepare(mysql,
                             "INSERT INTO t1 VALUES (?, ?, ?, ?, ?, ?)");
  check_stmt(stmt);
  verify_param_count(stmt, 6);

  memset(my_bind, 0, sizeof(my_bind));
  for (i= 0; i < 6; i++)
  {
    my_bind[i].buffer_type= MYSQL_TYPE_DATETIME;
    my_bind[i].buffer= &tm[i];
  }
  rc= mysql_stmt_bind_param(stmt, my_bind);
  check_execute(stmt, rc);

  tm[0].year= 2004; tm[0].month= 11; tm[0].day= 10;
  tm[0].hour= 12; tm[0].minute= 30; tm[0].second= 30;
  tm[0].second_part= 0; tm[0].neg= 0;

  tm[5]= tm[4]= tm[3]= tm[2]= tm[1]= tm[0];
  tm[0].year= 10000;  tm[1].month= 13; tm[2].day= 32;
  tm[3].hour= 24; tm[4].minute= 60; tm[5].second= 60;

  rc= mysql_stmt_execute(stmt);
  check_execute(stmt, rc);
  my_process_warnings(mysql, 12);

  verify_col_data("t1", "year", "0000-00-00 00:00:00");
  verify_col_data("t1", "month", "0000-00-00 00:00:00");
  verify_col_data("t1", "day", "0000-00-00 00:00:00");
  verify_col_data("t1", "hour", "0000-00-00 00:00:00");
  verify_col_data("t1", "min", "0000-00-00 00:00:00");
  verify_col_data("t1", "sec", "0000-00-00 00:00:00");

  mysql_stmt_close(stmt);

  stmt_text= "delete from t1";
  rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
  myquery(rc);

  stmt= mysql_simple_prepare(mysql, "INSERT INTO t1 (year, month, day) "
                                    "VALUES (?, ?, ?)");
  check_stmt(stmt);
  verify_param_count(stmt, 3);

  /*
    We reuse contents of bind and tm arrays left from previous part of test.
  */
  for (i= 0; i < 3; i++)
    my_bind[i].buffer_type= MYSQL_TYPE_DATE;

  rc= mysql_stmt_bind_param(stmt, my_bind);
  check_execute(stmt, rc);

  rc= mysql_stmt_execute(stmt);
  check_execute(stmt, rc);
  my_process_warnings(mysql, 6);

  verify_col_data("t1", "year", "0000-00-00 00:00:00");
  verify_col_data("t1", "month", "0000-00-00 00:00:00");
  verify_col_data("t1", "day", "0000-00-00 00:00:00");

  mysql_stmt_close(stmt);

  stmt_text= "drop table t1";
  rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
  myquery(rc);

  stmt_text= "create table t1 (day_ovfl time, day time, hour time, min time, sec time)";
  rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
  myquery(rc);

  stmt= mysql_simple_prepare(mysql,
                             "INSERT INTO t1 VALUES (?, ?, ?, ?, ?)");
  check_stmt(stmt);
  verify_param_count(stmt, 5);

  /*
    Again we reuse what we can from previous part of test.
  */
  for (i= 0; i < 5; i++)
    my_bind[i].buffer_type= MYSQL_TYPE_TIME;

  rc= mysql_stmt_bind_param(stmt, my_bind);
  check_execute(stmt, rc);

  tm[0].year= 0; tm[0].month= 0; tm[0].day= 10;
  tm[0].hour= 12; tm[0].minute= 30; tm[0].second= 30;
  tm[0].second_part= 0; tm[0].neg= 0;

  tm[4]= tm[3]= tm[2]= tm[1]= tm[0];
  tm[0].day= 35; tm[1].day= 34; tm[2].hour= 30; tm[3].minute= 60; tm[4].second= 60;

  rc= mysql_stmt_execute(stmt);
  check_execute(stmt, rc);
  my_process_warnings(mysql, 2);

  verify_col_data("t1", "day_ovfl", "838:59:59");
  verify_col_data("t1", "day", "828:30:30");
  verify_col_data("t1", "hour", "270:30:30");
  verify_col_data("t1", "min", "00:00:00");
  verify_col_data("t1", "sec", "00:00:00");

  mysql_stmt_close(stmt);

  stmt_text= "drop table t1";
  rc= mysql_real_query(mysql, stmt_text, strlen(stmt_text));
  myquery(rc);
}


static void test_update()
{
  MYSQL_STMT *stmt;
  int        rc;
  char       szData[25];
  int        nData= 1;
  MYSQL_RES  *result;
  MYSQL_BIND my_bind[2];
  ulong length[2];
  char query[MAX_TEST_QUERY_LENGTH];

  myheader("test_update");

  rc= mysql_autocommit(mysql, TRUE);
  myquery(rc);

  rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_update");
  myquery(rc);

  rc= mysql_query(mysql, "CREATE TABLE test_update("
                               "col1 int primary key auto_increment, "
                               "col2 varchar(50), col3 int )");
  myquery(rc);

  strmov(query, "INSERT INTO test_update(col2, col3) VALUES(?, ?)");
  stmt= mysql_simple_prepare(mysql, query);
  check_stmt(stmt);

  verify_param_count(stmt, 2);

  /* Always memset all members of bind parameter */
  memset(my_bind, 0, sizeof(my_bind));

  /* string data */
  my_bind[0].buffer_type= MYSQL_TYPE_STRING;
  my_bind[0].buffer= szData;
  my_bind[0].buffer_length= sizeof(szData);
  my_bind[0].length= &length[0];
  length[0]= sprintf(szData, "inserted-data");

  my_bind[1].buffer= (void *)&nData;
  my_bind[1].buffer_type= MYSQL_TYPE_LONG;

  rc= mysql_stmt_bind_param(stmt, my_bind);
  check_execute(stmt, rc);

  nData= 100;
  rc= mysql_stmt_execute(stmt);
  check_execute(stmt, rc);

  verify_affected_rows(1);
  mysql_stmt_close(stmt);

  strmov(query, "UPDATE test_update SET col2= ? WHERE col3= ?");
  stmt= mysql_simple_prepare(mysql, query);
  check_stmt(stmt);

  verify_param_count(stmt, 2);
  nData= 100;

  /* Always memset all members of bind parameter */
  memset(my_bind, 0, sizeof(my_bind));

  my_bind[0].buffer_type= MYSQL_TYPE_STRING;
  my_bind[0].buffer= szData;
  my_bind[0].buffer_length= sizeof(szData);
  my_bind[0].length= &length[0];
  length[0]= sprintf(szData, "updated-data");

  my_bind[1].buffer= (void *)&nData;
  my_bind[1].buffer_type= MYSQL_TYPE_LONG;

  rc= mysql_stmt_bind_param(stmt, my_bind);
  check_execute(stmt, rc);

  rc= mysql_stmt_execute(stmt);
  check_execute(stmt, rc);
  verify_affected_rows(1);

  mysql_stmt_close(stmt);

  /* now fetch the results ..*/
  rc= mysql_commit(mysql);
  myquery(rc);

  /* test the results now, only one row should exist */
  rc= mysql_query(mysql, "SELECT * FROM test_update");
  myquery(rc);

  /* get the result */
  result= mysql_store_result(mysql);
  mytest(result);

  rc= my_process_result_set(result);
  DIE_UNLESS(rc == 1);
  mysql_free_result(result);
}


int STDCALL
mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg)
{
  DBUG_ENTER("mysql_option");
  DBUG_PRINT("enter",("option: %d",(int) option));
  switch (option) {
  case MYSQL_OPT_CONNECT_TIMEOUT:
    mysql->options.connect_timeout= *(uint*) arg;
    break;
  case MYSQL_OPT_READ_TIMEOUT:
    mysql->options.read_timeout= *(uint*) arg;
    break;
  case MYSQL_OPT_WRITE_TIMEOUT:
    mysql->options.write_timeout= *(uint*) arg;
    break;
  case MYSQL_OPT_COMPRESS:
    mysql->options.compress= 1;			/* Remember for connect */
    mysql->options.client_flag|= CLIENT_COMPRESS;
    break;
  case MYSQL_OPT_NAMED_PIPE:			/* This option is depricated */
    mysql->options.protocol=MYSQL_PROTOCOL_PIPE; /* Force named pipe */
    break;
  case MYSQL_OPT_LOCAL_INFILE:			/* Allow LOAD DATA LOCAL ?*/
    if (!arg || MY_TEST(*(uint*) arg))
      mysql->options.client_flag|= CLIENT_LOCAL_FILES;
    else
      mysql->options.client_flag&= ~CLIENT_LOCAL_FILES;
    break;
  case MYSQL_INIT_COMMAND:
    add_init_command(&mysql->options,arg);
    break;
  case MYSQL_READ_DEFAULT_FILE:
    my_free(mysql->options.my_cnf_file);
    mysql->options.my_cnf_file=my_strdup(arg,MYF(MY_WME));
    break;
  case MYSQL_READ_DEFAULT_GROUP:
    my_free(mysql->options.my_cnf_group);
    mysql->options.my_cnf_group=my_strdup(arg,MYF(MY_WME));
    break;
  case MYSQL_SET_CHARSET_DIR:
    my_free(mysql->options.charset_dir);
    mysql->options.charset_dir=my_strdup(arg,MYF(MY_WME));
    break;
  case MYSQL_SET_CHARSET_NAME:
    my_free(mysql->options.charset_name);
    mysql->options.charset_name=my_strdup(arg,MYF(MY_WME));
    break;
  case MYSQL_OPT_PROTOCOL:
    mysql->options.protocol= *(uint*) arg;
    break;
  case MYSQL_SHARED_MEMORY_BASE_NAME:
#ifdef HAVE_SMEM
    if (mysql->options.shared_memory_base_name != def_shared_memory_base_name)
      my_free(mysql->options.shared_memory_base_name);
    mysql->options.shared_memory_base_name=my_strdup(arg,MYF(MY_WME));
#endif
    break;
  case MYSQL_OPT_USE_REMOTE_CONNECTION:
  case MYSQL_OPT_USE_EMBEDDED_CONNECTION:
  case MYSQL_OPT_GUESS_CONNECTION:
    mysql->options.methods_to_use= option;
    break;
  case MYSQL_SET_CLIENT_IP:
    my_free(mysql->options.ci.client_ip);
    mysql->options.ci.client_ip= my_strdup(arg, MYF(MY_WME));
    break;
  case MYSQL_SECURE_AUTH:
    mysql->options.secure_auth= *(my_bool *) arg;
    break;
  case MYSQL_REPORT_DATA_TRUNCATION:
    mysql->options.report_data_truncation= MY_TEST(*(my_bool *) arg);
    break;
  case MYSQL_OPT_RECONNECT:
    mysql->reconnect= *(my_bool *) arg;
    break;
  case MYSQL_OPT_BIND:
    my_free(mysql->options.ci.bind_address);
    mysql->options.ci.bind_address= my_strdup(arg, MYF(MY_WME));
    break;
  case MYSQL_OPT_SSL_VERIFY_SERVER_CERT:
    if (*(my_bool*) arg)
      mysql->options.client_flag|= CLIENT_SSL_VERIFY_SERVER_CERT;
    else
      mysql->options.client_flag&= ~CLIENT_SSL_VERIFY_SERVER_CERT;
    break;
  case MYSQL_PLUGIN_DIR:
    EXTENSION_SET_STRING(&mysql->options, plugin_dir, arg);
    break;
  case MYSQL_DEFAULT_AUTH:
    EXTENSION_SET_STRING(&mysql->options, default_auth, arg);
    break;
  case MYSQL_OPT_SSL_KEY:      SET_SSL_OPTION(ssl_key, arg);     break;
  case MYSQL_OPT_SSL_CERT:     SET_SSL_OPTION(ssl_cert, arg);    break;
  case MYSQL_OPT_SSL_CA:       SET_SSL_OPTION(ssl_ca, arg);      break;
  case MYSQL_OPT_SSL_CAPATH:   SET_SSL_OPTION(ssl_capath, arg);  break;
  case MYSQL_OPT_SSL_CIPHER:   SET_SSL_OPTION(ssl_cipher, arg);  break;
  case MYSQL_OPT_SSL_CRL:      EXTENSION_SET_SSL_STRING(&mysql->options,
                                                        ssl_crl, arg);
                               break;
  case MYSQL_OPT_SSL_CRLPATH:  EXTENSION_SET_SSL_STRING(&mysql->options,
                                                        ssl_crlpath, arg);
                               break;
  case MYSQL_SERVER_PUBLIC_KEY:
    EXTENSION_SET_STRING(&mysql->options, server_public_key_path, arg);
    break;

  case MYSQL_OPT_CONNECT_ATTR_RESET:
    ENSURE_EXTENSIONS_PRESENT(&mysql->options);
    if (my_hash_inited(&mysql->options.extension->connection_attributes))
    {
      my_hash_free(&mysql->options.extension->connection_attributes);
      mysql->options.extension->connection_attributes_length= 0;
    }
    break;
  case MYSQL_OPT_CONNECT_ATTR_DELETE:
    ENSURE_EXTENSIONS_PRESENT(&mysql->options);
    if (my_hash_inited(&mysql->options.extension->connection_attributes))
    {
      size_t len;
      uchar *elt;

      len= arg ? strlen(arg) : 0;

      if (len)
      {
        elt= my_hash_search(&mysql->options.extension->connection_attributes,
                            arg, len);
        if (elt)
        {
          LEX_STRING *attr= (LEX_STRING *) elt;
          LEX_STRING *key= attr, *value= attr + 1;

          mysql->options.extension->connection_attributes_length-=
            get_length_store_length(key->length) + key->length +
            get_length_store_length(value->length) + value->length;

          my_hash_delete(&mysql->options.extension->connection_attributes,
                         elt);

        }
      }
    }
    break;
  case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
    ENSURE_EXTENSIONS_PRESENT(&mysql->options);
    mysql->options.extension->enable_cleartext_plugin=
      (*(my_bool*) arg) ? TRUE : FALSE;
    break;
  case MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS:
    if (*(my_bool*) arg)
      mysql->options.client_flag|= CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
    else
      mysql->options.client_flag&= ~CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS;
    break;

  default:
    DBUG_RETURN(1);
  }
  DBUG_RETURN(0);


static void test_simple_update()
{
  MYSQL_STMT *stmt;
  int        rc;
  char       szData[25];
  int        nData= 1;
  MYSQL_RES  *result;
  MYSQL_BIND my_bind[2];
  ulong      length[2];
  char query[MAX_TEST_QUERY_LENGTH];

  myheader("test_simple_update");

  rc= mysql_autocommit(mysql, TRUE);
  myquery(rc);

  rc= mysql_query(mysql, "DROP TABLE IF EXISTS test_update");
  myquery(rc);

  rc= mysql_query(mysql, "CREATE TABLE test_update(col1 int, "
                         " col2 varchar(50), col3 int )");
  myquery(rc);

  rc= mysql_query(mysql, "INSERT INTO test_update VALUES(1, 'MySQL', 100)");
  myquery(rc);

  verify_affected_rows(1);

  rc= mysql_commit(mysql);
  myquery(rc);

  /* insert by prepare */
  strmov(query, "UPDATE test_update SET col2= ? WHERE col1= ?");
  stmt= mysql_simple_prepare(mysql, query);
  check_stmt(stmt);

  verify_param_count(stmt, 2);

  /* Always memset all members of bind parameter */
  memset(my_bind, 0, sizeof(my_bind));

  nData= 1;
  my_bind[0].buffer_type= MYSQL_TYPE_STRING;
  my_bind[0].buffer= szData;                /* string data */
  my_bind[0].buffer_length= sizeof(szData);
  my_bind[0].length= &length[0];
  length[0]= sprintf(szData, "updated-data");

  my_bind[1].buffer= (void *) &nData;
  my_bind[1].buffer_type= MYSQL_TYPE_LONG;

  rc= mysql_stmt_bind_param(stmt, my_bind);
  check_execute(stmt, rc);

  rc= mysql_stmt_execute(stmt);
  check_execute(stmt, rc);
  verify_affected_rows(1);

  mysql_stmt_close(stmt);

  /* now fetch the results ..*/
  rc= mysql_commit(mysql);
  myquery(rc);

  /* test the results now, only one row should exist */
  rc= mysql_query(mysql, "SELECT * FROM test_update");
  myquery(rc);

  /* get the result */
  result= mysql_store_result(mysql);
  mytest(result);

  rc= my_process_result_set(result);
  DIE_UNLESS(rc == 1);
  mysql_free_result(result);
}


static void test_mem_overun()
{
  char       buffer[10000], field[10];
  MYSQL_STMT *stmt;
  MYSQL_RES  *field_res;
  int        rc, i, length;

  myheader("test_mem_overun");

  /*
    Test a memory ovverun bug when a table had 1000 fields with
    a row of data
  */
  rc= mysql_query(mysql, "drop table if exists t_mem_overun");
  myquery(rc);

  strxmov(buffer, "create table t_mem_overun(", NullS);
  for (i= 0; i < 1000; i++)
  {
    sprintf(field, "c%d int", i);
    strxmov(buffer, buffer, field, ", ", NullS);
  }
  length= strlen(buffer);
  buffer[length-2]= ')';
  buffer[--length]= '\0';

  rc= mysql_real_query(mysql, buffer, length);
  myquery(rc);

  strxmov(buffer, "insert into t_mem_overun values(", NullS);
  for (i= 0; i < 1000; i++)
  {
    strxmov(buffer, buffer, "1, ", NullS);
  }
  length= strlen(buffer);
  buffer[length-2]= ')';
  buffer[--length]= '\0';

  rc= mysql_real_query(mysql, buffer, length);
  myquery(rc);

  rc= mysql_query(mysql, "select * from t_mem_overun");
  myquery(rc);

  rc= my_process_result(mysql);
  DIE_UNLESS(rc == 1);

  stmt= mysql_simple_prepare(mysql, "select * from t_mem_overun");
  check_stmt(stmt);

  rc= mysql_stmt_execute(stmt);
  check_execute(stmt, rc);

  field_res= mysql_stmt_result_metadata(stmt);
  mytest(field_res);

  if (!opt_silent)
    fprintf(stdout, "\n total fields : %d", mysql_num_fields(field_res));
  DIE_UNLESS( 1000 == mysql_num_fields(field_res));

  rc= mysql_stmt_store_result(stmt);
  check_execute(stmt, rc);

  rc= mysql_stmt_fetch(stmt);
  check_execute(stmt, rc);

  rc= mysql_stmt_fetch(stmt);
  DIE_UNLESS(rc == MYSQL_NO_DATA);

  mysql_free_result(field_res);

  mysql_stmt_close(stmt);
}


static void *timer_list_next(struct seq_file *file, void *v, loff_t *offset)
{
	struct timer_list_iter *iter = file->private;
	++*offset;
	return move_iter(iter, 1);
}


unsigned long round_jiffies_up(unsigned long j)
{
	return round_jiffies_common(j, raw_smp_processor_id(), true);
}


static inline void forward_timer_base(struct timer_base *base)
{
	unsigned long jnow = READ_ONCE(jiffies);

	/*
	 * We only forward the base when it's idle and we have a delta between
	 * base clock and jiffies.
	 */
	if (!base->is_idle || (long) (jnow - base->clk) < 2)
		return;

	/*
	 * If the next expiry value is > jiffies, then we fast forward to
	 * jiffies otherwise we forward to the next expiry value.
	 */
	if (time_after(base->next_expiry, jnow))
		base->clk = jnow;
	else
		base->clk = base->next_expiry;
}


static ssize_t wq_nice_show(struct device *dev, struct device_attribute *attr,
			    char *buf)
{
	struct workqueue_struct *wq = dev_to_wq(dev);
	int written;

	mutex_lock(&wq->mutex);
	written = scnprintf(buf, PAGE_SIZE, "%d\n", wq->unbound_attrs->nice);
	mutex_unlock(&wq->mutex);

	return written;
}


static void timer_list_stop(struct seq_file *seq, void *v)
{
}


bool kthread_queue_work(struct kthread_worker *worker,
			struct kthread_work *work)
{
	bool ret = false;
	unsigned long flags;

	spin_lock_irqsave(&worker->lock, flags);
	if (!queuing_blocked(worker, work)) {
		kthread_insert_work(worker, work, &worker->work_list);
		ret = true;
	}
	spin_unlock_irqrestore(&worker->lock, flags);
	return ret;
}


void workqueue_set_max_active(struct workqueue_struct *wq, int max_active)
{
	struct pool_workqueue *pwq;

	/* disallow meddling with max_active for ordered workqueues */
	if (WARN_ON(wq->flags & __WQ_ORDERED))
		return;

	max_active = wq_clamp_max_active(max_active, wq->flags, wq->name);

	mutex_lock(&wq->mutex);

	wq->saved_max_active = max_active;

	for_each_pwq(pwq, wq)
		pwq_adjust_max_active(pwq);

	mutex_unlock(&wq->mutex);
}


void unlock_hrtimer_base(const struct hrtimer *timer, unsigned long *flags)
{
	raw_spin_unlock_irqrestore(&timer->base->cpu_base->lock, *flags);
}


struct hrtimer_cpu_base *get_target_base(struct hrtimer_cpu_base *base,
					 int pinned)
{
	if (pinned || !base->migration_enabled)
		return base;
	return &per_cpu(hrtimer_bases, get_nohz_timer_target());
}


int mod_timer(struct timer_list *timer, unsigned long expires)
{
	return __mod_timer(timer, expires, false);
}


static void idle_worker_timeout(unsigned long __pool)
{
	struct worker_pool *pool = (void *)__pool;

	spin_lock_irq(&pool->lock);

	while (too_many_workers(pool)) {
		struct worker *worker;
		unsigned long expires;

		/* idle_list is kept in LIFO order, check the last one */
		worker = list_entry(pool->idle_list.prev, struct worker, entry);
		expires = worker->last_active + IDLE_WORKER_TIMEOUT;

		if (time_before(jiffies, expires)) {
			mod_timer(&pool->idle_timer, expires);
			break;
		}

		destroy_worker(worker);
	}

	spin_unlock_irq(&pool->lock);
}


static int work_next_color(int color)
{
	return (color + 1) % WORK_NR_COLORS;
}


void print_worker_info(const char *log_lvl, struct task_struct *task)
{
	work_func_t *fn = NULL;
	char name[WQ_NAME_LEN] = { };
	char desc[WORKER_DESC_LEN] = { };
	struct pool_workqueue *pwq = NULL;
	struct workqueue_struct *wq = NULL;
	bool desc_valid = false;
	struct worker *worker;

	if (!(task->flags & PF_WQ_WORKER))
		return;

	/*
	 * This function is called without any synchronization and @task
	 * could be in any state.  Be careful with dereferences.
	 */
	worker = kthread_probe_data(task);

	/*
	 * Carefully copy the associated workqueue's workfn and name.  Keep
	 * the original last '\0' in case the original contains garbage.
	 */
	probe_kernel_read(&fn, &worker->current_func, sizeof(fn));
	probe_kernel_read(&pwq, &worker->current_pwq, sizeof(pwq));
	probe_kernel_read(&wq, &pwq->wq, sizeof(wq));
	probe_kernel_read(name, wq->name, sizeof(name) - 1);

	/* copy worker description */
	probe_kernel_read(&desc_valid, &worker->desc_valid, sizeof(desc_valid));
	if (desc_valid)
		probe_kernel_read(desc, worker->desc, sizeof(desc) - 1);

	if (fn || name[0] || desc[0]) {
		printk("%sWorkqueue: %s %pf", log_lvl, name, fn);
		if (desc[0])
			pr_cont(" (%s)", desc);
		pr_cont("\n");
	}
}


bool workqueue_congested(int cpu, struct workqueue_struct *wq)
{
	struct pool_workqueue *pwq;
	bool ret;

	rcu_read_lock_sched();

	if (cpu == WORK_CPU_UNBOUND)
		cpu = smp_processor_id();

	if (!(wq->flags & WQ_UNBOUND))
		pwq = per_cpu_ptr(wq->cpu_pwqs, cpu);
	else
		pwq = unbound_pwq_by_node(wq, cpu_to_node(cpu));

	ret = !list_empty(&pwq->delayed_works);
	rcu_read_unlock_sched();

	return ret;
}


static bool manage_workers(struct worker *worker)
{
	struct worker_pool *pool = worker->pool;

	/*
	 * Anyone who successfully grabs manager_arb wins the arbitration
	 * and becomes the manager.  mutex_trylock() on pool->manager_arb
	 * failure while holding pool->lock reliably indicates that someone
	 * else is managing the pool and the worker which failed trylock
	 * can proceed to executing work items.  This means that anyone
	 * grabbing manager_arb is responsible for actually performing
	 * manager duties.  If manager_arb is grabbed and released without
	 * actual management, the pool may stall indefinitely.
	 */
	if (!mutex_trylock(&pool->manager_arb))
		return false;
	pool->manager = worker;

	maybe_create_worker(pool);

	pool->manager = NULL;
	mutex_unlock(&pool->manager_arb);
	return true;
}


lock_hrtimer_base(const struct hrtimer *timer, unsigned long *flags)
{
	struct hrtimer_clock_base *base = timer->base;

	raw_spin_lock_irqsave(&base->cpu_base->lock, *flags);

	return base;
}


void freeze_workqueues_begin(void)
{
	struct workqueue_struct *wq;
	struct pool_workqueue *pwq;

	mutex_lock(&wq_pool_mutex);

	WARN_ON_ONCE(workqueue_freezing);
	workqueue_freezing = true;

	list_for_each_entry(wq, &workqueues, list) {
		mutex_lock(&wq->mutex);
		for_each_pwq(pwq, wq)
			pwq_adjust_max_active(pwq);
		mutex_unlock(&wq->mutex);
	}

	mutex_unlock(&wq_pool_mutex);
}


static struct worker_pool *get_work_pool(struct work_struct *work)
{
	unsigned long data = atomic_long_read(&work->data);
	int pool_id;

	assert_rcu_or_pool_mutex();

	if (data & WORK_STRUCT_PWQ)
		return ((struct pool_workqueue *)
			(data & WORK_STRUCT_WQ_DATA_MASK))->pool;

	pool_id = data >> WORK_OFFQ_POOL_SHIFT;
	if (pool_id == WORK_OFFQ_POOL_NONE)
		return NULL;

	return idr_find(&worker_pool_idr, pool_id);
}


static bool work_is_canceling(struct work_struct *work)
{
	unsigned long data = atomic_long_read(&work->data);

	return !(data & WORK_STRUCT_PWQ) && (data & WORK_OFFQ_CANCELING);
}


int execute_in_process_context(work_func_t fn, struct execute_work *ew)
{
	if (!in_interrupt()) {
		fn(&ew->work);
		return 0;
	}

	INIT_WORK(&ew->work, fn);
	schedule_work(&ew->work);

	return 1;
}


int try_to_del_timer_sync(struct timer_list *timer)
{
	struct timer_base *base;
	unsigned long flags;
	int ret = -1;

	debug_assert_init(timer);

	base = lock_timer_base(timer, &flags);

	if (base->running_timer != timer)
		ret = detach_if_pending(timer, base, true);

	spin_unlock_irqrestore(&base->lock, flags);

	return ret;
}


internal_add_timer(struct timer_base *base, struct timer_list *timer)
{
	__internal_add_timer(base, timer);
	trigger_dyntick_cpu(base, timer);
}