static void bnx2x_set_modes_bitmap(struct bnx2x *bp)
{
	u32 flags = 0;

	if (CHIP_REV_IS_FPGA(bp))
		SET_FLAGS(flags, MODE_FPGA);
	else if (CHIP_REV_IS_EMUL(bp))
		SET_FLAGS(flags, MODE_EMUL);
	else
		SET_FLAGS(flags, MODE_ASIC);

	if (CHIP_MODE_IS_4_PORT(bp))
		SET_FLAGS(flags, MODE_PORT4);
	else
		SET_FLAGS(flags, MODE_PORT2);

	if (CHIP_IS_E2(bp))
		SET_FLAGS(flags, MODE_E2);
	else if (CHIP_IS_E3(bp)) {
		SET_FLAGS(flags, MODE_E3);
		if (CHIP_REV(bp) == CHIP_REV_Ax)
			SET_FLAGS(flags, MODE_E3_A0);
		else /*if (CHIP_REV(bp) == CHIP_REV_Bx)*/
			SET_FLAGS(flags, MODE_E3_B0 | MODE_COS3);
	}

	if (IS_MF(bp)) {
		SET_FLAGS(flags, MODE_MF);
		switch (bp->mf_mode) {
		case MULTI_FUNCTION_SD:
			SET_FLAGS(flags, MODE_MF_SD);
			break;
		case MULTI_FUNCTION_SI:
			SET_FLAGS(flags, MODE_MF_SI);
			break;
		case MULTI_FUNCTION_AFEX:
			SET_FLAGS(flags, MODE_MF_AFEX);
			break;
		}
	} else
		SET_FLAGS(flags, MODE_SF);

#if defined(__LITTLE_ENDIAN)
	SET_FLAGS(flags, MODE_LITTLE_ENDIAN);
#else /*(__BIG_ENDIAN)*/
	SET_FLAGS(flags, MODE_BIG_ENDIAN);
#endif
	INIT_MODE_FLAGS(bp) = flags;
}


static void bnx2x_dp_dmae(struct bnx2x *bp,
			  struct dmae_command *dmae, int msglvl)
{
	u32 src_type = dmae->opcode & DMAE_COMMAND_SRC;
	int i;

	switch (dmae->opcode & DMAE_COMMAND_DST) {
	case DMAE_CMD_DST_PCI:
		if (src_type == DMAE_CMD_SRC_PCI)
			DP(msglvl, "DMAE: opcode 0x%08x\n"
			   "src [%x:%08x], len [%d*4], dst [%x:%08x]\n"
			   "comp_addr [%x:%08x], comp_val 0x%08x\n",
			   dmae->opcode, dmae->src_addr_hi, dmae->src_addr_lo,
			   dmae->len, dmae->dst_addr_hi, dmae->dst_addr_lo,
			   dmae->comp_addr_hi, dmae->comp_addr_lo,
			   dmae->comp_val);
		else
			DP(msglvl, "DMAE: opcode 0x%08x\n"
			   "src [%08x], len [%d*4], dst [%x:%08x]\n"
			   "comp_addr [%x:%08x], comp_val 0x%08x\n",
			   dmae->opcode, dmae->src_addr_lo >> 2,
			   dmae->len, dmae->dst_addr_hi, dmae->dst_addr_lo,
			   dmae->comp_addr_hi, dmae->comp_addr_lo,
			   dmae->comp_val);
		break;
	case DMAE_CMD_DST_GRC:
		if (src_type == DMAE_CMD_SRC_PCI)
			DP(msglvl, "DMAE: opcode 0x%08x\n"
			   "src [%x:%08x], len [%d*4], dst_addr [%08x]\n"
			   "comp_addr [%x:%08x], comp_val 0x%08x\n",
			   dmae->opcode, dmae->src_addr_hi, dmae->src_addr_lo,
			   dmae->len, dmae->dst_addr_lo >> 2,
			   dmae->comp_addr_hi, dmae->comp_addr_lo,
			   dmae->comp_val);
		else
			DP(msglvl, "DMAE: opcode 0x%08x\n"
			   "src [%08x], len [%d*4], dst [%08x]\n"
			   "comp_addr [%x:%08x], comp_val 0x%08x\n",
			   dmae->opcode, dmae->src_addr_lo >> 2,
			   dmae->len, dmae->dst_addr_lo >> 2,
			   dmae->comp_addr_hi, dmae->comp_addr_lo,
			   dmae->comp_val);
		break;
	default:
		if (src_type == DMAE_CMD_SRC_PCI)
			DP(msglvl, "DMAE: opcode 0x%08x\n"
			   "src_addr [%x:%08x]  len [%d * 4]  dst_addr [none]\n"
			   "comp_addr [%x:%08x]  comp_val 0x%08x\n",
			   dmae->opcode, dmae->src_addr_hi, dmae->src_addr_lo,
			   dmae->len, dmae->comp_addr_hi, dmae->comp_addr_lo,
			   dmae->comp_val);
		else
			DP(msglvl, "DMAE: opcode 0x%08x\n"
			   "src_addr [%08x]  len [%d * 4]  dst_addr [none]\n"
			   "comp_addr [%x:%08x]  comp_val 0x%08x\n",
			   dmae->opcode, dmae->src_addr_lo >> 2,
			   dmae->len, dmae->comp_addr_hi, dmae->comp_addr_lo,
			   dmae->comp_val);
		break;
	}

	for (i = 0; i < (sizeof(struct dmae_command)/4); i++)
		DP(msglvl, "DMAE RAW [%02d]: 0x%08x\n",
		   i, *(((u32 *)dmae) + i));
}


static void bnx2x_period_task(struct work_struct *work)
{
	struct bnx2x *bp = container_of(work, struct bnx2x, period_task.work);

	if (!netif_running(bp->dev))
		goto period_task_exit;

	if (CHIP_REV_IS_SLOW(bp)) {
		BNX2X_ERR("period task called on emulation, ignoring\n");
		goto period_task_exit;
	}

	bnx2x_acquire_phy_lock(bp);
	/*
	 * The barrier is needed to ensure the ordering between the writing to
	 * the bp->port.pmf in the bnx2x_nic_load() or bnx2x_pmf_update() and
	 * the reading here.
	 */
	smp_mb();
	if (bp->port.pmf) {
		bnx2x_period_func(&bp->link_params, &bp->link_vars);

		/* Re-queue task in 1 sec */
		queue_delayed_work(bnx2x_wq, &bp->period_task, 1*HZ);
	}

	bnx2x_release_phy_lock(bp);
period_task_exit:
	return;
}


void bnx2x_write_dmae(struct bnx2x *bp, dma_addr_t dma_addr, u32 dst_addr,
		      u32 len32)
{
	int rc;
	struct dmae_command dmae;

	if (!bp->dmae_ready) {
		u32 *data = bnx2x_sp(bp, wb_data[0]);

		if (CHIP_IS_E1(bp))
			bnx2x_init_ind_wr(bp, dst_addr, data, len32);
		else
			bnx2x_init_str_wr(bp, dst_addr, data, len32);
		return;
	}

	/* set opcode and fixed command fields */
	bnx2x_prep_dmae_with_comp(bp, &dmae, DMAE_SRC_PCI, DMAE_DST_GRC);

	/* fill in addresses and len */
	dmae.src_addr_lo = U64_LO(dma_addr);
	dmae.src_addr_hi = U64_HI(dma_addr);
	dmae.dst_addr_lo = dst_addr >> 2;
	dmae.dst_addr_hi = 0;
	dmae.len = len32;

	/* issue the command and wait for completion */
	rc = bnx2x_issue_dmae_with_comp(bp, &dmae, bnx2x_sp(bp, wb_comp));
	if (rc) {
		BNX2X_ERR("DMAE returned failure %d\n", rc);
#ifdef BNX2X_STOP_ON_ERROR
		bnx2x_panic();
#endif
	}
}


static u64 bnx2x_cyclecounter_read(const struct cyclecounter *cc)
{
	struct bnx2x *bp = container_of(cc, struct bnx2x, cyclecounter);
	int port = BP_PORT(bp);
	u32 wb_data[2];
	u64 phc_cycles;

	REG_RD_DMAE(bp, port ? NIG_REG_TIMESYNC_GEN_REG + tsgen_synctime_t1 :
		    NIG_REG_TIMESYNC_GEN_REG + tsgen_synctime_t0, wb_data, 2);
	phc_cycles = wb_data[1];
	phc_cycles = (phc_cycles << 32) + wb_data[0];

	DP(BNX2X_MSG_PTP, "PHC read cycles = %llu\n", phc_cycles);

	return phc_cycles;
}


static u32 bnx2x_update_mng_version_utility(u8 *version, bool bnx2x_format)
{
	u8 vals[4];
	int i = 0;

	if (bnx2x_format) {
		i = sscanf(version, "1.%c%hhd.%hhd.%hhd",
			   &vals[0], &vals[1], &vals[2], &vals[3]);
		if (i > 0)
			vals[0] -= '0';
	} else {
		i = sscanf(version, "%hhd.%hhd.%hhd.%hhd",
			   &vals[0], &vals[1], &vals[2], &vals[3]);
	}

	while (i < 4)
		vals[i++] = 0;

	return (vals[0] << 24) | (vals[1] << 16) | (vals[2] << 8) | vals[3];
}


struct ubuf_info *sock_zerocopy_alloc(struct sock *sk, size_t size)
{
	struct ubuf_info *uarg;
	struct sk_buff *skb;

	WARN_ON_ONCE(!in_task());

	if (!sock_flag(sk, SOCK_ZEROCOPY))
		return NULL;

	skb = sock_omalloc(sk, 0, GFP_KERNEL);
	if (!skb)
		return NULL;

	BUILD_BUG_ON(sizeof(*uarg) > sizeof(skb->cb));
	uarg = (void *)skb->cb;
	uarg->mmp.user = NULL;

	if (mm_account_pinned_pages(&uarg->mmp, size)) {
		kfree_skb(skb);
		return NULL;
	}

	uarg->callback = sock_zerocopy_callback;
	uarg->id = ((u32)atomic_inc_return(&sk->sk_zckey)) - 1;
	uarg->len = 1;
	uarg->bytelen = size;
	uarg->zerocopy = 1;
	refcount_set(&uarg->refcnt, 1);
	sock_hold(sk);

	return uarg;


static int tbf_enqueue(struct sk_buff *skb, struct Qdisc *sch,
		       struct sk_buff **to_free)
{
	struct tbf_sched_data *q = qdisc_priv(sch);
	int ret;

	if (qdisc_pkt_len(skb) > q->max_size) {
		if (skb_is_gso(skb) && skb_gso_mac_seglen(skb) <= q->max_size)
			return tbf_segment(skb, sch, to_free);
		return qdisc_drop(skb, sch, to_free);
	}
	ret = qdisc_enqueue(skb, q->qdisc, to_free);
	if (ret != NET_XMIT_SUCCESS) {
		if (net_xmit_drop_count(ret))
			qdisc_qstats_drop(sch);
		return ret;
	}

	qdisc_qstats_backlog_inc(sch, skb);
	sch->q.qlen++;
	return NET_XMIT_SUCCESS;
}


static inline unsigned int skb_pagelen(const struct sk_buff *skb)
{
	return skb_headlen(skb) + __skb_pagelen(skb);


*/
static inline struct page *__dev_alloc_pages(gfp_t gfp_mask,
					     unsigned int order)
{
	/* This piece of code contains several assumptions.
	 * 1.  This is for device Rx, therefor a cold page is preferred.
	 * 2.  The expectation is the user wants a compound page.
	 * 3.  If requesting a order 0 page it will not be compound
	 *     due to the check to see if order has a value in prep_new_page
	 * 4.  __GFP_MEMALLOC is ignored if __GFP_NOMEMALLOC is set due to
	 *     code in gfp_to_alloc_flags that should be enforcing this.
	 */
	gfp_mask |= __GFP_COMP | __GFP_MEMALLOC;

	return alloc_pages_node(NUMA_NO_NODE, gfp_mask, order);


void __skb_free_datagram_locked(struct sock *sk, struct sk_buff *skb, int len);
static inline void skb_free_datagram_locked(struct sock *sk,
					    struct sk_buff *skb)
{
	__skb_free_datagram_locked(sk, skb, 0);


static inline int skb_network_offset(const struct sk_buff *skb)
{
	return skb_network_header(skb) - skb->data;


static inline int skb_inner_network_offset(const struct sk_buff *skb)
{
	return skb_inner_network_header(skb) - skb->data;


static inline int pskb_trim_rcsum(struct sk_buff *skb, unsigned int len)
{
	if (likely(len >= skb->len))
		return 0;
	if (skb->ip_summed == CHECKSUM_COMPLETE)
		skb->ip_summed = CHECKSUM_NONE;
	return __pskb_trim(skb, len);


 */
static inline void __skb_fill_page_desc(struct sk_buff *skb, int i,
					struct page *page, int off, int size)
{
	skb_frag_t *frag = &skb_shinfo(skb)->frags[i];

	/*
	 * Propagate page pfmemalloc to the skb if we can. The problem is
	 * that not all callers have unique ownership of the page but rely
	 * on page_is_pfmemalloc doing the right thing(tm).
	 */
	frag->page.p		  = page;
	frag->page_offset	  = off;
	skb_frag_size_set(frag, size);

	page = compound_head(page);
	if (page_is_pfmemalloc(page))
		skb->pfmemalloc	= true;


 */
static inline __sum16 skb_checksum_complete(struct sk_buff *skb)
{
	return skb_csum_unnecessary(skb) ?
	       0 : __skb_checksum_complete(skb);


/* Returns true if the skb was allocated from PFMEMALLOC reserves */
static inline bool skb_pfmemalloc(const struct sk_buff *skb)
{
	return unlikely(skb->pfmemalloc);


static inline int skb_add_data(struct sk_buff *skb,
			       struct iov_iter *from, int copy)
{
	const int off = skb->len;

	if (skb->ip_summed == CHECKSUM_NONE) {
		__wsum csum = 0;
		if (csum_and_copy_from_iter_full(skb_put(skb, copy), copy,
					         &csum, from)) {
			skb->csum = csum_block_add(skb->csum, csum, off);
			return 0;
		}
	} else if (copy_from_iter_full(skb_put(skb, copy), copy, from))
		return 0;

	__skb_trim(skb, off);
	return -EFAULT;


#else
static inline unsigned char *skb_end_pointer(const struct sk_buff *skb)
{
	return skb->end;


static inline bool skb_get_dst_pending_confirm(const struct sk_buff *skb)
{
	return skb->dst_pending_confirm != 0;


int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
		     gfp_t gfp_mask)
{
	int i, osize = skb_end_offset(skb);
	int size = osize + nhead + ntail;
	long off;
	u8 *data;

	BUG_ON(nhead < 0);

	BUG_ON(skb_shared(skb));

	size = SKB_DATA_ALIGN(size);

	if (skb_pfmemalloc(skb))
		gfp_mask |= __GFP_MEMALLOC;
	data = kmalloc_reserve(size + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)),
			       gfp_mask, NUMA_NO_NODE, NULL);
	if (!data)
		goto nodata;
	size = SKB_WITH_OVERHEAD(ksize(data));

	/* Copy only real data... and, alas, header. This should be
	 * optimized for the cases when header is void.
	 */
	memcpy(data + nhead, skb->head, skb_tail_pointer(skb) - skb->head);

	memcpy((struct skb_shared_info *)(data + size),
	       skb_shinfo(skb),
	       offsetof(struct skb_shared_info, frags[skb_shinfo(skb)->nr_frags]));

	/*
	 * if shinfo is shared we must drop the old head gracefully, but if it
	 * is not we can just drop the old head and let the existing refcount
	 * be since all we did is relocate the values
	 */
	if (skb_cloned(skb)) {
		if (skb_orphan_frags(skb, gfp_mask))
			goto nofrags;
		if (skb_zcopy(skb))
			refcount_inc(&skb_uarg(skb)->refcnt);
		for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
			skb_frag_ref(skb, i);

		if (skb_has_frag_list(skb))
			skb_clone_fraglist(skb);

		skb_release_data(skb);
	} else {
		skb_free_head(skb);
	}
	off = (data + nhead) - skb->head;

	skb->head     = data;
	skb->head_frag = 0;
	skb->data    += off;
#ifdef NET_SKBUFF_DATA_USES_OFFSET
	skb->end      = size;
	off           = nhead;
#else
	skb->end      = skb->head + size;
#endif
	skb->tail	      += off;
	skb_headers_offset_update(skb, nhead);
	skb->cloned   = 0;
	skb->hdr_len  = 0;
	skb->nohdr    = 0;
	atomic_set(&skb_shinfo(skb)->dataref, 1);

	skb_metadata_clear(skb);

	/* It is not generally safe to change skb->truesize.
	 * For the moment, we really care of rx path, or
	 * when skb is orphaned (not attached to a socket).
	 */
	if (!skb->sk || skb->destructor == sock_edemux)
		skb->truesize += size - osize;

	return 0;

nofrags:
	kfree(data);
nodata:
	return -ENOMEM;


/* legacy helper around __netdev_alloc_skb() */
static inline struct sk_buff *__dev_alloc_skb(unsigned int length,
					      gfp_t gfp_mask)
{
	return __netdev_alloc_skb(NULL, length, gfp_mask);


static void tbf_walk(struct Qdisc *sch, struct qdisc_walker *walker)
{
	if (!walker->stop) {
		if (walker->count >= walker->skip)
			if (walker->fn(sch, 1, walker) < 0) {
				walker->stop = 1;
				return;
			}
		walker->count++;
	}
}


 */
int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask)
{
	int num_frags = skb_shinfo(skb)->nr_frags;
	struct page *page, *head = NULL;
	int i, new_frags;
	u32 d_off;

	if (skb_shared(skb) || skb_unclone(skb, gfp_mask))
		return -EINVAL;

	if (!num_frags)
		goto release;

	new_frags = (__skb_pagelen(skb) + PAGE_SIZE - 1) >> PAGE_SHIFT;
	for (i = 0; i < new_frags; i++) {
		page = alloc_page(gfp_mask);
		if (!page) {
			while (head) {
				struct page *next = (struct page *)page_private(head);
				put_page(head);
				head = next;
			}
			return -ENOMEM;
		}
		set_page_private(page, (unsigned long)head);
		head = page;
	}

	page = head;
	d_off = 0;
	for (i = 0; i < num_frags; i++) {
		skb_frag_t *f = &skb_shinfo(skb)->frags[i];
		u32 p_off, p_len, copied;
		struct page *p;
		u8 *vaddr;

		skb_frag_foreach_page(f, f->page_offset, skb_frag_size(f),
				      p, p_off, p_len, copied) {
			u32 copy, done = 0;
			vaddr = kmap_atomic(p);

			while (done < p_len) {
				if (d_off == PAGE_SIZE) {
					d_off = 0;
					page = (struct page *)page_private(page);
				}
				copy = min_t(u32, PAGE_SIZE - d_off, p_len - done);
				memcpy(page_address(page) + d_off,
				       vaddr + p_off + done, copy);
				done += copy;
				d_off += copy;
			}
			kunmap_atomic(vaddr);
		}
	}

	/* skb frags release userspace buffers */
	for (i = 0; i < num_frags; i++)
		skb_frag_unref(skb, i);

	/* skb frags point to kernel buffers */
	for (i = 0; i < new_frags - 1; i++) {
		__skb_fill_page_desc(skb, i, head, 0, PAGE_SIZE);
		head = (struct page *)page_private(head);
	}
	__skb_fill_page_desc(skb, new_frags - 1, head, 0, d_off);
	skb_shinfo(skb)->nr_frags = new_frags;

release:
	skb_zcopy_clear(skb, false);
	return 0;


void napi_consume_skb(struct sk_buff *skb, int budget)
{
	if (unlikely(!skb))
		return;

	/* Zero budget indicate non-NAPI context called us, like netpoll */
	if (unlikely(!budget)) {
		dev_consume_skb_any(skb);
		return;
	}

	if (!skb_unref(skb))
		return;

	/* if reaching here SKB is ready to free */
	trace_consume_skb(skb);

	/* if SKB is a clone, don't handle this case */
	if (skb->fclone != SKB_FCLONE_UNAVAILABLE) {
		__kfree_skb(skb);
		return;
	}

	_kfree_skb_defer(skb);
}


void *netdev_alloc_frag(unsigned int fragsz)
{
	return __netdev_alloc_frag(fragsz, GFP_ATOMIC);
}


 */
void *__pskb_pull_tail(struct sk_buff *skb, int delta)
{
	/* If skb has not enough free space at tail, get new one
	 * plus 128 bytes for future expansions. If we have enough
	 * room at tail, reallocate without expansion only if skb is cloned.
	 */
	int i, k, eat = (skb->tail + delta) - skb->end;

	if (eat > 0 || skb_cloned(skb)) {
		if (pskb_expand_head(skb, 0, eat > 0 ? eat + 128 : 0,
				     GFP_ATOMIC))
			return NULL;
	}

	BUG_ON(skb_copy_bits(skb, skb_headlen(skb),
			     skb_tail_pointer(skb), delta));

	/* Optimization: no fragments, no reasons to preestimate
	 * size of pulled pages. Superb.
	 */
	if (!skb_has_frag_list(skb))
		goto pull_pages;

	/* Estimate size of pulled pages. */
	eat = delta;
	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
		int size = skb_frag_size(&skb_shinfo(skb)->frags[i]);

		if (size >= eat)
			goto pull_pages;
		eat -= size;
	}

	/* If we need update frag list, we are in troubles.
	 * Certainly, it is possible to add an offset to skb data,
	 * but taking into account that pulling is expected to
	 * be very rare operation, it is worth to fight against
	 * further bloating skb head and crucify ourselves here instead.
	 * Pure masohism, indeed. 8)8)
	 */
	if (eat) {
		struct sk_buff *list = skb_shinfo(skb)->frag_list;
		struct sk_buff *clone = NULL;
		struct sk_buff *insp = NULL;

		do {
			BUG_ON(!list);

			if (list->len <= eat) {
				/* Eaten as whole. */
				eat -= list->len;
				list = list->next;
				insp = list;
			} else {
				/* Eaten partially. */

				if (skb_shared(list)) {
					/* Sucks! We need to fork list. :-( */
					clone = skb_clone(list, GFP_ATOMIC);
					if (!clone)
						return NULL;
					insp = list->next;
					list = clone;
				} else {
					/* This may be pulled without
					 * problems. */
					insp = list;
				}
				if (!pskb_pull(list, eat)) {
					kfree_skb(clone);
					return NULL;
				}
				break;
			}
		} while (eat);

		/* Free pulled out fragments. */
		while ((list = skb_shinfo(skb)->frag_list) != insp) {
			skb_shinfo(skb)->frag_list = list->next;
			kfree_skb(list);
		}
		/* And insert new clone at head. */
		if (clone) {
			clone->next = list;
			skb_shinfo(skb)->frag_list = clone;
		}
	}
	/* Success! Now we may commit changes to skb data. */

pull_pages:
	eat = delta;
	k = 0;
	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
		int size = skb_frag_size(&skb_shinfo(skb)->frags[i]);

		if (size <= eat) {
			skb_frag_unref(skb, i);
			eat -= size;
		} else {
			skb_shinfo(skb)->frags[k] = skb_shinfo(skb)->frags[i];
			if (eat) {
				skb_shinfo(skb)->frags[k].page_offset += eat;
				skb_frag_size_sub(&skb_shinfo(skb)->frags[k], eat);
				if (!i)
					goto end;
				eat = 0;
			}
			k++;
		}
	}
	skb_shinfo(skb)->nr_frags = k;

end:
	skb->tail     += delta;
	skb->data_len -= delta;

	if (!skb->data_len)
		skb_zcopy_clear(skb, false);

	return skb_tail_pointer(skb);


 */
static inline int skb_clone_writable(const struct sk_buff *skb, unsigned int len)
{
	return !skb_header_cloned(skb) &&
	       skb_headroom(skb) + len <= skb->hdr_len;


static __always_inline void
__skb_postpull_rcsum(struct sk_buff *skb, const void *start, unsigned int len,
		     unsigned int off)
{
	if (skb->ip_summed == CHECKSUM_COMPLETE)
		skb->csum = csum_block_sub(skb->csum,
					   csum_partial(start, len, 0), off);
	else if (skb->ip_summed == CHECKSUM_PARTIAL &&
		 skb_checksum_start_offset(skb) < 0)
		skb->ip_summed = CHECKSUM_NONE;


struct sk_buff *skb_copy(const struct sk_buff *skb, gfp_t gfp_mask)
{
	int headerlen = skb_headroom(skb);
	unsigned int size = skb_end_offset(skb) + skb->data_len;
	struct sk_buff *n = __alloc_skb(size, gfp_mask,
					skb_alloc_rx_flag(skb), NUMA_NO_NODE);

	if (!n)
		return NULL;

	/* Set the data pointer */
	skb_reserve(n, headerlen);
	/* Set the tail pointer and length */
	skb_put(n, skb->len);

	BUG_ON(skb_copy_bits(skb, -headerlen, n->head, headerlen + skb->len));

	copy_skb_header(n, skb);
	return n;


void skb_release_head_state(struct sk_buff *skb)
{
	skb_dst_drop(skb);
	secpath_reset(skb);
	if (skb->destructor) {
		WARN_ON(in_irq());
		skb->destructor(skb);
	}
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
	nf_conntrack_put(skb_nfct(skb));
#endif
#if IS_ENABLED(CONFIG_BRIDGE_NETFILTER)
	nf_bridge_put(skb->nf_bridge);
#endif
}


static struct sk_buff *tbf_dequeue(struct Qdisc *sch)
{
	struct tbf_sched_data *q = qdisc_priv(sch);
	struct sk_buff *skb;

	skb = q->qdisc->ops->peek(q->qdisc);

	if (skb) {
		s64 now;
		s64 toks;
		s64 ptoks = 0;
		unsigned int len = qdisc_pkt_len(skb);

		now = ktime_get_ns();
		toks = min_t(s64, now - q->t_c, q->buffer);

		if (tbf_peak_present(q)) {
			ptoks = toks + q->ptokens;
			if (ptoks > q->mtu)
				ptoks = q->mtu;
			ptoks -= (s64) psched_l2t_ns(&q->peak, len);
		}
		toks += q->tokens;
		if (toks > q->buffer)
			toks = q->buffer;
		toks -= (s64) psched_l2t_ns(&q->rate, len);

		if ((toks|ptoks) >= 0) {
			skb = qdisc_dequeue_peeked(q->qdisc);
			if (unlikely(!skb))
				return NULL;

			q->t_c = now;
			q->tokens = toks;
			q->ptokens = ptoks;
			qdisc_qstats_backlog_dec(sch, skb);
			sch->q.qlen--;
			qdisc_bstats_update(sch, skb);
			return skb;
		}

		qdisc_watchdog_schedule_ns(&q->watchdog,
					   now + max_t(long, -toks, -ptoks));

		/* Maybe we have a shorter packet in the queue,
		   which can be sent now. It sounds cool,
		   but, however, this is wrong in principle.
		   We MUST NOT reorder packets under these circumstances.

		   Really, if we split the flow into independent
		   subflows, it would be a very good solution.
		   This is the main idea of all FQ algorithms
		   (cf. CSZ, HPFQ, HFSC)
		 */

		qdisc_qstats_overlimit(sch);
	}
	return NULL;
}


static inline void skb_reset_tail_pointer(struct sk_buff *skb)
{
	skb->tail = skb->data - skb->head;


int skb_zerocopy_iter_stream(struct sock *sk, struct sk_buff *skb,
			     struct msghdr *msg, int len,
			     struct ubuf_info *uarg)
{
	struct ubuf_info *orig_uarg = skb_zcopy(skb);
	struct iov_iter orig_iter = msg->msg_iter;
	int err, orig_len = skb->len;

	/* An skb can only point to one uarg. This edge case happens when
	 * TCP appends to an skb, but zerocopy_realloc triggered a new alloc.
	 */
	if (orig_uarg && uarg != orig_uarg)
		return -EEXIST;

	err = __zerocopy_sg_from_iter(sk, skb, &msg->msg_iter, len);
	if (err == -EFAULT || (err == -EMSGSIZE && skb->len == orig_len)) {
		struct sock *save_sk = skb->sk;

		/* Streams do not free skb on error. Reset to prev state. */
		msg->msg_iter = orig_iter;
		skb->sk = sk;
		___pskb_trim(skb, orig_len);
		skb->sk = save_sk;
		return err;
	}

	skb_zcopy_set(skb, uarg);
	return skb->len - orig_len;


static inline int skb_unclone(struct sk_buff *skb, gfp_t pri)
{
	might_sleep_if(gfpflags_allow_blocking(pri));

	if (skb_cloned(skb))
		return pskb_expand_head(skb, 0, 0, pri);

	return 0;


 */
void skb_rbtree_purge(struct rb_root *root)
{
	struct rb_node *p = rb_first(root);

	while (p) {
		struct sk_buff *skb = rb_entry(p, struct sk_buff, rbnode);

		p = rb_next(p);
		rb_erase(&skb->rbnode, root);
		kfree_skb(skb);
	}


/* decrement the reference count and return true if we can free the skb */
static inline bool skb_unref(struct sk_buff *skb)
{
	if (unlikely(!skb))
		return false;
	if (likely(refcount_read(&skb->users) == 1))
		smp_rmb();
	else if (likely(!refcount_dec_and_test(&skb->users)))
		return false;

	return true;


 */
static inline void skb_queue_splice_tail_init(struct sk_buff_head *list,
					      struct sk_buff_head *head)
{
	if (!skb_queue_empty(list)) {
		__skb_queue_splice(list, head->prev, (struct sk_buff *) head);
		head->qlen += list->qlen;
		__skb_queue_head_init(list);
	}


		struct sk_buff_head *list);
static inline void __skb_insert(struct sk_buff *newsk,
				struct sk_buff *prev, struct sk_buff *next,
				struct sk_buff_head *list)
{
	newsk->next = next;
	newsk->prev = prev;
	next->prev  = prev->next = newsk;
	list->qlen++;


static inline void skb_metadata_set(struct sk_buff *skb, u8 meta_len)
{
	skb_shinfo(skb)->meta_len = meta_len;


static inline bool skb_defer_rx_timestamp(struct sk_buff *skb)
{
	return false;


void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk);
static inline void __skb_queue_head(struct sk_buff_head *list,
				    struct sk_buff *newsk)
{
	__skb_queue_after(list, (struct sk_buff *)list, newsk);


 */
static inline void __skb_frag_set_page(skb_frag_t *frag, struct page *page)
{
	frag->page.p = page;


 */
static inline int skb_cow_head(struct sk_buff *skb, unsigned int headroom)
{
	return __skb_cow(skb, headroom, skb_header_cloned(skb));


static inline __u32 skb_get_hash_raw(const struct sk_buff *skb)
{
	return skb->hash;


static inline void skb_reset_inner_mac_header(struct sk_buff *skb)
{
	skb->inner_mac_header = skb->data - skb->head;


 */
struct sk_buff *alloc_skb_with_frags(unsigned long header_len,
				     unsigned long data_len,
				     int max_page_order,
				     int *errcode,
				     gfp_t gfp_mask)
{
	int npages = (data_len + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
	unsigned long chunk;
	struct sk_buff *skb;
	struct page *page;
	gfp_t gfp_head;
	int i;

	*errcode = -EMSGSIZE;
	/* Note this test could be relaxed, if we succeed to allocate
	 * high order pages...
	 */
	if (npages > MAX_SKB_FRAGS)
		return NULL;

	gfp_head = gfp_mask;
	if (gfp_head & __GFP_DIRECT_RECLAIM)
		gfp_head |= __GFP_RETRY_MAYFAIL;

	*errcode = -ENOBUFS;
	skb = alloc_skb(header_len, gfp_head);
	if (!skb)
		return NULL;

	skb->truesize += npages << PAGE_SHIFT;

	for (i = 0; npages > 0; i++) {
		int order = max_page_order;

		while (order) {
			if (npages >= 1 << order) {
				page = alloc_pages((gfp_mask & ~__GFP_DIRECT_RECLAIM) |
						   __GFP_COMP |
						   __GFP_NOWARN |
						   __GFP_NORETRY,
						   order);
				if (page)
					goto fill_page;
				/* Do not retry other high order allocations */
				order = 1;
				max_page_order = 0;
			}
			order--;
		}
		page = alloc_page(gfp_mask);
		if (!page)
			goto failure;
fill_page:
		chunk = min_t(unsigned long, data_len,
			      PAGE_SIZE << order);
		skb_fill_page_desc(skb, i, page, 0, chunk);
		data_len -= chunk;
		npages -= 1 << order;
	}
	return skb;

failure:
	kfree_skb(skb);
	return NULL;


static inline __u32 skb_get_hash_flowi6(struct sk_buff *skb, const struct flowi6 *fl6)
{
	if (!skb->l4_hash && !skb->sw_hash) {
		struct flow_keys keys;
		__u32 hash = __get_hash_from_flowi6(fl6, &keys);

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

	return skb->hash;


static void kfree_skbmem(struct sk_buff *skb)
{
	struct sk_buff_fclones *fclones;

	switch (skb->fclone) {
	case SKB_FCLONE_UNAVAILABLE:
		kmem_cache_free(skbuff_head_cache, skb);
		return;

	case SKB_FCLONE_ORIG:
		fclones = container_of(skb, struct sk_buff_fclones, skb1);

		/* We usually free the clone (TX completion) before original skb
		 * This test would have no chance to be true for the clone,
		 * while here, branch prediction will be good.
		 */
		if (refcount_read(&fclones->fclone_ref) == 1)
			goto fastpath;
		break;

	default: /* SKB_FCLONE_CLONE */
		fclones = container_of(skb, struct sk_buff_fclones, skb2);
		break;
	}
	if (!refcount_dec_and_test(&fclones->fclone_ref))
		return;
fastpath:
	kmem_cache_free(skbuff_fclone_cache, fclones);
}


static inline void skb_queue_head_init_class(struct sk_buff_head *list,
		struct lock_class_key *class)
{
	skb_queue_head_init(list);
	lockdep_set_class(&list->lock, class);


static inline void __skb_trim(struct sk_buff *skb, unsigned int len)
{
	__skb_set_length(skb, len);


static unsigned long get_devid(uint8_t busnum, uint8_t devnum)
{
	return (busnum << 16) | devnum;
}


static void stub_rx_pdu(struct usbip_device *ud)
{
	int ret;
	struct usbip_header pdu;
	struct stub_device *sdev = container_of(ud, struct stub_device, ud);
	struct device *dev = &sdev->udev->dev;

	usbip_dbg_stub_rx("Enter\n");

	memset(&pdu, 0, sizeof(pdu));

	/* receive a pdu header */
	ret = usbip_recv(ud->tcp_socket, &pdu, sizeof(pdu));
	if (ret != sizeof(pdu)) {
		dev_err(dev, "recv a header, %d\n", ret);
		usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
		return;
	}

	usbip_header_correct_endian(&pdu, 0);

	if (usbip_dbg_flag_stub_rx)
		usbip_dump_header(&pdu);

	if (!valid_request(sdev, &pdu)) {
		dev_err(dev, "recv invalid request\n");
		usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
		return;
	}

	switch (pdu.base.command) {
	case USBIP_CMD_UNLINK:
		stub_recv_cmd_unlink(sdev, &pdu);
		break;

	case USBIP_CMD_SUBMIT:
		stub_recv_cmd_submit(sdev, &pdu);
		break;

	default:
		/* NOTREACHED */
		dev_err(dev, "unknown pdu\n");
		usbip_event_add(ud, SDEV_EVENT_ERROR_TCP);
		break;
	}
}


int stub_rx_loop(void *data)
{
	struct usbip_device *ud = data;

	while (!kthread_should_stop()) {
		if (usbip_event_happened(ud))
			break;

		stub_rx_pdu(ud);
	}

	return 0;
}


zzip_disk_entry_to_file_header(ZZIP_DISK * disk, struct zzip_disk_entry *entry)
{
    zzip_byte_t *const ptr = disk->buffer + zzip_disk_entry_fileoffset(entry);
    if (disk->buffer > ptr || ptr >= disk->endbuf)
    {
        errno = EBADMSG;
        return 0;
    }
    ___  struct zzip_file_header *file_header = (void *) ptr;
    if (zzip_file_header_get_magic(file_header) != ZZIP_FILE_HEADER_MAGIC)
    {
        errno = EBADMSG;
        return 0;
    }
    return file_header;
    ____;
}


zzip_disk_findfile(ZZIP_DISK * disk, char *filename,
                   struct zzip_disk_entry *after, zzip_strcmp_fn_t compare)
{
    struct zzip_disk_entry *entry = (! after ? zzip_disk_findfirst(disk)
                                     : zzip_disk_findnext(disk, after));
    if (! compare)
        compare = (zzip_strcmp_fn_t) ((disk->flags & ZZIP_DISK_FLAGS_MATCH_NOCASE) ?
                                      (_zzip_strcasecmp) : (strcmp));
    for (; entry; entry = zzip_disk_findnext(disk, entry))
    {
        /* filenames within zip files are often not null-terminated! */
        char *realname = zzip_disk_entry_strdup_name(disk, entry);
        if (! realname)
        {
            return 0; /* ENOMEM | EBADMSG */
        }
        if (! compare(filename, realname))
        {
            free(realname);
            return entry; /* found */
        }
        free(realname);
    }
    errno = ENOENT;
    return 0;
}


foo(short zz_datatype)
{
    /* Header IDs of 0 through 31 are reserved for use by PKWARE.(APPNOTE.TXT) */
    switch (zz_datatype)
    {
    /* *INDENT-OFF* */
    case 0x0001: /* ZIP64 extended information extra field */
    case 0x0007: /* AV Info */
    case 0x0008: /* Reserved for future Unicode file name data (PFS) */
    case 0x0009: /* OS/2 */
    case 0x000a: /* NTFS */
    case 0x000c: /* OpenVMS */
    case 0x000d: /* Unix */
    case 0x000e: /* Reserved for file stream and fork descriptors */
    case 0x000f: /* Patch Descriptor */
    case 0x0014: /* PKCS#7 Store for X.509 Certificates */
    case 0x0015: /* X.509 Certificate ID and Signature for file */
    case 0x0016: /* X.509 Certificate ID for Central Directory */
    case 0x0017: /* Strong Encryption Header */
    case 0x0018: /* Record Management Controls */
    case 0x0019: /* PKCS#7 Encryption Recipient Certificate List */
    /* ......................................................... */
    case 0x0065: /* IBM S/390, AS/400 attributes - uncompressed */
    case 0x0066: /* Reserved for IBM S/390, AS/400 attr - compressed */
    case 0x07c8: /* Macintosh */
    case 0x2605: /* ZipIt Macintosh */
    case 0x2705: /* ZipIt Macintosh 1.3.5+ */
    case 0x2805: /* ZipIt Macintosh 1.3.5+ */
    case 0x334d: /* Info-ZIP Macintosh */
    case 0x4341: /* Acorn/SparkFS  */
    case 0x4453: /* Windows NT security descriptor (binary ACL) */
    case 0x4704: /* VM/CMS */
    case 0x470f: /* MVS */
    case 0x4b46: /* FWKCS MD5 (see below) */
    case 0x4c41: /* OS/2 access control list (text ACL) */
    case 0x4d49: /* Info-ZIP OpenVMS */
    case 0x4f4c: /* Xceed original location extra field */
    case 0x5356: /* AOS/VS (ACL) */
    case 0x5455: /* extended timestamp */
    case 0x554e: /* Xceed unicode extra field */
    case 0x5855: /* Info-ZIP Unix (original, also OS/2, NT, etc) */
    case 0x6542: /* BeOS/BeBox */
    case 0x756e: /* ASi Unix */
    case 0x7855: /* Info-ZIP Unix (new) */
    case 0xfd4a: /* SMS/QDOS */
    /* *INDENT-ON* */
    }
}


zzip_dir_fdopen_ext_io(int fd, zzip_error_t * errcode_p,
                       zzip_strings_t * ext, const zzip_plugin_io_t io)
{
    zzip_error_t rv;
    ZZIP_DIR *dir;

    if ((dir = zzip_dir_alloc_ext_io(ext, io)) == NULL)
        { rv = ZZIP_OUTOFMEM; goto error; }

    dir->fd = fd;
    if ((rv = __zzip_dir_parse(dir)))
        goto error;

    dir->hdr = dir->hdr0;
    dir->refcount |= 0x10000000;

    if (errcode_p)
        *errcode_p = rv;
    return dir;
  error:
    if (dir)
        zzip_dir_free(dir);
    if (errcode_p)
        *errcode_p = rv;
    return NULL;
}


  t_go_generator(t_program* program,
                 const std::map<std::string, std::string>& parsed_options,
                 const std::string& option_string)
    : t_generator(program) {
    (void)option_string;
    std::map<std::string, std::string>::const_iterator iter;

    gen_thrift_import_ = DEFAULT_THRIFT_IMPORT;
    gen_package_prefix_ = "";
    package_flag = "";
    read_write_private_ = false;
    ignore_initialisms_ = false;
    for( iter = parsed_options.begin(); iter != parsed_options.end(); ++iter) {
      if( iter->first.compare("package_prefix") == 0) {
        gen_package_prefix_ = (iter->second);
      } else if( iter->first.compare("thrift_import") == 0) {
        gen_thrift_import_ = (iter->second);
      } else if( iter->first.compare("package") == 0) {
        package_flag = (iter->second);
      } else if( iter->first.compare("read_write_private") == 0) {
        read_write_private_ = true;
      } else if( iter->first.compare("ignore_initialisms") == 0) {
        ignore_initialisms_ =  true;
      } else {
        throw "unknown option go:" + iter->first;
      }
    }

    out_dir_base_ = "gen-go";
  }


string t_go_generator::render_import_protection() {
  return string("var GoUnusedProtection__ int;\n\n");
}


bool t_go_generator::omit_initialization(t_field* tfield) {
  t_const_value* value = tfield->get_value();
  if (!value) {
    return true;
  }
  t_type* type = tfield->get_type()->get_true_type();
  if (type->is_base_type()) {
    t_base_type::t_base tbase = ((t_base_type*)type)->get_base();

    switch (tbase) {
    case t_base_type::TYPE_VOID:
      throw "";

    case t_base_type::TYPE_STRING:
      if (((t_base_type*)type)->is_binary()) {
        //[]byte are always inline
        return false;
      }
      // strings are pointers if has no default
      return value->get_string().empty();

    case t_base_type::TYPE_BOOL:
    case t_base_type::TYPE_I8:
    case t_base_type::TYPE_I16:
    case t_base_type::TYPE_I32:
    case t_base_type::TYPE_I64:
      return value->get_integer() == 0;
    case t_base_type::TYPE_DOUBLE:
      if (value->get_type() == t_const_value::CV_INTEGER) {
        return value->get_integer() == 0;
      } else {
        return value->get_double() == 0.;
      }
    }
  }
  return false;
}


void t_go_generator::generate_serialize_container(ofstream& out,
                                                  t_type* ttype,
                                                  bool pointer_field,
                                                  string prefix) {
  if (pointer_field) {
    prefix = "*" + prefix;
  }
  if (ttype->is_map()) {
    out << indent() << "if err := oprot.WriteMapBegin("
        << type_to_enum(((t_map*)ttype)->get_key_type()) << ", "
        << type_to_enum(((t_map*)ttype)->get_val_type()) << ", "
        << "len(" << prefix << ")); err != nil {" << endl;
    out << indent() << "  return thrift.PrependError(\"error writing map begin: \", err)" << endl;
    out << indent() << "}" << endl;
  } else if (ttype->is_set()) {
    out << indent() << "if err := oprot.WriteSetBegin("
        << type_to_enum(((t_set*)ttype)->get_elem_type()) << ", "
        << "len(" << prefix << ")); err != nil {" << endl;
    out << indent() << "  return thrift.PrependError(\"error writing set begin: \", err)" << endl;
    out << indent() << "}" << endl;
  } else if (ttype->is_list()) {
    out << indent() << "if err := oprot.WriteListBegin("
        << type_to_enum(((t_list*)ttype)->get_elem_type()) << ", "
        << "len(" << prefix << ")); err != nil {" << endl;
    out << indent() << "  return thrift.PrependError(\"error writing list begin: \", err)" << endl;
    out << indent() << "}" << endl;
  } else {
    throw "compiler error: Invalid type in generate_serialize_container '" + ttype->get_name()
        + "' for prefix '" + prefix + "'";
  }

  if (ttype->is_map()) {
    t_map* tmap = (t_map*)ttype;
    out << indent() << "for k, v := range " << prefix << " {" << endl;
    indent_up();
    generate_serialize_map_element(out, tmap, "k", "v");
    indent_down();
    indent(out) << "}" << endl;
  } else if (ttype->is_set()) {
    t_set* tset = (t_set*)ttype;
    out << indent() << "for v, _ := range " << prefix << " {" << endl;
    indent_up();
    generate_serialize_set_element(out, tset, "v");
    indent_down();
    indent(out) << "}" << endl;
  } else if (ttype->is_list()) {
    t_list* tlist = (t_list*)ttype;
    out << indent() << "for _, v := range " << prefix << " {" << endl;

    indent_up();
    generate_serialize_list_element(out, tlist, "v");
    indent_down();
    indent(out) << "}" << endl;
  }

  if (ttype->is_map()) {
    out << indent() << "if err := oprot.WriteMapEnd(); err != nil {" << endl;
    out << indent() << "  return thrift.PrependError(\"error writing map end: \", err)" << endl;
    out << indent() << "}" << endl;
  } else if (ttype->is_set()) {
    out << indent() << "if err := oprot.WriteSetEnd(); err != nil {" << endl;
    out << indent() << "  return thrift.PrependError(\"error writing set end: \", err)" << endl;
    out << indent() << "}" << endl;
  } else if (ttype->is_list()) {
    out << indent() << "if err := oprot.WriteListEnd(); err != nil {" << endl;
    out << indent() << "  return thrift.PrependError(\"error writing list end: \", err)" << endl;
    out << indent() << "}" << endl;
  }
}


void t_go_generator::generate_typedef(t_typedef* ttypedef) {
  generate_go_docstring(f_types_, ttypedef);
  string new_type_name(publicize(ttypedef->get_symbolic()));
  string base_type(type_to_go_type(ttypedef->get_type()));

  if (base_type == new_type_name) {
    return;
  }

  f_types_ << "type " << new_type_name << " " << base_type << endl << endl;
  // Generate a convenience function that converts an instance of a type
  // (which may be a constant) into a pointer to an instance of a type.
  f_types_ << "func " << new_type_name << "Ptr(v " << new_type_name << ") *" << new_type_name
           << " { return &v }" << endl << endl;
}


void t_go_generator::generate_service_helpers(t_service* tservice) {
  vector<t_function*> functions = tservice->get_functions();
  vector<t_function*>::iterator f_iter;
  f_types_ << "// HELPER FUNCTIONS AND STRUCTURES" << endl << endl;

  for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
    t_struct* ts = (*f_iter)->get_arglist();
    generate_go_struct_definition(f_types_, ts, false, false, true);
    generate_go_function_helpers(*f_iter);
  }
}


void t_go_generator::generate_serialize_map_element(ofstream& out,
                                                    t_map* tmap,
                                                    string kiter,
                                                    string viter) {
  t_field kfield(tmap->get_key_type(), "");
  t_field vfield(tmap->get_val_type(), "");
  kfield.set_req(t_field::T_OPT_IN_REQ_OUT);
  vfield.set_req(t_field::T_OPT_IN_REQ_OUT);
  generate_serialize_field(out, &kfield, kiter, true);
  generate_serialize_field(out, &vfield, viter);
}


string t_go_generator::declare_argument(t_field* tfield) {
  std::ostringstream result;
  result << publicize(tfield->get_name()) << "=";

  if (tfield->get_value() != NULL) {
    result << "thrift_spec[" << tfield->get_key() << "][4]";
  } else {
    result << "nil";
  }

  return result.str();
}


            int version() const
            {
                assert(isValid());
                return version_;
            }


    std::string FileIo::path() const
    {
#ifdef EXV_UNICODE_PATH
        if (p_->wpMode_ == Impl::wpUnicode) {
            return ws2s(p_->wpath_);
        }
#endif
        return p_->path_;
    }


    size_t RemoteIo::Impl::populateBlocks(size_t lowBlock, size_t highBlock)
    {
        assert(isMalloced_);

        // optimize: ignore all true blocks on left & right sides.
        while(!blocksMap_[lowBlock].isNone()  && lowBlock  < highBlock) lowBlock++;
        while(!blocksMap_[highBlock].isNone() && highBlock > lowBlock)  highBlock--;

        size_t rcount = 0;
        if (blocksMap_[highBlock].isNone())
        {
            std::string data;
            getDataByRange( (long) lowBlock, (long) highBlock, data);
            rcount = (size_t)data.length();
            if (rcount == 0) {
                throw Error(1, "Data By Range is empty. Please check the permission.");
            }
            byte* source = (byte*)data.c_str();
            size_t remain = rcount, totalRead = 0;
            size_t iBlock = (rcount == size_) ? 0 : lowBlock;

            while (remain) {
                size_t allow = EXV_MIN(remain, blockSize_);
                blocksMap_[iBlock].populate(&source[totalRead], allow);
                remain -= allow;
                totalRead += allow;
                iBlock++;
            }
        }

        return rcount;
    }


    int FileIo::getb()
    {
        assert(p_->fp_ != 0);
        if (p_->switchMode(Impl::opRead) != 0) return EOF;
        return getc(p_->fp_);
    }


    Image::Image(int              imageType,
                 uint16_t         supportedMetadata,
                 BasicIo::AutoPtr io)
        : io_(io),
          pixelWidth_(0),
          pixelHeight_(0),
          imageType_(imageType),
          supportedMetadata_(supportedMetadata),
#ifdef EXV_HAVE_XMP_TOOLKIT
          writeXmpFromPacket_(false),
#else
          writeXmpFromPacket_(true),
#endif
          byteOrder_(invalidByteOrder),
          tags_(),
          init_(true)
    {
    }


    DataBuf readFile(const std::string& path)
    {
        FileIo file(path);
        if (file.open("rb") != 0) {
            throw Error(10, path, "rb", strError());
        }
        struct stat st;
        if (0 != ::stat(path.c_str(), &st)) {
            throw Error(2, path, strError(), "::stat");
        }
        DataBuf buf(st.st_size);
        long len = file.read(buf.pData_, buf.size_);
        if (len != buf.size_) {
            throw Error(2, path, strError(), "FileIo::read");
        }
        return buf;
    }


    void MemIo::populateFakeData() {

    }


    long HttpIo::HttpImpl::getFileLength()
    {
        Exiv2::Dictionary response;
        Exiv2::Dictionary request;
        std::string errors;
        request["server"] = hostInfo_.Host;
        request["page"  ] = hostInfo_.Path;
        if (hostInfo_.Port != "") request["port"] = hostInfo_.Port;
        request["verb"]   = "HEAD";
        long serverCode = (long)http(request, response, errors);
        if (serverCode < 0 || serverCode >= 400 || errors.compare("") != 0) {
            throw Error(55, "Server", serverCode);
        }

        Exiv2::Dictionary_i lengthIter = response.find("Content-Length");
        return (lengthIter == response.end()) ? -1 : atol((lengthIter->second).c_str());
    }


    FileIo::Impl::Impl(const std::wstring& wpath)
        : wpath_(wpath),
          wpMode_(wpUnicode),
          fp_(0), opMode_(opSeek),
#if defined WIN32 && !defined __CYGWIN__
          hFile_(0), hMap_(0),
#endif
          pMappedArea_(0), mappedLength_(0), isMalloced_(false), isWriteable_(false)
    {
    }


    static bool typeValid(uint16_t type)
    {
        return type >= 1 && type <= 13 ;
    }


    long MemIo::write(BasicIo& src)
    {
        if (static_cast<BasicIo*>(this) == &src) return 0;
        if (!src.isopen()) return 0;

        byte buf[4096];
        long readCount = 0;
        long writeTotal = 0;
        while ((readCount = src.read(buf, sizeof(buf)))) {
            write(buf, readCount);
            writeTotal += readCount;
        }

        return writeTotal;
    }


    int RemoteIo::seek( int64_t offset, Position pos )
    {
        assert(p_->isMalloced_);
        uint64_t newIdx = 0;

        switch (pos) {
            case BasicIo::cur: newIdx = p_->idx_ + offset; break;
            case BasicIo::beg: newIdx = offset; break;
            case BasicIo::end: newIdx = p_->size_ + offset; break;
        }

        if ( /*newIdx < 0 || */ newIdx > static_cast<uint64_t>(p_->size_) ) return 1;
        p_->idx_ = static_cast<long>(newIdx);   //not very sure about this. need more test!!    - note by Shawn  fly2xj@gmail.com //TODO
        p_->eof_ = false;
        return 0;
    }


QUtil::setLineBuf(FILE* f)
{
#ifndef _WIN32
    setvbuf(f, reinterpret_cast<char *>(NULL), _IOLBF, 0);
#endif
}


QUtil::fopen_wrapper(std::string const& description, FILE* f)
{
    if (f == 0)
    {
	throw_system_error(description);
    }
    return f;
}


QPDFWriter::writeHeader()
{
    setMinimumPDFVersion(this->m->pdf.getPDFVersion(),
                         this->m->pdf.getExtensionLevel());
    this->m->final_pdf_version = this->m->min_pdf_version;
    this->m->final_extension_level = this->m->min_extension_level;
    if (! this->m->forced_pdf_version.empty())
    {
	QTC::TC("qpdf", "QPDFWriter using forced PDF version");
	this->m->final_pdf_version = this->m->forced_pdf_version;
        this->m->final_extension_level = this->m->forced_extension_level;
    }

    writeString("%PDF-");
    writeString(this->m->final_pdf_version);
    if (this->m->pclm)
    {
        // PCLm version
        writeString("\n%PCLm 1.0\n");
    }
    else
    {
        // This string of binary characters would not be valid UTF-8, so
        // it really should be treated as binary.
        writeString("\n%\xbf\xf7\xa2\xfe\n");
    }
    writeStringQDF("%QDF-1.0\n\n");

    // Note: do not write extra header text here.  Linearized PDFs
    // must include the entire linearization parameter dictionary
    // within the first 1024 characters of the PDF file, so for
    // linearized files, we have to write extra header text after the
    // linearization parameter dictionary.
}


QPDFWriter::setPreserveEncryption(bool val)
{
    this->m->preserve_encryption = val;
}


int main(int argc, char* argv[])
{
    QUtil::setLineBuf(stdout);
    if ((whoami = strrchr(argv[0], '/')) == NULL)
    {
	whoami = argv[0];
    }
    else
    {
	++whoami;
    }
    // For libtool's sake....
    if (strncmp(whoami, "lt-", 3) == 0)
    {
	whoami += 3;
    }

    if ((argc < 3) || (argc > 4))
    {
	usage();
    }

    try
    {
	int n = QUtil::string_to_int(argv[1]);
	char const* filename1 = argv[2];
        char const* arg2 = argv[3];
	runtest(n, filename1, arg2);
    }
    catch (std::exception& e)
    {
	std::cerr << e.what() << std::endl;
	exit(2);
    }

    return 0;
}


QPDFWriter::enqueueObjectsPCLm()
{
    // Image transform stream content for page strip images.
    // Each of this new stream has to come after every page image
    // strip written in the pclm file.
    std::string image_transform_content = "q /image Do Q\n";

    // enqueue all pages first
    std::vector<QPDFObjectHandle> all = this->m->pdf.getAllPages();
    for (std::vector<QPDFObjectHandle>::iterator iter = all.begin();
         iter != all.end(); ++iter)
    {
        // enqueue page
        enqueueObject(*iter);

        // enqueue page contents stream
        enqueueObject((*iter).getKey("/Contents"));

        // enqueue all the strips for each page
        QPDFObjectHandle strips =
            (*iter).getKey("/Resources").getKey("/XObject");
        std::set<std::string> keys = strips.getKeys();
        for (std::set<std::string>::iterator image = keys.begin();
             image != keys.end(); ++image)
        {
            enqueueObject(strips.getKey(*image));
            enqueueObject(QPDFObjectHandle::newStream(
                              &this->m->pdf, image_transform_content));
        }
    }

    // Put root in queue.
    QPDFObjectHandle trailer = getTrimmedTrailer();
    enqueueObject(trailer.getKey("/Root"));
}


QPDFWriter::writeObject(QPDFObjectHandle object, int object_stream_index)
{
    QPDFObjGen old_og = object.getObjGen();

    if ((object_stream_index == -1) &&
        (old_og.getGen() == 0) &&
	(this->m->object_stream_to_objects.count(old_og.getObj())))
    {
	writeObjectStream(object);
	return;
    }

    int new_id = this->m->obj_renumber[old_og];
    if (this->m->qdf_mode)
    {
	if (this->m->page_object_to_seq.count(old_og))
	{
	    writeString("%% Page ");
	    writeString(
		QUtil::int_to_string(
		    this->m->page_object_to_seq[old_og]));
	    writeString("\n");
	}
	if (this->m->contents_to_page_seq.count(old_og))
	{
	    writeString("%% Contents for page ");
	    writeString(
		QUtil::int_to_string(
		    this->m->contents_to_page_seq[old_og]));
	    writeString("\n");
	}
    }
    if (object_stream_index == -1)
    {
	if (this->m->qdf_mode && (! this->m->suppress_original_object_ids))
	{
	    writeString("%% Original object ID: " +
			QUtil::int_to_string(object.getObjectID()) + " " +
			QUtil::int_to_string(object.getGeneration()) + "\n");
	}
	openObject(new_id);
	setDataKey(new_id);
	unparseObject(object, 0, 0);
	this->m->cur_data_key.clear();
	closeObject(new_id);
    }
    else
    {
	unparseObject(object, 0, f_in_ostream);
	writeString("\n");
    }

    if ((! this->m->direct_stream_lengths) && object.isStream())
    {
	if (this->m->qdf_mode)
	{
	    if (this->m->added_newline)
	    {
		writeString("%QDF: ignore_newline\n");
	    }
	}
	openObject(new_id + 1);
	writeString(QUtil::int_to_string(this->m->cur_stream_length));
	closeObject(new_id + 1);
    }
}


QPDFWriter::writeObjectStream(QPDFObjectHandle object)
{
    // Note: object might be null if this is a place-holder for an
    // object stream that we are generating from scratch.

    QPDFObjGen old_og = object.getObjGen();
    assert(old_og.getGen() == 0);
    int old_id = old_og.getObj();
    int new_id = this->m->obj_renumber[old_og];

    std::vector<qpdf_offset_t> offsets;
    qpdf_offset_t first = 0;

    // Generate stream itself.  We have to do this in two passes so we
    // can calculate offsets in the first pass.
    PointerHolder<Buffer> stream_buffer;
    int first_obj = -1;
    bool compressed = false;
    for (int pass = 1; pass <= 2; ++pass)
    {
	if (pass == 1)
	{
	    pushDiscardFilter();
	}
	else
	{
	    // Adjust offsets to skip over comment before first object

	    first = offsets.at(0);
	    for (std::vector<qpdf_offset_t>::iterator iter = offsets.begin();
		 iter != offsets.end(); ++iter)
	    {
		*iter -= first;
	    }

	    // Take one pass at writing pairs of numbers so we can get
	    // their size information
	    pushDiscardFilter();
	    writeObjectStreamOffsets(offsets, first_obj);
	    first += this->m->pipeline->getCount();
	    popPipelineStack();

	    // Set up a stream to write the stream data into a buffer.
	    Pipeline* next = pushPipeline(new Pl_Buffer("object stream"));
            if (! (this->m->stream_decode_level || this->m->qdf_mode))
	    {
		compressed = true;
		next = pushPipeline(
		    new Pl_Flate("compress object stream", next,
				 Pl_Flate::a_deflate));
	    }
	    activatePipelineStack();
	    writeObjectStreamOffsets(offsets, first_obj);
	}

	int count = 0;
	for (std::set<QPDFObjGen>::iterator iter =
		 this->m->object_stream_to_objects[old_id].begin();
	     iter != this->m->object_stream_to_objects[old_id].end();
	     ++iter, ++count)
	{
	    QPDFObjGen obj = *iter;
	    int new_obj = this->m->obj_renumber[obj];
	    if (first_obj == -1)
	    {
		first_obj = new_obj;
	    }
	    if (this->m->qdf_mode)
	    {
		writeString("%% Object stream: object " +
			    QUtil::int_to_string(new_obj) + ", index " +
			    QUtil::int_to_string(count));
		if (! this->m->suppress_original_object_ids)
		{
		    writeString("; original object ID: " +
				QUtil::int_to_string(obj.getObj()));
                    // For compatibility, only write the generation if
                    // non-zero.  While object streams only allow
                    // objects with generation 0, if we are generating
                    // object streams, the old object could have a
                    // non-zero generation.
                    if (obj.getGen() != 0)
                    {
                        QTC::TC("qpdf", "QPDFWriter original obj non-zero gen");
                        writeString(" " + QUtil::int_to_string(obj.getGen()));
                    }
		}
		writeString("\n");
	    }
	    if (pass == 1)
	    {
		offsets.push_back(this->m->pipeline->getCount());
	    }
	    writeObject(this->m->pdf.getObjectByObjGen(obj), count);

	    this->m->xref[new_obj] = QPDFXRefEntry(2, new_id, count);
	}

	// stream_buffer will be initialized only for pass 2
	popPipelineStack(&stream_buffer);
    }

    // Write the object
    openObject(new_id);
    setDataKey(new_id);
    writeString("<<");
    writeStringQDF("\n ");
    writeString(" /Type /ObjStm");
    writeStringQDF("\n ");
    size_t length = stream_buffer->getSize();
    adjustAESStreamLength(length);
    writeString(" /Length " + QUtil::int_to_string(length));
    writeStringQDF("\n ");
    if (compressed)
    {
	writeString(" /Filter /FlateDecode");
    }
    writeString(" /N " + QUtil::int_to_string(offsets.size()));
    writeStringQDF("\n ");
    writeString(" /First " + QUtil::int_to_string(first));
    if (! object.isNull())
    {
	// If the original object has an /Extends key, preserve it.
	QPDFObjectHandle dict = object.getDict();
	QPDFObjectHandle extends = dict.getKey("/Extends");
	if (extends.isIndirect())
	{
	    QTC::TC("qpdf", "QPDFWriter copy Extends");
	    writeStringQDF("\n ");
	    writeString(" /Extends ");
	    unparseChild(extends, 1, f_in_ostream);
	}
    }
    writeStringQDF("\n");
    writeStringNoQDF(" ");
    writeString(">>\nstream\n");
    if (this->m->encrypted)
    {
	QTC::TC("qpdf", "QPDFWriter encrypt object stream");
    }
    pushEncryptionFilter();
    writeBuffer(stream_buffer);
    popPipelineStack();
    writeString("endstream");
    this->m->cur_data_key.clear();
    closeObject(new_id);
}


QPDFObjectHandle newName(std::string const& name)
{
    return QPDFObjectHandle::newName(name);
}


QPDFWriter::setNewlineBeforeEndstream(bool val)
{
    this->m->newline_before_endstream = val;
}


    virtual ~ParserCallbacks()
    {
    }


static void test_numrange(char const* range)
{
    if (range == 0)
    {
        std::cout << "null" << std::endl;
    }
    else
    {
        std::vector<int> result = parse_numrange(range, 15);
        std::cout << "numeric range " << range << " ->";
        for (std::vector<int>::iterator iter = result.begin();
             iter != result.end(); ++iter)
        {
            std::cout << " " << *iter;
        }
        std::cout << std::endl;
    }
}


static line_status iniparser_line(
    const char * input_line,
    char * section,
    char * key,
    char * value)
{
    line_status sta ;
    char * line = NULL;
    size_t      len ;

    line = xstrdup(input_line);
    len = strstrip(line);

    sta = LINE_UNPROCESSED ;
    if (len<1) {
        /* Empty line */
        sta = LINE_EMPTY ;
    } else if (line[0]=='#' || line[0]==';') {
        /* Comment line */
        sta = LINE_COMMENT ;
    } else if (line[0]=='[' && line[len-1]==']') {
        /* Section name */
        sscanf(line, "[%[^]]", section);
        strstrip(section);
        strlwc(section, section, len);
        sta = LINE_SECTION ;
    } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2
           ||  sscanf (line, "%[^=] = '%[^\']'",   key, value) == 2) {
        /* Usual key=value with quotes, with or without comments */
        strstrip(key);
        strlwc(key, key, len);
        /* Don't strip spaces from values surrounded with quotes */
        /*
         * sscanf cannot handle '' or "" as empty values
         * this is done here
         */
        if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) {
            value[0]=0 ;
        }
        sta = LINE_VALUE ;
    } else if (sscanf (line, "%[^=] = %[^;#]", key, value) == 2) {
        /* Usual key=value without quotes, with or without comments */
        strstrip(key);
        strlwc(key, key, len);
        strstrip(value);

        sta = LINE_VALUE ;
    } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2
           ||  sscanf(line, "%[^=] %[=]", key, value) == 2) {
        /*
         * Special cases:
         * key=
         * key=;
         * key=#
         */
        strstrip(key);
        strlwc(key, key, len);
        value[0]=0 ;
        sta = LINE_VALUE ;
    } else {
        /* Generate syntax error */
        sta = LINE_ERROR ;
    }

    free(line);
    return sta ;
}


static void temp_split_chan_free(TEMP_SPLIT_CHAN_REC *rec)
{
	g_string_free(rec->nicks, TRUE);
	g_free(rec);
}


static int netjoin_set_nickmode(IRC_SERVER_REC *server, NETJOIN_REC *rec,
				const char *channel, char prefix)
{
	GSList *pos;
	const char *flags;
	char *found_chan = NULL;

	for (pos = rec->now_channels; pos != NULL; pos = pos->next) {
		char *chan = pos->data;
		if (strcasecmp(chan+1, channel) == 0) {
			found_chan = chan;
			break;
		}
	}

	if (found_chan == NULL)
		return FALSE;

	flags = server->get_nick_flags(SERVER(server));
	while (*flags != '\0') {
		if (found_chan[0] == *flags)
			break;
		if (prefix == *flags) {
			found_chan[0] = prefix;
			break;
		}
		flags++;
	}
	return TRUE;
}


void fe_netsplit_init(void)
{
	settings_add_int("misc", "netsplit_max_nicks", 10);
	settings_add_int("misc", "netsplit_nicks_hide_threshold", 15);
	split_tag = -1;
	printing_splits = FALSE;

	read_settings();
	signal_add("netsplit new", (SIGNAL_FUNC) sig_netsplit_servers);
	signal_add("setup changed", (SIGNAL_FUNC) read_settings);
	command_bind_irc("netsplit", NULL, (SIGNAL_FUNC) cmd_netsplit);
}


static CHATNET_REC *create_chatnet(void)
{
        return g_malloc0(sizeof(IRC_CHATNET_REC));
}


static void sig_chatnet_destroyed(IRC_CHATNET_REC *rec)
{
	if (IS_IRC_CHATNET(rec))
		g_free(rec->usermode);
}


void irc_servers_setup_deinit(void)
{
	signal_remove("server setup fill reconn", (SIGNAL_FUNC) sig_server_setup_fill_reconn);
	signal_remove("server setup fill connect", (SIGNAL_FUNC) sig_server_setup_fill_connect);
	signal_remove("server setup fill chatnet", (SIGNAL_FUNC) sig_server_setup_fill_chatnet);
	signal_remove("server setup read", (SIGNAL_FUNC) sig_server_setup_read);
	signal_remove("server setup saved", (SIGNAL_FUNC) sig_server_setup_saved);
}


static void event_connected(IRC_SERVER_REC *server, const char *data, const char *from)
{
	char *params, *nick;
	GTimeVal now;

	g_return_if_fail(server != NULL);

	params = event_get_params(data, 1, &nick);

	if (g_strcmp0(server->nick, nick) != 0) {
		/* nick changed unexpectedly .. connected via proxy, etc. */
		g_free(server->nick);
		server->nick = g_strdup(nick);
	}

	/* set the server address */
	g_free(server->real_address);
	server->real_address = from == NULL ?
		g_strdup(server->connrec->address) : /* shouldn't happen.. */
		g_strdup(from);

	/* last welcome message found - commands can be sent to server now. */
	server->connected = 1;
	server->real_connect_time = time(NULL);

	/* let the queue send now that we are identified */
	g_get_current_time(&now);
	memcpy(&server->wait_cmd, &now, sizeof(GTimeVal));

	if (server->connrec->usermode != NULL) {
		/* Send the user mode, before the autosendcmd.
		 * Do not pass this through cmd_mode because it
		 * is not known whether the resulting MODE message
		 * (if any) is the initial umode or a reply to this.
		 */
		irc_send_cmdv(server, "MODE %s %s", server->nick,
				server->connrec->usermode);
		g_free_not_null(server->wanted_usermode);
		server->wanted_usermode = g_strdup(server->connrec->usermode);
	}

	signal_emit("event connected", 1, server);
	g_free(params);
}


void chat_completion_init(void)
{
	settings_add_str("completion", "completion_char", ":");
	settings_add_bool("completion", "completion_auto", FALSE);
	settings_add_int("completion", "completion_keep_publics", 50);
	settings_add_int("completion", "completion_keep_privates", 10);
	settings_add_bool("completion", "completion_nicks_lowercase", FALSE);
	settings_add_bool("completion", "completion_strict", FALSE);
	settings_add_bool("completion", "completion_empty_line", TRUE);
	settings_add_choice("completion", "completion_nicks_match_case", COMPLETE_MCASE_AUTO, "never;always;auto");

	settings_add_bool("lookandfeel", "expand_escapes", FALSE);

	read_settings();
	signal_add("complete word", (SIGNAL_FUNC) sig_complete_word);
	signal_add("complete command msg", (SIGNAL_FUNC) sig_complete_msg);
	signal_add("complete command query", (SIGNAL_FUNC) sig_complete_msg);
	signal_add("complete command action", (SIGNAL_FUNC) sig_complete_msg);
	signal_add("complete erase command msg", (SIGNAL_FUNC) sig_erase_complete_msg);
	signal_add("complete erase command query", (SIGNAL_FUNC) sig_erase_complete_msg);
	signal_add("complete erase command action", (SIGNAL_FUNC) sig_erase_complete_msg);
	signal_add("complete command connect", (SIGNAL_FUNC) sig_complete_connect);
	signal_add("complete command server", (SIGNAL_FUNC) sig_complete_connect);
	signal_add("complete command disconnect", (SIGNAL_FUNC) sig_complete_tag);
	signal_add("complete command reconnect", (SIGNAL_FUNC) sig_complete_tag);
	signal_add("complete command window server", (SIGNAL_FUNC) sig_complete_tag);
	signal_add("complete command topic", (SIGNAL_FUNC) sig_complete_topic);
	signal_add("complete command away", (SIGNAL_FUNC) sig_complete_away);
	signal_add("complete command unalias", (SIGNAL_FUNC) sig_complete_unalias);
	signal_add("complete command alias", (SIGNAL_FUNC) sig_complete_alias);
	signal_add("complete command window goto", (SIGNAL_FUNC) sig_complete_window);
	signal_add("complete command window item move", (SIGNAL_FUNC) sig_complete_channel);
	signal_add("complete command server add", (SIGNAL_FUNC) sig_complete_server);
	signal_add("complete command server remove", (SIGNAL_FUNC) sig_complete_server);
	signal_add("complete command recode remove", (SIGNAL_FUNC) sig_complete_target);
	signal_add("message public", (SIGNAL_FUNC) sig_message_public);
	signal_add("message join", (SIGNAL_FUNC) sig_message_join);
	signal_add("message private", (SIGNAL_FUNC) sig_message_private);
	signal_add("message own_public", (SIGNAL_FUNC) sig_message_own_public);
	signal_add("message own_private", (SIGNAL_FUNC) sig_message_own_private);
	signal_add("nicklist remove", (SIGNAL_FUNC) sig_nick_removed);
	signal_add("nicklist changed", (SIGNAL_FUNC) sig_nick_changed);
	signal_add("send text", (SIGNAL_FUNC) event_text);
	signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
	signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
	signal_add("setup changed", (SIGNAL_FUNC) read_settings);
}


static void sig_message_own_private(SERVER_REC *server, const char *msg,
				    const char *target, const char *origtarget)
{
	g_return_if_fail(server != NULL);

	if (target != NULL && query_find(server, target) == NULL)
		SERVER_LAST_MSG_ADD(server, target);
}


static void sig_message_join(SERVER_REC *server, const char *channel,
			     const char *nick, const char *address)
{
	CHANNEL_REC *chanrec;
	g_return_if_fail(nick != NULL);

	chanrec = channel_find(server, channel);
	if (chanrec != NULL)
		CHANNEL_LAST_MSG_ADD(chanrec, nick, FALSE);
}


static void sasl_start(IRC_SERVER_REC *server, const char *data, const char *from)
{
	IRC_SERVER_CONNECT_REC *conn;

	sasl_timeout_stop(server);

	conn = server->connrec;

	switch (conn->sasl_mechanism) {
		case SASL_MECHANISM_PLAIN:
			irc_send_cmd_now(server, "AUTHENTICATE PLAIN");
			break;

		case SASL_MECHANISM_EXTERNAL:
			irc_send_cmd_now(server, "AUTHENTICATE EXTERNAL");
			break;
	}
	server->sasl_timeout = g_timeout_add(SASL_TIMEOUT, (GSourceFunc) sasl_timeout, server);
}


static void cmd_oper_got_pass(const char *password, OPER_PASS_REC *rec)
{
	SERVER_REC *server_rec = server_find_tag(rec->server_tag);
	if (*password != '\0' && IS_IRC_SERVER(server_rec))
		irc_send_cmdv((IRC_SERVER_REC *) server_rec, "OPER %s %s", rec->nick, password);
	g_free(rec->nick);
	g_free(rec->server_tag);
	g_free(rec);
}


static char *auto_complete(CHANNEL_REC *channel, const char *line)
{
	GList *comp;
	const char *p;
        char *nick, *ret;

	p = strstr(line, completion_char);
	if (p == NULL)
		return NULL;

        nick = g_strndup(line, (int) (p-line));

        ret = NULL;
	if (nicklist_find(channel, nick) == NULL) {
                /* not an exact match, use the first possible completion */
		comp = completion_channel_nicks(channel, nick, NULL);
		if (comp != NULL) {
			ret = g_strconcat(comp->data, p, NULL);
			g_list_foreach(comp, (GFunc) g_free, NULL);
			g_list_free(comp);
		}
	}

	g_free(nick);

        return ret;
}


static void sig_complete_alias(GList **list, WINDOW_REC *window,
				const char *word, const char *line,
				int *want_space)
{
	const char *definition;

	g_return_if_fail(list != NULL);
	g_return_if_fail(word != NULL);
	g_return_if_fail(line != NULL);

	if (*line != '\0') {
		if ((definition = alias_find(line)) != NULL) {
			*list = g_list_append(NULL, g_strdup(definition));
			signal_stop();
		}
	} else {
		*list = completion_get_aliases(word);
		if (*list != NULL) signal_stop();
	}
}


static void cmd_sethost(const char *data, IRC_SERVER_REC *server)
{
	GSList *tmp;

	g_return_if_fail(data != NULL);
	if (!IS_IRC_SERVER(server) || !server->connected)
		cmd_return_error(CMDERR_NOT_CONNECTED);

	/* Save all the joined channels in server to window binds, since
	   the server will soon /PART + /JOIN us in all channels. */
	for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
		CHANNEL_REC *channel = tmp->data;

		window_bind_add(window_item_window(channel),
				server->tag, channel->visible_name);
	}

        irc_send_cmdv(server, "SETHOST %s", data);
}


static void last_msg_add(GSList **list, const char *nick, int own, int max)
{
	LAST_MSG_REC *rec;

	if (max <= 0)
		return;

	rec = last_msg_find(*list, nick);
	if (rec != NULL) {
		/* msg already exists, update it */
		*list = g_slist_remove(*list, rec);
		if (own)
			rec->own = max;
		else if (rec->own)
                        rec->own--;
	} else {
		rec = g_new(LAST_MSG_REC, 1);
		rec->nick = g_strdup(nick);

		while ((int)g_slist_length(*list) >= max) {
			last_msg_destroy(list, g_slist_last(*list)->data);
		}

		rec->own = own ? max : 0;
	}
	rec->time = time(NULL);

        last_msg_dec_owns(*list);

	*list = g_slist_prepend(*list, rec);
}


static void sasl_timeout_stop(IRC_SERVER_REC *server)
{
	/* Stop any pending timeout, if any */
	if (server->sasl_timeout != 0) {
		g_source_remove(server->sasl_timeout);
		server->sasl_timeout = 0;
	}
}


MAIN_WINDOW_REC *mainwindow_create(int right)
{
	MAIN_WINDOW_REC *rec, *parent;
	int space;

	rec = g_new0(MAIN_WINDOW_REC, 1);
	rec->dirty = TRUE;

	if (mainwindows == NULL) {
		active_mainwin = rec;

		rec->first_line = screen_reserved_top;
		rec->last_line = term_height-1 - screen_reserved_bottom;
		rec->height = rec->last_line-rec->first_line+1;
		rec->first_column = screen_reserved_left;
		rec->last_column = screen_width-1 - screen_reserved_right;
		rec->width = rec->last_column-rec->first_column+1;
	} else {
		parent = WINDOW_MAIN(active_win);

		if (!right) {
			GSList *tmp, *line;
			if (MAIN_WINDOW_TEXT_HEIGHT(parent) <
			    WINDOW_MIN_SIZE+NEW_WINDOW_SIZE)
				parent = find_window_with_room();
			if (parent == NULL)
				return NULL; /* not enough space */

			space = parent->height / 2;
			rec->first_line = parent->first_line;
			rec->last_line = rec->first_line + space;
			rec->height = rec->last_line-rec->first_line+1;
			rec->first_column = screen_reserved_left;
			rec->last_column = screen_width-1 - screen_reserved_right;
			rec->width = rec->last_column-rec->first_column+1;

			line = mainwindows_get_line(parent);
			for (tmp = line; tmp != NULL; tmp = tmp->next) {
				MAIN_WINDOW_REC *rec = tmp->data;
				rec->first_line += space+1;
				mainwindow_resize(rec, 0, -space-1);
			}
			g_slist_free(line);
		} else {
			if (MAIN_WINDOW_TEXT_WIDTH(parent) <
			    2* NEW_WINDOW_WIDTH)
				parent = find_window_with_room_right();
			if (parent == NULL)
				return NULL; /* not enough space */

			space = parent->width / 2;
			rec->first_line = parent->first_line;
			rec->last_line = parent->last_line;
			rec->height = parent->height;
			rec->first_column = parent->last_column - space + 1;
			rec->last_column = parent->last_column;
			rec->width = rec->last_column-rec->first_column+1;

			parent->last_column -= space+1;
			mainwindow_resize(parent, -space-1, 0);
		}
	}

	rec->screen_win = mainwindow_create_screen(rec);
	term_refresh(NULL);

	mainwindows = g_slist_append(mainwindows, rec);
	signal_emit("mainwindow created", 1, rec);
	return rec;
}


static void cmd_window_grow(const char *data)
{
	MAIN_WINDOW_REC *window;
	int count;

	count = *data == '\0' ? 1 : atoi(data);
	window = WINDOW_MAIN(active_win);

	if (!mainwindow_grow(window, count, FALSE)) {
		printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
				   TXT_WINDOW_TOO_SMALL);
	}
}


static void cmd_window_rshrink(const char *data)
{
	int count;

	count = *data == '\0' ? 1 : atoi(data);
	if (!mainwindow_rshrink(WINDOW_MAIN(active_win), count)) {
		printformat_window(active_win, MSGLEVEL_CLIENTNOTICE,
				   TXT_WINDOW_TOO_SMALL);
	}
}


void mainwindow_change_active(MAIN_WINDOW_REC *mainwin,
			      WINDOW_REC *skip_window)
{
        WINDOW_REC *window, *other;
	GSList *tmp;

        mainwin->active = NULL;
	if (mainwin->sticky_windows) {
		/* sticky window */
                tmp = get_sticky_windows_sorted(mainwin);
                window = tmp->data;
		if (window == skip_window) {
			window = tmp->next == NULL ? NULL :
				tmp->next->data;
		}
                g_slist_free(tmp);

		if (window != NULL) {
			window_set_active(window);
			return;
		}
	}

        other = NULL;
	for (tmp = windows; tmp != NULL; tmp = tmp->next) {
		WINDOW_REC *rec = tmp->data;

		if (rec != skip_window) {
			other = rec;
			break;
		}
	}

	window_set_active(other);
	if (mainwindows->next != NULL)
		mainwindow_destroy(mainwin);
}


static void mainwindows_resize_smaller(int ydiff)
{
	MAIN_WINDOW_REC *rec;
	GSList *sorted, *tmp;
	int space;

	sorted = NULL;
	for (rec = mainwindows_find_lower(NULL);
	     rec != NULL;
	     rec = mainwindows_find_lower(rec)) {
		sorted = g_slist_prepend(sorted, rec);
	}
	if (sorted == NULL)
		return;

	for (;;) {
		int skip_active = FALSE;
		space = 0;
		/* for each line of windows, calculate the space that can be reduced still */
		for (tmp = sorted; tmp != NULL; tmp = tmp->next) {
			int min;
			GSList *line, *ltmp;
			rec = tmp->data;
			line = mainwindows_get_line(rec);
			min = screen_height - ydiff;
			for (ltmp = line; ltmp != NULL; ltmp = ltmp->next) {
				int lmin;
				MAIN_WINDOW_REC *win = ltmp->data;
				if (win == active_mainwin && tmp == sorted)
					skip_active = TRUE;

				lmin = MAIN_WINDOW_TEXT_HEIGHT(win)-WINDOW_MIN_SIZE;
				if (lmin < min)
					min = lmin;
			}
			g_slist_free(line);
			space += min;
		}

		if (space >= -ydiff)
			break;

		rec = sorted->data;
		if (skip_active && sorted->next != NULL)
			rec = sorted->next->data;
		sorted = g_slist_remove(sorted, rec);

		if (sorted != NULL) {
			/* terminal is too small - destroy the
			   uppest window and try again */
			GSList *line, *ltmp;
			line = mainwindows_get_line(rec);
			for (ltmp = line; ltmp != NULL; ltmp = ltmp->next) {
				MAIN_WINDOW_REC *win = ltmp->data;
				mainwindow_destroy(win);
			}
			g_slist_free(line);
		} else {
			/* only one line of window in screen.. just force the resize */
			GSList *line, *ltmp;
			line = mainwindows_get_line(rec);
			for (ltmp = line; ltmp != NULL; ltmp = ltmp->next) {
				MAIN_WINDOW_REC *win = ltmp->data;
				win->last_line += ydiff;
				mainwindow_resize(win, 0, ydiff);
			}
			g_slist_free(line);
			return;
		}
	}

	/* resize windows that have space */
	for (tmp = sorted; tmp != NULL && ydiff < 0; tmp = tmp->next) {
		int min;
		GSList *line, *ltmp;

		rec = tmp->data;
		line = mainwindows_get_line(rec);
		min = screen_height - ydiff;
		for (ltmp = line; ltmp != NULL; ltmp = ltmp->next) {
			int lmin;
			MAIN_WINDOW_REC *win = ltmp->data;
			lmin = MAIN_WINDOW_TEXT_HEIGHT(win)-WINDOW_MIN_SIZE;
			if (lmin < min)
				min = lmin;
		}
		space = min;

		if (space == 0) {
			/* move the line */
			for (ltmp = line; ltmp != NULL; ltmp = ltmp->next) {
				MAIN_WINDOW_REC *win = ltmp->data;
				mainwindow_resize(win, 0, 0);
				win->size_dirty = TRUE;
				win->first_line += ydiff;
				win->last_line += ydiff;
				signal_emit("mainwindow moved", 1, win);
			}
		} else {
			if (space > -ydiff) space = -ydiff;
			for (ltmp = line; ltmp != NULL; ltmp = ltmp->next) {
				MAIN_WINDOW_REC *win = ltmp->data;
				win->last_line += ydiff;
				win->first_line += ydiff + space;

				mainwindow_resize(win, 0, -space);
			}
			ydiff += space;
		}
		g_slist_free(line);
	}

	g_slist_free(sorted);
}


static void cmd_window_show(const char *data)
{
	_cmd_window_show_opt(data, FALSE);
}


static void windows_print_sticky(WINDOW_REC *win)
{
        MAIN_WINDOW_REC *mainwin;
        GSList *tmp, *list;
	GString *str;

        mainwin = WINDOW_MAIN(win);

        /* convert to string */
	str = g_string_new(NULL);
	list = get_sticky_windows_sorted(mainwin);
	for (tmp = list; tmp != NULL; tmp = tmp->next) {
		WINDOW_REC *rec = tmp->data;

		g_string_append_printf(str, "#%d, ", rec->refnum);
	}
        g_string_truncate(str, str->len-2);
        g_slist_free(list);

	printformat_window(win, MSGLEVEL_CLIENTCRAP,
			   TXT_WINDOW_INFO_STICKY, str->str);
        g_string_free(str, TRUE);
}


void mainwindows_resize(int width, int height)
{
	int xdiff, ydiff;

	xdiff = width-screen_width;
	ydiff = height-screen_height;
	screen_width = width;
	screen_height = height;

	if (ydiff > 0) {
		/* algorithm: enlarge bottom window */
		MAIN_WINDOW_REC *rec;
		GSList *line, *tmp;
		line = mainwindows_get_line(mainwindows_find_upper(NULL));
		for (tmp = line; tmp != NULL; tmp = tmp->next) {
			rec = tmp->data;
			rec->last_line += ydiff;
			mainwindow_resize(rec, 0, ydiff);
		}
		g_slist_free(line);
	}

	if (xdiff > 0) {
		/* algorithm: distribute new space on each line */
		MAIN_WINDOW_REC *win;

		for (win = mainwindows_find_lower(NULL);
		     win != NULL;
		     win = mainwindows_find_lower(win)) {
			mainwindows_rresize_line(xdiff, win);
		}
	}

	if (xdiff < 0) {
		/* algorithm: shrink each window,
		   destroy windows on the right if no room */
		MAIN_WINDOW_REC *win;

		for (win = mainwindows_find_lower(NULL);
		     win != NULL;
		     win = mainwindows_find_lower(win)) {
			int max_windows, i, last_column;
			GSList *line, *tmp;

			line = mainwindows_get_line(win);
			max_windows = (screen_width + 1) / (NEW_WINDOW_WIDTH + 1);
			if (max_windows < 1)
				max_windows = 1;
			last_column = screen_width - 1;
			for (tmp = line, i = 0; tmp != NULL; tmp = tmp->next, i++) {
				MAIN_WINDOW_REC *rec = tmp->data;
				if (i >= max_windows)
					mainwindow_destroy_half(rec);
				else
					last_column = rec->last_column;
			}
			win = line->data;
			g_slist_free(line);

			mainwindows_rresize_line(screen_width - last_column + 1, win);
		}
	}

	if (ydiff < 0) {
		/* algorithm: shrink windows starting from bottom,
		   destroy windows starting from top if no room */
		mainwindows_resize_smaller(ydiff);
	}

	signal_emit("terminal resized", 0);

	irssi_redraw();
}


static int try_shrink_lower(MAIN_WINDOW_REC *window, int count)
{
	MAIN_WINDOW_REC *shrink_win;

	shrink_win = mainwindows_find_lower(window);
	if (shrink_win != NULL) {
		int ok;
		GSList *shrink_list, *tmp;
		MAIN_WINDOW_REC *win;

		ok = TRUE;
		shrink_list = mainwindows_get_line(shrink_win);

		for (tmp = shrink_list; tmp != NULL; tmp = tmp->next) {
			win = tmp->data;
			if (MAIN_WINDOW_TEXT_HEIGHT(win)-count < WINDOW_MIN_SIZE) {
				ok = FALSE;
				break;
			}
		}
		if (ok) {
			GSList *grow_list;
			grow_list = mainwindows_get_line(window);

			for (tmp = shrink_list; tmp != NULL; tmp = tmp->next) {
				win = tmp->data;
				win->first_line += count;
			}
			for (tmp = grow_list; tmp != NULL; tmp = tmp->next) {
				win = tmp->data;
				win->last_line += count;
			}

			mainwindows_resize_two(grow_list, shrink_list, count);
			g_slist_free(grow_list);
		}

		g_slist_free(shrink_list);
		return ok;
	}

	return FALSE;
}


static int mainwindow_shrink(MAIN_WINDOW_REC *window, int count, int resize_lower)
{
	if (MAIN_WINDOW_TEXT_HEIGHT(window)-count < WINDOW_MIN_SIZE)
                return FALSE;

	if (!resize_lower || !try_grow_lower(window, count)) {
		if (!try_grow_upper(window, count)) {
                        if (resize_lower || !try_grow_lower(window, count))
				return FALSE;
		}
	}

        return TRUE;
}


static void cmd_window_move_left(void)
{
	int refnum;

	refnum = window_refnum_left(active_win->refnum, TRUE);
	if (refnum != -1)
		window_set_refnum(active_win, refnum);
}


static void sig_complete_format(GList **list, WINDOW_REC *window,
				const char *word, const char *line, int *want_space)
{
	const char *ptr;
	int words;

	g_return_if_fail(list != NULL);
	g_return_if_fail(word != NULL);
	g_return_if_fail(line != NULL);

        ptr = line;

	words = 0;
	do {
                ptr++;

		words++;
                ptr = strchr(ptr, ' ');
	} while (ptr != NULL);

	if (words > 2)
		return;

	*list = completion_get_formats(line, word);
	if (*list != NULL) signal_stop();
}


char *theme_format_expand_get(THEME_REC *theme, const char **format)
{
	GString *str;
	char *ret;
	theme_rm_col dummy, reset;
	int braces = 1; /* we start with one brace opened */
	dummy.m[0] = '\0';
	strcpy(reset.m, "n");

	str = g_string_new(NULL);
	while (**format != '\0' && braces != 0) {
		if (**format == '{')
			braces++;
		else if (**format == '}')
			braces--;
		else if ((braces > 1) && (**format == ' ')) {
			g_string_append(str, "\\x20");
			(*format)++;
			continue;
		} else {
			theme_format_append_next(theme, str, format,
						 reset, reset,
						 &dummy, &dummy,
						 EXPAND_FLAG_IGNORE_REPLACES);
			continue;
		}

		if (braces == 0) {
			(*format)++;
			break;
		}

		g_string_append_c(str, **format);
		(*format)++;
	}

	ret = str->str;
        g_string_free(str, FALSE);
        return ret;
}


static inline int get_v4l2_input32(struct v4l2_input __user *kp,
				   struct v4l2_input32 __user *up)
{
	if (copy_in_user(kp, up, sizeof(*up)))
		return -EFAULT;
	return 0;
}


comics_generate_command_lines (ComicsDocument *comics_document,
			       GError         **error)
{
	gchar *quoted_file, *quoted_file_aux;
	gchar *quoted_command;
	ComicBookDecompressType type;

	type = comics_document->command_usage;
	comics_document->regex_arg = command_usage_def[type].regex_arg;
	quoted_command = g_shell_quote (comics_document->selected_command);
	if (comics_document->regex_arg) {
		quoted_file = comics_regex_quote (comics_document->archive);
		quoted_file_aux = g_shell_quote (comics_document->archive);
		comics_document->list_command =
			   g_strdup_printf (command_usage_def[type].list,
			                    comics_document->alternative_command,
			                    quoted_file_aux);
		g_free (quoted_file_aux);
	} else {
		quoted_file = g_shell_quote (comics_document->archive);
		comics_document->list_command =
				g_strdup_printf (command_usage_def[type].list,
				                 quoted_command, quoted_file);
	}
	comics_document->extract_command =
			    g_strdup_printf (command_usage_def[type].extract,
				             quoted_command);
	comics_document->offset = command_usage_def[type].offset;
	if (command_usage_def[type].decompress_tmp) {
		comics_document->dir = ev_mkdtemp ("atril-comics-XXXXXX", error);
                if (comics_document->dir == NULL)
                        return FALSE;

		/* unrar-free can't create directories, but ev_mkdtemp already created the dir */

		comics_document->decompress_tmp =
			g_strdup_printf (command_usage_def[type].decompress_tmp,
					 quoted_command, quoted_file,
					 comics_document->dir);
		g_free (quoted_file);
		g_free (quoted_command);

		if (!comics_decompress_temp_dir (comics_document->decompress_tmp,
		    comics_document->selected_command, error))
			return FALSE;
		else
			return TRUE;
	} else {
		g_free (quoted_file);
		g_free (quoted_command);
		return TRUE;
	}

}


comics_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
					   EvRenderContext      *rc,
					   gint                 *width,
					   gint                 *height)
{
	gdouble page_width, page_height;

	comics_document_get_page_size (EV_DOCUMENT (document), rc->page,
				       &page_width, &page_height);

	if (rc->rotation == 90 || rc->rotation == 270) {
		*width = (gint) (page_height * rc->scale);
		*height = (gint) (page_width * rc->scale);
	} else {
		*width = (gint) (page_width * rc->scale);
		*height = (gint) (page_height * rc->scale);
	}
}


comics_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
					  EvRenderContext      *rc,
					  gboolean              border)
{
	GdkPixbuf *thumbnail;

	thumbnail = comics_document_render_pixbuf (EV_DOCUMENT (document), rc);

	if (border) {
	      GdkPixbuf *tmp_pixbuf = thumbnail;

	      thumbnail = ev_document_misc_get_thumbnail_frame (-1, -1, tmp_pixbuf);
	      g_object_unref (tmp_pixbuf);
	}

	return thumbnail;
}


struct sctp_chunk *sctp_make_fwdtsn(const struct sctp_association *asoc,
				    __u32 new_cum_tsn, size_t nstreams,
				    struct sctp_fwdtsn_skip *skiplist)
{
	struct sctp_chunk *retval = NULL;
	struct sctp_fwdtsn_hdr ftsn_hdr;
	struct sctp_fwdtsn_skip skip;
	size_t hint;
	int i;

	hint = (nstreams + 1) * sizeof(__u32);

	retval = sctp_make_control(asoc, SCTP_CID_FWD_TSN, 0, hint, GFP_ATOMIC);

	if (!retval)
		return NULL;

	ftsn_hdr.new_cum_tsn = htonl(new_cum_tsn);
	retval->subh.fwdtsn_hdr =
		sctp_addto_chunk(retval, sizeof(ftsn_hdr), &ftsn_hdr);

	for (i = 0; i < nstreams; i++) {
		skip.stream = skiplist[i].stream;
		skip.ssn = skiplist[i].ssn;
		sctp_addto_chunk(retval, sizeof(skip), &skip);
	}

	return retval;
}


int sctp_verify_init(struct net *net, const struct sctp_endpoint *ep,
		     const struct sctp_association *asoc, enum sctp_cid cid,
		     struct sctp_init_chunk *peer_init,
		     struct sctp_chunk *chunk, struct sctp_chunk **errp)
{
	union sctp_params param;
	bool has_cookie = false;
	int result;

	/* Check for missing mandatory parameters. Note: Initial TSN is
	 * also mandatory, but is not checked here since the valid range
	 * is 0..2**32-1. RFC4960, section 3.3.3.
	 */
	if (peer_init->init_hdr.num_outbound_streams == 0 ||
	    peer_init->init_hdr.num_inbound_streams == 0 ||
	    peer_init->init_hdr.init_tag == 0 ||
	    ntohl(peer_init->init_hdr.a_rwnd) < SCTP_DEFAULT_MINWINDOW)
		return sctp_process_inv_mandatory(asoc, chunk, errp);

	sctp_walk_params(param, peer_init, init_hdr.params) {
		if (param.p->type == SCTP_PARAM_STATE_COOKIE)
			has_cookie = true;
	}

	/* There is a possibility that a parameter length was bad and
	 * in that case we would have stoped walking the parameters.
	 * The current param.p would point at the bad one.
	 * Current consensus on the mailing list is to generate a PROTOCOL
	 * VIOLATION error.  We build the ERROR chunk here and let the normal
	 * error handling code build and send the packet.
	 */
	if (param.v != (void *)chunk->chunk_end)
		return sctp_process_inv_paramlength(asoc, param.p, chunk, errp);

	/* The only missing mandatory param possible today is
	 * the state cookie for an INIT-ACK chunk.
	 */
	if ((SCTP_CID_INIT_ACK == cid) && !has_cookie)
		return sctp_process_missing_param(asoc, SCTP_PARAM_STATE_COOKIE,
						  chunk, errp);

	/* Verify all the variable length parameters */
	sctp_walk_params(param, peer_init, init_hdr.params) {
		result = sctp_verify_param(net, ep, asoc, param, cid,
					   chunk, errp);
		switch (result) {
		case SCTP_IERROR_ABORT:
		case SCTP_IERROR_NOMEM:
			return 0;
		case SCTP_IERROR_ERROR:
			return 1;
		case SCTP_IERROR_NO_ERROR:
		default:
			break;
		}

	} /* for (loop through all parameters) */

	return 1;
}


struct sctp_chunk *sctp_make_cookie_echo(const struct sctp_association *asoc,
					 const struct sctp_chunk *chunk)
{
	struct sctp_chunk *retval;
	int cookie_len;
	void *cookie;

	cookie = asoc->peer.cookie;
	cookie_len = asoc->peer.cookie_len;

	/* Build a cookie echo chunk.  */
	retval = sctp_make_control(asoc, SCTP_CID_COOKIE_ECHO, 0,
				   cookie_len, GFP_ATOMIC);
	if (!retval)
		goto nodata;
	retval->subh.cookie_hdr =
		sctp_addto_chunk(retval, cookie_len, cookie);

	/* RFC 2960 6.4 Multi-homed SCTP Endpoints
	 *
	 * An endpoint SHOULD transmit reply chunks (e.g., SACK,
	 * HEARTBEAT ACK, * etc.) to the same destination transport
	 * address from which it * received the DATA or control chunk
	 * to which it is replying.
	 *
	 * [COOKIE ECHO back to where the INIT ACK came from.]
	 */
	if (chunk)
		retval->transport = chunk->transport;

nodata:
	return retval;
}


int sctp_chunk_iif(const struct sctp_chunk *chunk)
{
	struct sk_buff *skb = chunk->skb;

	return SCTP_INPUT_CB(skb)->af->skb_iif(skb);
}


MagickExport const KernelProfileRecord *GetOpenCLKernelProfileRecords(
  const MagickCLDevice device,size_t *length)
{
  if ((device == (const MagickCLDevice) NULL) || (device->profile_records ==
      (KernelProfileRecord *) NULL))
  {
    if (length != (size_t *) NULL)
      *length=0;
    return((const KernelProfileRecord *) NULL);
  }
  if (length != (size_t *) NULL)
    {
      *length=0;
      LockSemaphoreInfo(device->lock);
      while (device->profile_records[*length] != (KernelProfileRecord) NULL)
        *length=*length+1;
      UnlockSemaphoreInfo(device->lock);
    }
  return(device->profile_records);
}


MagickExport MagickBooleanType DefineImageRegistry(const RegistryType type,
  const char *option,ExceptionInfo *exception)
{
  char
    key[MagickPathExtent],
    value[MagickPathExtent];

  register char
    *p;

  assert(option != (const char *) NULL);
  (void) CopyMagickString(key,option,MagickPathExtent);
  for (p=key; *p != '\0'; p++)
    if (*p == '=')
      break;
  *value='\0';
  if (*p == '=')
    (void) CopyMagickString(value,p+1,MagickPathExtent);
  *p='\0';
  return(SetImageRegistry(type,key,value,exception));
}


static MagickBooleanType LoadCachedOpenCLKernel(MagickCLDevice device,
  const char *filename)
{
  cl_int
    binaryStatus,
    status;

  ExceptionInfo
    *exception;

  size_t
    length;

  unsigned char
    *binaryProgram;

  exception=AcquireExceptionInfo();
  binaryProgram=(unsigned char *) FileToBlob(filename,~0UL,&length,exception);
  exception=DestroyExceptionInfo(exception);
  if (binaryProgram == (unsigned char *) NULL)
    return(MagickFalse);
  device->program=openCL_library->clCreateProgramWithBinary(device->context,1,
    &device->deviceID,&length,(const unsigned char**)&binaryProgram,
    &binaryStatus,&status);
  binaryProgram=(unsigned char *) RelinquishMagickMemory(binaryProgram);
  return((status != CL_SUCCESS) || (binaryStatus != CL_SUCCESS) ? MagickFalse :
    MagickTrue);
}


static MagickCLDevice RelinquishMagickCLDevice(MagickCLDevice device)
{
  if (device == (MagickCLDevice) NULL)
    return((MagickCLDevice) NULL);

  device->platform_name=RelinquishMagickMemory(device->platform_name);
  device->vendor_name=RelinquishMagickMemory(device->vendor_name);
  device->name=RelinquishMagickMemory(device->name);
  device->version=RelinquishMagickMemory(device->version);
  if (device->program != (cl_program) NULL)
    (void) openCL_library->clReleaseProgram(device->program);
  while (device->command_queues_index >= 0)
    (void) openCL_library->clReleaseCommandQueue(
      device->command_queues[device->command_queues_index--]);
  RelinquishSemaphoreInfo(&device->lock);
  return((MagickCLDevice) RelinquishMagickMemory(device));
}


static void CacheOpenCLBenchmarks(MagickCLEnv clEnv)
{
  char
    filename[MagickPathExtent];

  FILE
    *cache_file;

  MagickCLDevice
    device;

  size_t
    i,
    j;

  (void) FormatLocaleString(filename,MagickPathExtent,"%s%s%s",
    GetOpenCLCacheDirectory(),DirectorySeparator,
    IMAGEMAGICK_PROFILE_FILE);

  cache_file=fopen_utf8(filename,"wb");
  if (cache_file == (FILE *) NULL)
    return;
  fwrite("<devices>\n",sizeof(char),10,cache_file);
  fprintf(cache_file,"  <device name=\"CPU\" score=\"%.4g\"/>\n",
    clEnv->cpu_score);
  for (i = 0; i < clEnv->number_devices; i++)
  {
    MagickBooleanType
      duplicate;

    device=clEnv->devices[i];
    duplicate=MagickFalse;
    for (j = 0; j < i; j++)
    {
      if (IsSameOpenCLDevice(clEnv->devices[j],device))
      {
        duplicate=MagickTrue;
        break;
      }
    }

    if (duplicate)
      continue;

    if (device->score != MAGICKCORE_OPENCL_UNDEFINED_SCORE)
      fprintf(cache_file,"  <device platform=\"%s\" vendor=\"%s\" name=\"%s\"\
 version=\"%s\" maxClockFrequency=\"%d\" maxComputeUnits=\"%d\"\
 score=\"%.4g\"/>\n",
        device->platform_name,device->vendor_name,device->name,device->version,
        (int)device->max_clock_frequency,(int)device->max_compute_units,
        device->score);
  }
  fwrite("</devices>",sizeof(char),10,cache_file);

  fclose(cache_file);
}


static inline MagickBooleanType IsSameOpenCLDevice(MagickCLDevice a,
  MagickCLDevice b)
{
  if ((LocaleCompare(a->platform_name,b->platform_name) == 0) &&
      (LocaleCompare(a->vendor_name,b->vendor_name) == 0) &&
      (LocaleCompare(a->name,b->name) == 0) &&
      (LocaleCompare(a->version,b->version) == 0) &&
      (a->max_clock_frequency == b->max_clock_frequency) &&
      (a->max_compute_units == b->max_compute_units))
    return(MagickTrue);

  return(MagickFalse);
}


static void RegisterCacheEvent(MagickCLCacheInfo info,cl_event event)
{
  assert(info != (MagickCLCacheInfo) NULL);
  assert(event != (cl_event) NULL);
  LockSemaphoreInfo(info->events_semaphore);
  if (info->events == (cl_event *) NULL)
    {
      info->events=AcquireMagickMemory(sizeof(*info->events));
      info->event_count=1;
    }
  else
    info->events=ResizeQuantumMemory(info->events,++info->event_count,
      sizeof(*info->events));
  if (info->events == (cl_event *) NULL)
    ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
  info->events[info->event_count-1]=event;
  UnlockSemaphoreInfo(info->events_semaphore);
  openCL_library->clRetainEvent(event);
}


static void AutoSelectOpenCLDevices(MagickCLEnv clEnv)
{
  const char
    *option;

  double
    best_score;

  MagickBooleanType
    benchmark;

  size_t
    i;

  option=getenv("MAGICK_OCL_DEVICE");
  if (option != (const char *) NULL)
    {
      if (strcmp(option,"GPU") == 0)
        SelectOpenCLDevice(clEnv,CL_DEVICE_TYPE_GPU);
      else if (strcmp(option,"CPU") == 0)
        SelectOpenCLDevice(clEnv,CL_DEVICE_TYPE_CPU);
      else if (strcmp(option,"OFF") == 0)
        {
          for (i = 0; i < clEnv->number_devices; i++)
            clEnv->devices[i]->enabled=MagickFalse;
          clEnv->enabled=MagickFalse;
        }
    }

  if (LoadOpenCLBenchmarks(clEnv) == MagickFalse)
    return;

  benchmark=MagickFalse;
  if (clEnv->cpu_score == MAGICKCORE_OPENCL_UNDEFINED_SCORE)
    benchmark=MagickTrue;
  else
    {
      for (i = 0; i < clEnv->number_devices; i++)
      {
        if (clEnv->devices[i]->score == MAGICKCORE_OPENCL_UNDEFINED_SCORE)
        {
          benchmark=MagickTrue;
          break;
        }
      }
    }

  if (benchmark != MagickFalse)
    BenchmarkOpenCLDevices(clEnv);

  best_score=clEnv->cpu_score;
  for (i = 0; i < clEnv->number_devices; i++)
    best_score=MagickMin(clEnv->devices[i]->score,best_score);

  for (i = 0; i < clEnv->number_devices; i++)
  {
    if (clEnv->devices[i]->score != best_score)
      clEnv->devices[i]->enabled=MagickFalse;
  }
}


MagickExport double GetOpenCLDeviceBenchmarkScore(
  const MagickCLDevice device)
{
  if (device == (MagickCLDevice) NULL)
    return(MAGICKCORE_OPENCL_UNDEFINED_SCORE);
  return(device->score);
}


struct crypto_instance *crypto_init(
	const unsigned char *private_key,
	unsigned int private_key_len,
	const char *crypto_cipher_type,
	const char *crypto_hash_type,
	void (*log_printf_func) (
		int level,
		int subsys,
                const char *function,
                const char *file,
                int line,
                const char *format,
                ...)__attribute__((format(printf, 6, 7))),
	int log_level_security,
	int log_level_notice,
	int log_level_error,
	int log_subsys_id)
{
	struct crypto_instance *instance;
	instance = malloc(sizeof(*instance));
	if (instance == NULL) {
		return (NULL);
	}
	memset(instance, 0, sizeof(struct crypto_instance));

	memcpy(instance->private_key, private_key, private_key_len);
	instance->private_key_len = private_key_len;

	instance->crypto_cipher_type = string_to_crypto_cipher_type(crypto_cipher_type);
	instance->crypto_hash_type = string_to_crypto_hash_type(crypto_hash_type);

	instance->crypto_header_size = crypto_sec_header_size(crypto_cipher_type, crypto_hash_type);

	instance->log_printf_func = log_printf_func;
	instance->log_level_security = log_level_security;
	instance->log_level_notice = log_level_notice;
	instance->log_level_error = log_level_error;
	instance->log_subsys_id = log_subsys_id;

	if (init_nss(instance, crypto_cipher_type, crypto_hash_type) < 0) {
		free(instance);
		return(NULL);
	}

	return (instance);
}


    return filename_local;
  }

  // Implement a tic/toc mechanism to display elapsed time of algorithms.
  inline cimg_ulong tictoc(const bool is_tic) {
    cimg::mutex(2);
    static CImg<cimg_ulong> times(64);
    static unsigned int pos = 0;
    const cimg_ulong t1 = cimg::time();
    if (is_tic) {
      // Tic
      times[pos++] = t1;
      if (pos>=times._width)
        throw CImgArgumentException("cimg::tic(): Too much calls to 'cimg::tic()' without calls to 'cimg::toc()'.");
      cimg::mutex(2,0);
      return t1;
    }

    // Toc
    if (!pos)
      throw CImgArgumentException("cimg::toc(): No previous call to 'cimg::tic()' has been made.");
    const cimg_ulong
      t0 = times[--pos],
      dt = t1>=t0?(t1 - t0):cimg::type<cimg_ulong>::max();
    const unsigned int
      edays = (unsigned int)(dt/86400000.0),
      ehours = (unsigned int)((dt - edays*86400000.0)/3600000.0),
      emin = (unsigned int)((dt - edays*86400000.0 - ehours*3600000.0)/60000.0),
      esec = (unsigned int)((dt - edays*86400000.0 - ehours*3600000.0 - emin*60000.0)/1000.0),
      ems = (unsigned int)(dt - edays*86400000.0 - ehours*3600000.0 - emin*60000.0 - esec*1000.0);
    if (!edays && !ehours && !emin && !esec)
      std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u ms%s\n",
                   cimg::t_red,1 + 2*pos,"",ems,cimg::t_normal);
    else {
      if (!edays && !ehours && !emin)
        std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u sec %u ms%s\n",
                     cimg::t_red,1 + 2*pos,"",esec,ems,cimg::t_normal);
      else {
        if (!edays && !ehours)
          std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u min %u sec %u ms%s\n",
                       cimg::t_red,1 + 2*pos,"",emin,esec,ems,cimg::t_normal);
        else{
          if (!edays)
            std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u hours %u min %u sec %u ms%s\n",
                         cimg::t_red,1 + 2*pos,"",ehours,emin,esec,ems,cimg::t_normal);
          else{
            std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u days %u hours %u min %u sec %u ms%s\n",
                         cimg::t_red,1 + 2*pos,"",edays,ehours,emin,esec,ems,cimg::t_normal);
          }
        }
      }


    //! Convert pixel values from CMY to CMYK color spaces \newinstance.
    CImg<Tuchar> get_CMYtoCMYK() const {
      if (_spectrum!=3)
        throw CImgInstanceException(_cimg_instance
                                    "CMYtoCMYK(): Instance is not a CMY image.",
                                    cimg_instance);

      CImg<Tfloat> res(_width,_height,_depth,4);
      const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2);
      Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2), *pd4 = res.data(0,0,0,3);
      const ulongT whd = (ulongT)_width*_height*_depth;
      cimg_pragma_openmp(parallel for cimg_openmp_if(whd>=1024))
      for (ulongT N = 0; N<whd; ++N) {
        Tfloat
	  C = (Tfloat)ps1[N],
	  M = (Tfloat)ps2[N],
	  Y = (Tfloat)ps3[N],
	  K = cimg::min(C,M,Y);
	if (K>=255) C = M = Y = 0;
	else { const Tfloat K1 = 255 - K; C = 255*(C - K)/K1; M = 255*(M - K)/K1; Y = 255*(Y - K)/K1; }
        pd1[N] = (Tfloat)cimg::cut(C,0,255),
        pd2[N] = (Tfloat)cimg::cut(M,0,255),
        pd3[N] = (Tfloat)cimg::cut(Y,0,255),
        pd4[N] = (Tfloat)cimg::cut(K,0,255);
      }
      return res;


    //! Resize 3d object \newinstance.
    CImg<Tfloat> get_resize_object3d(const float sx, const float sy=-100, const float sz=-100) const {
      return CImg<Tfloat>(*this,false).resize_object3d(sx,sy,sz);


    METHODDEF(void) _cimg_jpeg_error_exit(j_common_ptr cinfo) {
      _cimg_error_ptr c_err = (_cimg_error_ptr) cinfo->err;  // Return control to the setjmp point
      (*cinfo->err->format_message)(cinfo,c_err->message);
      jpeg_destroy(cinfo);  // Clean memory and temp files.
      longjmp(c_err->setjmp_buffer,1);