HTMLtagproc1(struct parsed_tag *tag, struct html_feed_environ *h_env)
{
    char *p, *q, *r;
    int i, w, x, y, z, count, width;
    struct readbuffer *obuf = h_env->obuf;
    struct environment *envs = h_env->envs;
    Str tmp;
    int hseq;
    int cmd;
#ifdef ID_EXT
    char *id = NULL;
#endif				/* ID_EXT */

    cmd = tag->tagid;

    if (obuf->flag & RB_PRE) {
	switch (cmd) {
	case HTML_NOBR:
	case HTML_N_NOBR:
	case HTML_PRE_INT:
	case HTML_N_PRE_INT:
	    return 1;
	}
    }

    switch (cmd) {
    case HTML_B:
	obuf->in_bold++;
	if (obuf->in_bold > 1)
	    return 1;
	return 0;
    case HTML_N_B:
	if (obuf->in_bold == 1 && close_effect0(obuf, HTML_B))
	    obuf->in_bold = 0;
	if (obuf->in_bold > 0) {
	    obuf->in_bold--;
	    if (obuf->in_bold == 0)
		return 0;
	}
	return 1;
    case HTML_I:
	obuf->in_italic++;
	if (obuf->in_italic > 1)
	    return 1;
	return 0;
    case HTML_N_I:
	if (obuf->in_italic == 1 && close_effect0(obuf, HTML_I))
	    obuf->in_italic = 0;
	if (obuf->in_italic > 0) {
	    obuf->in_italic--;
	    if (obuf->in_italic == 0)
		return 0;
	}
	return 1;
    case HTML_U:
	obuf->in_under++;
	if (obuf->in_under > 1)
	    return 1;
	return 0;
    case HTML_N_U:
	if (obuf->in_under == 1 && close_effect0(obuf, HTML_U))
	    obuf->in_under = 0;
	if (obuf->in_under > 0) {
	    obuf->in_under--;
	    if (obuf->in_under == 0)
		return 0;
	}
	return 1;
    case HTML_EM:
	HTMLlineproc1("", h_env);
	return 1;
    case HTML_N_EM:
	HTMLlineproc1("", h_env);
	return 1;
    case HTML_STRONG:
	HTMLlineproc1("", h_env);
	return 1;
    case HTML_N_STRONG:
	HTMLlineproc1("", h_env);
	return 1;
    case HTML_Q:
	HTMLlineproc1("`", h_env);
	return 1;
    case HTML_N_Q:
	HTMLlineproc1("'", h_env);
	return 1;
    case HTML_P:
    case HTML_N_P:
	CLOSE_A;
	if (!(obuf->flag & RB_IGNORE_P)) {
	    flushline(h_env, obuf, envs[h_env->envc].indent, 1, h_env->limit);
	    do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
			 h_env->limit);
	}
	obuf->flag |= RB_IGNORE_P;
	if (cmd == HTML_P) {
	    set_alignment(obuf, tag);
	    obuf->flag |= RB_P;
	}
	return 1;
    case HTML_BR:
	flushline(h_env, obuf, envs[h_env->envc].indent, 1, h_env->limit);
	h_env->blank_lines = 0;
	return 1;
    case HTML_H:
	if (!(obuf->flag & (RB_PREMODE | RB_IGNORE_P))) {
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	    do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
			 h_env->limit);
	}
	HTMLlineproc1("", h_env);
	set_alignment(obuf, tag);
	return 1;
    case HTML_N_H:
	HTMLlineproc1("", h_env);
	if (!(obuf->flag & RB_PREMODE)) {
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	}
	do_blankline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	RB_RESTORE_FLAG(obuf);
	close_anchor(h_env, obuf);
	obuf->flag |= RB_IGNORE_P;
	return 1;
    case HTML_UL:
    case HTML_OL:
    case HTML_BLQ:
	CLOSE_A;
	if (!(obuf->flag & RB_IGNORE_P)) {
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	    if (!(obuf->flag & RB_PREMODE) &&
		(h_env->envc == 0 || cmd == HTML_BLQ))
		do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
			     h_env->limit);
	}
	PUSH_ENV(cmd);
	if (cmd == HTML_UL || cmd == HTML_OL) {
	    if (parsedtag_get_value(tag, ATTR_START, &count)) {
		envs[h_env->envc].count = count - 1;
	    }
	}
	if (cmd == HTML_OL) {
	    envs[h_env->envc].type = '1';
	    if (parsedtag_get_value(tag, ATTR_TYPE, &p)) {
		envs[h_env->envc].type = (int)*p;
	    }
	}
	if (cmd == HTML_UL)
	    envs[h_env->envc].type = ul_type(tag, 0);
	flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	return 1;
    case HTML_N_UL:
    case HTML_N_OL:
    case HTML_N_DL:
    case HTML_N_BLQ:
	CLOSE_DT;
	CLOSE_A;
	if (h_env->envc > 0) {
	    flushline(h_env, obuf, envs[h_env->envc - 1].indent, 0,
		      h_env->limit);
	    POP_ENV;
	    if (!(obuf->flag & RB_PREMODE) &&
		(h_env->envc == 0 || cmd == HTML_N_DL || cmd == HTML_N_BLQ)) {
		do_blankline(h_env, obuf,
			     envs[h_env->envc].indent,
			     INDENT_INCR, h_env->limit);
		obuf->flag |= RB_IGNORE_P;
	    }
	}
	close_anchor(h_env, obuf);
	return 1;
    case HTML_DL:
	CLOSE_A;
	if (!(obuf->flag & RB_IGNORE_P)) {
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	    if (!(obuf->flag & RB_PREMODE))
		do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
			     h_env->limit);
	}
	PUSH_ENV(cmd);
	if (parsedtag_exists(tag, ATTR_COMPACT))
	    envs[h_env->envc].env = HTML_DL_COMPACT;
	obuf->flag |= RB_IGNORE_P;
	return 1;
    case HTML_LI:
	CLOSE_A;
	CLOSE_DT;
	if (h_env->envc > 0) {
	    Str num;
	    flushline(h_env, obuf,
		      envs[h_env->envc - 1].indent, 0, h_env->limit);
	    envs[h_env->envc].count++;
	    if (parsedtag_get_value(tag, ATTR_VALUE, &p)) {
		count = atoi(p);
		if (count > 0)
		    envs[h_env->envc].count = count;
		else
		    envs[h_env->envc].count = 0;
	    }
	    switch (envs[h_env->envc].env) {
	    case HTML_UL:
		envs[h_env->envc].type = ul_type(tag, envs[h_env->envc].type);
		for (i = 0; i < INDENT_INCR - 3; i++)
		    push_charp(obuf, 1, NBSP, PC_ASCII);
		tmp = Strnew();
		switch (envs[h_env->envc].type) {
		case 'd':
		    push_symbol(tmp, UL_SYMBOL_DISC, symbol_width, 1);
		    break;
		case 'c':
		    push_symbol(tmp, UL_SYMBOL_CIRCLE, symbol_width, 1);
		    break;
		case 's':
		    push_symbol(tmp, UL_SYMBOL_SQUARE, symbol_width, 1);
		    break;
		default:
		    push_symbol(tmp,
				UL_SYMBOL((h_env->envc_real -
					   1) % MAX_UL_LEVEL), symbol_width,
				1);
		    break;
		}
		if (symbol_width == 1)
		    push_charp(obuf, 1, NBSP, PC_ASCII);
		push_str(obuf, symbol_width, tmp, PC_ASCII);
		push_charp(obuf, 1, NBSP, PC_ASCII);
		set_space_to_prevchar(obuf->prevchar);
		break;
	    case HTML_OL:
		if (parsedtag_get_value(tag, ATTR_TYPE, &p))
		    envs[h_env->envc].type = (int)*p;
		switch ((envs[h_env->envc].count > 0)? envs[h_env->envc].type: '1') {
		case 'i':
		    num = romanNumeral(envs[h_env->envc].count);
		    break;
		case 'I':
		    num = romanNumeral(envs[h_env->envc].count);
		    Strupper(num);
		    break;
		case 'a':
		    num = romanAlphabet(envs[h_env->envc].count);
		    break;
		case 'A':
		    num = romanAlphabet(envs[h_env->envc].count);
		    Strupper(num);
		    break;
		default:
		    num = Sprintf("%d", envs[h_env->envc].count);
		    break;
		}
		if (INDENT_INCR >= 4)
		    Strcat_charp(num, ". ");
		else
		    Strcat_char(num, '.');
		push_spaces(obuf, 1, INDENT_INCR - num->length);
		push_str(obuf, num->length, num, PC_ASCII);
		if (INDENT_INCR >= 4)
		    set_space_to_prevchar(obuf->prevchar);
		break;
	    default:
		push_spaces(obuf, 1, INDENT_INCR);
		break;
	    }
	}
	else {
	    flushline(h_env, obuf, 0, 0, h_env->limit);
	}
	obuf->flag |= RB_IGNORE_P;
	return 1;
    case HTML_DT:
	CLOSE_A;
	if (h_env->envc == 0 ||
	    (h_env->envc_real < h_env->nenv &&
	     envs[h_env->envc].env != HTML_DL &&
	     envs[h_env->envc].env != HTML_DL_COMPACT)) {
	    PUSH_ENV(HTML_DL);
	}
	if (h_env->envc > 0) {
	    flushline(h_env, obuf,
		      envs[h_env->envc - 1].indent, 0, h_env->limit);
	}
	if (!(obuf->flag & RB_IN_DT)) {
	    HTMLlineproc1("", h_env);
	    obuf->flag |= RB_IN_DT;
	}
	obuf->flag |= RB_IGNORE_P;
	return 1;
    case HTML_DD:
	CLOSE_A;
	CLOSE_DT;
	if (envs[h_env->envc].env == HTML_DL_COMPACT) {
	    if (obuf->pos > envs[h_env->envc].indent)
		flushline(h_env, obuf, envs[h_env->envc].indent, 0,
			  h_env->limit);
	    else
		push_spaces(obuf, 1, envs[h_env->envc].indent - obuf->pos);
	}
	else
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	/* obuf->flag |= RB_IGNORE_P; */
	return 1;
    case HTML_TITLE:
	close_anchor(h_env, obuf);
	process_title(tag);
	obuf->flag |= RB_TITLE;
	obuf->end_tag = HTML_N_TITLE;
	return 1;
    case HTML_N_TITLE:
	if (!(obuf->flag & RB_TITLE))
	    return 1;
	obuf->flag &= ~RB_TITLE;
	obuf->end_tag = 0;
	tmp = process_n_title(tag);
	if (tmp)
	    HTMLlineproc1(tmp->ptr, h_env);
	return 1;
    case HTML_TITLE_ALT:
	if (parsedtag_get_value(tag, ATTR_TITLE, &p))
	    h_env->title = html_unquote(p);
	return 0;
    case HTML_FRAMESET:
	PUSH_ENV(cmd);
	push_charp(obuf, 9, "--FRAME--", PC_ASCII);
	flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	return 0;
    case HTML_N_FRAMESET:
	if (h_env->envc > 0) {
	    POP_ENV;
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	}
	return 0;
    case HTML_NOFRAMES:
	CLOSE_A;
	flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	obuf->flag |= (RB_NOFRAMES | RB_IGNORE_P);
	/* istr = str; */
	return 1;
    case HTML_N_NOFRAMES:
	CLOSE_A;
	flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	obuf->flag &= ~RB_NOFRAMES;
	return 1;
    case HTML_FRAME:
	q = r = NULL;
	parsedtag_get_value(tag, ATTR_SRC, &q);
	parsedtag_get_value(tag, ATTR_NAME, &r);
	if (q) {
	    q = html_quote(q);
	    push_tag(obuf, Sprintf("",
				   cur_hseq++, q)->ptr, HTML_A);
	    if (r)
		q = html_quote(r);
	    push_charp(obuf, get_strwidth(q), q, PC_ASCII);
	    push_tag(obuf, "", HTML_N_A);
	}
	flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	return 0;
    case HTML_HR:
	close_anchor(h_env, obuf);
	tmp = process_hr(tag, h_env->limit, envs[h_env->envc].indent);
	HTMLlineproc1(tmp->ptr, h_env);
	set_space_to_prevchar(obuf->prevchar);
	return 1;
    case HTML_PRE:
	x = parsedtag_exists(tag, ATTR_FOR_TABLE);
	CLOSE_A;
	if (!(obuf->flag & RB_IGNORE_P)) {
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	    if (!x)
		do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
			     h_env->limit);
	}
	else
	    fillline(obuf, envs[h_env->envc].indent);
	obuf->flag |= (RB_PRE | RB_IGNORE_P);
	/* istr = str; */
	return 1;
    case HTML_N_PRE:
	flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	if (!(obuf->flag & RB_IGNORE_P)) {
	    do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
			 h_env->limit);
	    obuf->flag |= RB_IGNORE_P;
	    h_env->blank_lines++;
	}
	obuf->flag &= ~RB_PRE;
	close_anchor(h_env, obuf);
	return 1;
    case HTML_PRE_INT:
	i = obuf->line->length;
	append_tags(obuf);
	if (!(obuf->flag & RB_SPECIAL)) {
	    set_breakpoint(obuf, obuf->line->length - i);
	}
	obuf->flag |= RB_PRE_INT;
	return 0;
    case HTML_N_PRE_INT:
	push_tag(obuf, "</pre_int>", HTML_N_PRE_INT);
	obuf->flag &= ~RB_PRE_INT;
	if (!(obuf->flag & RB_SPECIAL) && obuf->pos > obuf->bp.pos) {
	    set_prevchar(obuf->prevchar, "", 0);
	    obuf->prev_ctype = PC_CTRL;
	}
	return 1;
    case HTML_NOBR:
	obuf->flag |= RB_NOBR;
	obuf->nobr_level++;
	return 0;
    case HTML_N_NOBR:
	if (obuf->nobr_level > 0)
	    obuf->nobr_level--;
	if (obuf->nobr_level == 0)
	    obuf->flag &= ~RB_NOBR;
	return 0;
    case HTML_PRE_PLAIN:
	CLOSE_A;
	if (!(obuf->flag & RB_IGNORE_P)) {
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	    do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
			 h_env->limit);
	}
	obuf->flag |= (RB_PRE | RB_IGNORE_P);
	return 1;
    case HTML_N_PRE_PLAIN:
	CLOSE_A;
	if (!(obuf->flag & RB_IGNORE_P)) {
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	    do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
			 h_env->limit);
	    obuf->flag |= RB_IGNORE_P;
	}
	obuf->flag &= ~RB_PRE;
	return 1;
    case HTML_LISTING:
    case HTML_XMP:
    case HTML_PLAINTEXT:
	CLOSE_A;
	if (!(obuf->flag & RB_IGNORE_P)) {
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	    do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
			 h_env->limit);
	}
	obuf->flag |= (RB_PLAIN | RB_IGNORE_P);
	switch (cmd) {
	case HTML_LISTING:
	    obuf->end_tag = HTML_N_LISTING;
	    break;
	case HTML_XMP:
	    obuf->end_tag = HTML_N_XMP;
	    break;
	case HTML_PLAINTEXT:
	    obuf->end_tag = MAX_HTMLTAG;
	    break;
	}
	return 1;
    case HTML_N_LISTING:
    case HTML_N_XMP:
	CLOSE_A;
	if (!(obuf->flag & RB_IGNORE_P)) {
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	    do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
			 h_env->limit);
	    obuf->flag |= RB_IGNORE_P;
	}
	obuf->flag &= ~RB_PLAIN;
	obuf->end_tag = 0;
	return 1;
    case HTML_SCRIPT:
	obuf->flag |= RB_SCRIPT;
	obuf->end_tag = HTML_N_SCRIPT;
	return 1;
    case HTML_STYLE:
	obuf->flag |= RB_STYLE;
	obuf->end_tag = HTML_N_STYLE;
	return 1;
    case HTML_N_SCRIPT:
	obuf->flag &= ~RB_SCRIPT;
	obuf->end_tag = 0;
	return 1;
    case HTML_N_STYLE:
	obuf->flag &= ~RB_STYLE;
	obuf->end_tag = 0;
	return 1;
    case HTML_A:
	if (obuf->anchor.url)
	    close_anchor(h_env, obuf);

	hseq = 0;

	if (parsedtag_get_value(tag, ATTR_HREF, &p))
	    obuf->anchor.url = Strnew_charp(p)->ptr;
	if (parsedtag_get_value(tag, ATTR_TARGET, &p))
	    obuf->anchor.target = Strnew_charp(p)->ptr;
	if (parsedtag_get_value(tag, ATTR_REFERER, &p))
	    obuf->anchor.referer = Strnew_charp(p)->ptr;
	if (parsedtag_get_value(tag, ATTR_TITLE, &p))
	    obuf->anchor.title = Strnew_charp(p)->ptr;
	if (parsedtag_get_value(tag, ATTR_ACCESSKEY, &p))
	    obuf->anchor.accesskey = (unsigned char)*p;
	if (parsedtag_get_value(tag, ATTR_HSEQ, &hseq))
	    obuf->anchor.hseq = hseq;

	if (hseq == 0 && obuf->anchor.url) {
	    obuf->anchor.hseq = cur_hseq;
	    tmp = process_anchor(tag, h_env->tagbuf->ptr);
	    push_tag(obuf, tmp->ptr, HTML_A);
	    if (displayLinkNumber)
		HTMLlineproc1(getLinkNumberStr(-1)->ptr, h_env);
	    return 1;
	}
	return 0;
    case HTML_N_A:
	close_anchor(h_env, obuf);
	return 1;
    case HTML_IMG:
	tmp = process_img(tag, h_env->limit);
	HTMLlineproc1(tmp->ptr, h_env);
	return 1;
    case HTML_IMG_ALT:
	if (parsedtag_get_value(tag, ATTR_SRC, &p))
	    obuf->img_alt = Strnew_charp(p);
#ifdef USE_IMAGE
	i = 0;
	if (parsedtag_get_value(tag, ATTR_TOP_MARGIN, &i)) {
	    if (i > obuf->top_margin)
		obuf->top_margin = i;
	}
	i = 0;
	if (parsedtag_get_value(tag, ATTR_BOTTOM_MARGIN, &i)) {
	    if (i > obuf->bottom_margin)
		obuf->bottom_margin = i;
	}
#endif
	return 0;
    case HTML_N_IMG_ALT:
	if (obuf->img_alt) {
	    if (!close_effect0(obuf, HTML_IMG_ALT))
		push_tag(obuf, "</img_alt>", HTML_N_IMG_ALT);
	    obuf->img_alt = NULL;
	}
	return 1;
    case HTML_INPUT_ALT:
	i = 0;
	if (parsedtag_get_value(tag, ATTR_TOP_MARGIN, &i)) {
	    if (i > obuf->top_margin)
		obuf->top_margin = i;
	}
	i = 0;
	if (parsedtag_get_value(tag, ATTR_BOTTOM_MARGIN, &i)) {
	    if (i > obuf->bottom_margin)
		obuf->bottom_margin = i;
	}
	if (parsedtag_get_value(tag, ATTR_HSEQ, &hseq)) {
	    obuf->input_alt.hseq = hseq;
	}
	if (parsedtag_get_value(tag, ATTR_FID, &i)) {
	    obuf->input_alt.fid = i;
	}
	if (parsedtag_get_value(tag, ATTR_TYPE, &p)) {
	    obuf->input_alt.type = Strnew_charp(p);
	}
	if (parsedtag_get_value(tag, ATTR_VALUE, &p)) {
	    obuf->input_alt.value = Strnew_charp(p);
	}
	if (parsedtag_get_value(tag, ATTR_NAME, &p)) {
	    obuf->input_alt.name = Strnew_charp(p);
	}
	obuf->input_alt.in = 1;
	return 0;
    case HTML_N_INPUT_ALT:
	if (obuf->input_alt.in) {
	    if (!close_effect0(obuf, HTML_INPUT_ALT))
		push_tag(obuf, "</input_alt>", HTML_N_INPUT_ALT);
	    obuf->input_alt.hseq = 0;
	    obuf->input_alt.fid = -1;
	    obuf->input_alt.in = 0;
	    obuf->input_alt.type = NULL;
	    obuf->input_alt.name = NULL;
	    obuf->input_alt.value = NULL;
	}
	return 1;
    case HTML_TABLE:
	close_anchor(h_env, obuf);
	obuf->table_level++;
	if (obuf->table_level >= MAX_TABLE)
	    break;
	w = BORDER_NONE;
	/* x: cellspacing, y: cellpadding */
	x = 2;
	y = 1;
	z = 0;
	width = 0;
	if (parsedtag_exists(tag, ATTR_BORDER)) {
	    if (parsedtag_get_value(tag, ATTR_BORDER, &w)) {
		if (w > 2)
		    w = BORDER_THICK;
		else if (w < 0) {	/* weird */
		    w = BORDER_THIN;
		}
	    }
	    else
		w = BORDER_THIN;
	}
	if (DisplayBorders && w == BORDER_NONE)
	    w = BORDER_THIN;
	if (parsedtag_get_value(tag, ATTR_WIDTH, &i)) {
	    if (obuf->table_level == 0)
		width = REAL_WIDTH(i, h_env->limit - envs[h_env->envc].indent);
	    else
		width = RELATIVE_WIDTH(i);
	}
	if (parsedtag_exists(tag, ATTR_HBORDER))
	    w = BORDER_NOWIN;
	parsedtag_get_value(tag, ATTR_CELLSPACING, &x);
	parsedtag_get_value(tag, ATTR_CELLPADDING, &y);
	parsedtag_get_value(tag, ATTR_VSPACE, &z);
#ifdef ID_EXT
	parsedtag_get_value(tag, ATTR_ID, &id);
#endif				/* ID_EXT */
	tables[obuf->table_level] = begin_table(w, x, y, z);
#ifdef ID_EXT
	if (id != NULL)
	    tables[obuf->table_level]->id = Strnew_charp(id);
#endif				/* ID_EXT */
	table_mode[obuf->table_level].pre_mode = 0;
	table_mode[obuf->table_level].indent_level = 0;
	table_mode[obuf->table_level].nobr_level = 0;
	table_mode[obuf->table_level].caption = 0;
	table_mode[obuf->table_level].end_tag = 0;	/* HTML_UNKNOWN */
#ifndef TABLE_EXPAND
	tables[obuf->table_level]->total_width = width;
#else
	tables[obuf->table_level]->real_width = width;
	tables[obuf->table_level]->total_width = 0;
#endif
	return 1;
    case HTML_N_TABLE:
	/* should be processed in HTMLlineproc() */
	return 1;
    case HTML_CENTER:
	CLOSE_A;
	if (!(obuf->flag & (RB_PREMODE | RB_IGNORE_P)))
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	RB_SAVE_FLAG(obuf);
	RB_SET_ALIGN(obuf, RB_CENTER);
	return 1;
    case HTML_N_CENTER:
	CLOSE_A;
	if (!(obuf->flag & RB_PREMODE))
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	RB_RESTORE_FLAG(obuf);
	return 1;
    case HTML_DIV:
	CLOSE_A;
	if (!(obuf->flag & RB_IGNORE_P))
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	set_alignment(obuf, tag);
	return 1;
    case HTML_N_DIV:
	CLOSE_A;
	flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	RB_RESTORE_FLAG(obuf);
	return 1;
    case HTML_DIV_INT:
	CLOSE_P;
	if (!(obuf->flag & RB_IGNORE_P))
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	set_alignment(obuf, tag);
	return 1;
    case HTML_N_DIV_INT:
	CLOSE_P;
	flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	RB_RESTORE_FLAG(obuf);
	return 1;
    case HTML_FORM:
	CLOSE_A;
	if (!(obuf->flag & RB_IGNORE_P))
	    flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	tmp = process_form(tag);
	if (tmp)
	    HTMLlineproc1(tmp->ptr, h_env);
	return 1;
    case HTML_N_FORM:
	CLOSE_A;
	flushline(h_env, obuf, envs[h_env->envc].indent, 0, h_env->limit);
	obuf->flag |= RB_IGNORE_P;
	process_n_form();
	return 1;
    case HTML_INPUT:
	close_anchor(h_env, obuf);
	tmp = process_input(tag);
       if (tmp)
           HTMLlineproc1(tmp->ptr, h_env);
       return 1;
    case HTML_BUTTON:
       tmp = process_button(tag);
       if (tmp)
           HTMLlineproc1(tmp->ptr, h_env);
       return 1;
    case HTML_N_BUTTON:
       tmp = process_n_button();
	if (tmp)
	    HTMLlineproc1(tmp->ptr, h_env);
	return 1;
    case HTML_SELECT:
	close_anchor(h_env, obuf);
	tmp = process_select(tag);
	if (tmp)
	    HTMLlineproc1(tmp->ptr, h_env);
	obuf->flag |= RB_INSELECT;
	obuf->end_tag = HTML_N_SELECT;
	return 1;
    case HTML_N_SELECT:
	obuf->flag &= ~RB_INSELECT;
	obuf->end_tag = 0;
	tmp = process_n_select();
	if (tmp)
	    HTMLlineproc1(tmp->ptr, h_env);
	return 1;
    case HTML_OPTION:
	/* nothing */
	return 1;
    case HTML_TEXTAREA:
	close_anchor(h_env, obuf);
	tmp = process_textarea(tag, h_env->limit);
	if (tmp)
	    HTMLlineproc1(tmp->ptr, h_env);
	obuf->flag |= RB_INTXTA;
	obuf->end_tag = HTML_N_TEXTAREA;
	return 1;
    case HTML_N_TEXTAREA:
	obuf->flag &= ~RB_INTXTA;
	obuf->end_tag = 0;
	tmp = process_n_textarea();
	if (tmp)
	    HTMLlineproc1(tmp->ptr, h_env);
	return 1;
    case HTML_ISINDEX:
	p = "";
	q = "!CURRENT_URL!";
	parsedtag_get_value(tag, ATTR_PROMPT, &p);
	parsedtag_get_value(tag, ATTR_ACTION, &q);
	tmp = Strnew_m_charp("<form method=get action=\"",
			     html_quote(q),
			     "\">",
			     html_quote(p),
			     "<input type=text name=\"\" accept></form>",
			     NULL);
	HTMLlineproc1(tmp->ptr, h_env);
	return 1;
    case HTML_META:
	p = q = r = NULL;
	parsedtag_get_value(tag, ATTR_HTTP_EQUIV, &p);
	parsedtag_get_value(tag, ATTR_CONTENT, &q);
#ifdef USE_M17N
	parsedtag_get_value(tag, ATTR_CHARSET, &r);
	if (r) {
	    /* <meta charset=""> */
	    SKIP_BLANKS(r);
	    meta_charset = wc_guess_charset(r, 0);
	}
	else
	if (p && q && !strcasecmp(p, "Content-Type") &&
	    (q = strcasestr(q, "charset")) != NULL) {
	    q += 7;
	    SKIP_BLANKS(q);
	    if (*q == '=') {
		q++;
		SKIP_BLANKS(q);
		meta_charset = wc_guess_charset(q, 0);
	    }
	}
	else
#endif
	if (p && q && !strcasecmp(p, "refresh")) {
	    int refresh_interval;
	    tmp = NULL;
	    refresh_interval = getMetaRefreshParam(q, &tmp);
	    if (tmp) {
		q = html_quote(tmp->ptr);
		tmp = Sprintf("Refresh (%d sec) %s",
			      refresh_interval, q, q);
	    }
	    else if (refresh_interval > 0)
		tmp = Sprintf("Refresh (%d sec)", refresh_interval);
	    if (tmp) {
		HTMLlineproc1(tmp->ptr, h_env);
		do_blankline(h_env, obuf, envs[h_env->envc].indent, 0,
			     h_env->limit);
		if (!is_redisplay &&
		    !((obuf->flag & RB_NOFRAMES) && RenderFrame)) {
		    tag->need_reconstruct = TRUE;
		    return 0;
		}
	    }
	}
	return 1;
    case HTML_BASE:
#if defined(USE_M17N) || defined(USE_IMAGE)
	p = NULL;
	if (parsedtag_get_value(tag, ATTR_HREF, &p)) {
	    cur_baseURL = New(ParsedURL);
	    parseURL(p, cur_baseURL, NULL);
	}
#endif
    case HTML_MAP:
    case HTML_N_MAP:
    case HTML_AREA:
	return 0;
    case HTML_DEL:
	switch (displayInsDel) {
	case DISPLAY_INS_DEL_SIMPLE:
	    obuf->flag |= RB_DEL;
	    break;
	case DISPLAY_INS_DEL_NORMAL:
	    HTMLlineproc1("[DEL:", h_env);
	    break;
	case DISPLAY_INS_DEL_FONTIFY:
	    obuf->in_strike++;
	    if (obuf->in_strike == 1) {
		push_tag(obuf, "<s>", HTML_S);
	    }
	    break;
	}
	return 1;
    case HTML_N_DEL:
	switch (displayInsDel) {
	case DISPLAY_INS_DEL_SIMPLE:
	    obuf->flag &= ~RB_DEL;
	    break;
	case DISPLAY_INS_DEL_NORMAL:
	    HTMLlineproc1(":DEL]", h_env);
	case DISPLAY_INS_DEL_FONTIFY:
	    if (obuf->in_strike == 0)
		return 1;
	    if (obuf->in_strike == 1 && close_effect0(obuf, HTML_S))
		obuf->in_strike = 0;
	    if (obuf->in_strike > 0) {
		obuf->in_strike--;
		if (obuf->in_strike == 0) {
		    push_tag(obuf, "</s>", HTML_N_S);
		}
	    }
	    break;
	}
	return 1;
    case HTML_S:
	switch (displayInsDel) {
	case DISPLAY_INS_DEL_SIMPLE:
	    obuf->flag |= RB_S;
	    break;
	case DISPLAY_INS_DEL_NORMAL:
	    HTMLlineproc1("[S:", h_env);
	    break;
	case DISPLAY_INS_DEL_FONTIFY:
	    obuf->in_strike++;
	    if (obuf->in_strike == 1) {
		push_tag(obuf, "<s>", HTML_S);
	    }
	    break;
	}
	return 1;
    case HTML_N_S:
	switch (displayInsDel) {
	case DISPLAY_INS_DEL_SIMPLE:
	    obuf->flag &= ~RB_S;
	    break;
	case DISPLAY_INS_DEL_NORMAL:
	    HTMLlineproc1(":S]", h_env);
	    break;
	case DISPLAY_INS_DEL_FONTIFY:
	    if (obuf->in_strike == 0)
		return 1;
	    if (obuf->in_strike == 1 && close_effect0(obuf, HTML_S))
		obuf->in_strike = 0;
	    if (obuf->in_strike > 0) {
		obuf->in_strike--;
		if (obuf->in_strike == 0) {
		    push_tag(obuf, "</s>", HTML_N_S);
		}
	    }
	}
	return 1;
    case HTML_INS:
	switch (displayInsDel) {
	case DISPLAY_INS_DEL_SIMPLE:
	    break;
	case DISPLAY_INS_DEL_NORMAL:
	    HTMLlineproc1("[INS:", h_env);
	    break;
	case DISPLAY_INS_DEL_FONTIFY:
	    obuf->in_ins++;
	    if (obuf->in_ins == 1) {
		push_tag(obuf, "", HTML_INS);
	    }
	    break;
	}
	return 1;
    case HTML_N_INS:
	switch (displayInsDel) {
	case DISPLAY_INS_DEL_SIMPLE:
	    break;
	case DISPLAY_INS_DEL_NORMAL:
	    HTMLlineproc1(":INS]", h_env);
	    break;
	case DISPLAY_INS_DEL_FONTIFY:
	    if (obuf->in_ins == 0)
		return 1;
	    if (obuf->in_ins == 1 && close_effect0(obuf, HTML_INS))
		obuf->in_ins = 0;
	    if (obuf->in_ins > 0) {
		obuf->in_ins--;
		if (obuf->in_ins == 0) {
		    push_tag(obuf, "", HTML_N_INS);
		}
	    }
	    break;
	}
	return 1;
    case HTML_SUP:
	if (!(obuf->flag & (RB_DEL | RB_S)))
	    HTMLlineproc1("^", h_env);
	return 1;
    case HTML_N_SUP:
	return 1;
    case HTML_SUB:
	if (!(obuf->flag & (RB_DEL | RB_S)))
	    HTMLlineproc1("[", h_env);
	return 1;
    case HTML_N_SUB:
	if (!(obuf->flag & (RB_DEL | RB_S)))
	    HTMLlineproc1("]", h_env);
	return 1;
    case HTML_FONT:
    case HTML_N_FONT:
    case HTML_NOP:
	return 1;
    case HTML_BGSOUND:
	if (view_unseenobject) {
	    if (parsedtag_get_value(tag, ATTR_SRC, &p)) {
		Str s;
		q = html_quote(p);
		s = Sprintf("bgsound(%s)", q, q);
		HTMLlineproc1(s->ptr, h_env);
	    }
	}
	return 1;
    case HTML_EMBED:
	if (view_unseenobject) {
	    if (parsedtag_get_value(tag, ATTR_SRC, &p)) {
		Str s;
		q = html_quote(p);
		s = Sprintf("embed(%s)", q, q);
		HTMLlineproc1(s->ptr, h_env);
	    }
	}
	return 1;
    case HTML_APPLET:
	if (view_unseenobject) {
	    if (parsedtag_get_value(tag, ATTR_ARCHIVE, &p)) {
		Str s;
		q = html_quote(p);
		s = Sprintf("applet archive(%s)", q, q);
		HTMLlineproc1(s->ptr, h_env);
	    }
	}
	return 1;
    case HTML_BODY:
	if (view_unseenobject) {
	    if (parsedtag_get_value(tag, ATTR_BACKGROUND, &p)) {
		Str s;
		q = html_quote(p);
		s = Sprintf("
", q, q);
		HTMLlineproc1(s->ptr, h_env);
	    }
	}
    case HTML_N_HEAD:
	if (obuf->flag & RB_TITLE)
	    HTMLlineproc1("</title>", h_env);
    case HTML_HEAD:
    case HTML_N_BODY:
	return 1;
    default:
	/* obuf->prevchar = '\0'; */
	return 0;
    }
    /* not reached */
    return 0;
}


formUpdateBuffer(Anchor *a, Buffer *buf, FormItemList *form)
{
    Buffer save;
    char *p;
    int spos, epos, rows, c_rows, pos, col = 0;
    Line *l;

    copyBuffer(&save, buf);
    gotoLine(buf, a->start.line);
    switch (form->type) {
    case FORM_TEXTAREA:
    case FORM_INPUT_TEXT:
    case FORM_INPUT_FILE:
    case FORM_INPUT_PASSWORD:
    case FORM_INPUT_CHECKBOX:
    case FORM_INPUT_RADIO:
#ifdef MENU_SELECT
    case FORM_SELECT:
#endif				/* MENU_SELECT */
	spos = a->start.pos;
	epos = a->end.pos;
	break;
    default:
	spos = a->start.pos + 1;
	epos = a->end.pos - 1;
    }
    switch (form->type) {
    case FORM_INPUT_CHECKBOX:
    case FORM_INPUT_RADIO:
	if (buf->currentLine == NULL ||
	    spos >= buf->currentLine->len || spos < 0)
	    break;
	if (form->checked)
	    buf->currentLine->lineBuf[spos] = '*';
	else
	    buf->currentLine->lineBuf[spos] = ' ';
	break;
    case FORM_INPUT_TEXT:
    case FORM_INPUT_FILE:
    case FORM_INPUT_PASSWORD:
    case FORM_TEXTAREA:
#ifdef MENU_SELECT
    case FORM_SELECT:
	if (form->type == FORM_SELECT) {
	    p = form->label->ptr;
	    updateSelectOption(form, form->select_option);
	}
	else
#endif				/* MENU_SELECT */
	{
	    if (!form->value)
		break;
	    p = form->value->ptr;
	}
	l = buf->currentLine;
	if (!l)
	    break;
	if (form->type == FORM_TEXTAREA) {
	    int n = a->y - buf->currentLine->linenumber;
	    if (n > 0)
		for (; l && n; l = l->prev, n--) ;
	    else if (n < 0)
		for (; l && n; l = l->prev, n++) ;
	    if (!l)
		break;
	}
	rows = form->rows ? form->rows : 1;
	col = COLPOS(l, a->start.pos);
	for (c_rows = 0; c_rows < rows; c_rows++, l = l->next) {
	    if (rows > 1) {
		pos = columnPos(l, col);
		a = retrieveAnchor(buf->formitem, l->linenumber, pos);
		if (a == NULL)
		    break;
		spos = a->start.pos;
		epos = a->end.pos;
	    }
	    if (a->start.line != a->end.line || spos > epos || epos >= l->len || spos < 0 || epos < 0)
		break;
	    pos = form_update_line(l, &p, spos, epos, COLPOS(l, epos) - col,
				   rows > 1,
				   form->type == FORM_INPUT_PASSWORD);
	    if (pos != epos) {
		shiftAnchorPosition(buf->href, buf->hmarklist,
				    a->start.line, spos, pos - epos);
		shiftAnchorPosition(buf->name, buf->hmarklist,
				    a->start.line, spos, pos - epos);
		shiftAnchorPosition(buf->img, buf->hmarklist,
				    a->start.line, spos, pos - epos);
		shiftAnchorPosition(buf->formitem, buf->hmarklist,
				    a->start.line, spos, pos - epos);
	    }
	}
	break;
    }
    copyBuffer(buf, &save);
    arrangeLine(buf);
}


disp_err_message(char *s, int redraw_current)
{
    record_err_message(s);
    disp_message(s, redraw_current);
}


schemeToProxy(int scheme)
{
    ParsedURL *pu = NULL;	/* for gcc */
    switch (scheme) {
    case SCM_HTTP:
	pu = &HTTP_proxy_parsed;
	break;
#ifdef USE_SSL
    case SCM_HTTPS:
	pu = &HTTPS_proxy_parsed;
	break;
#endif
    case SCM_FTP:
	pu = &FTP_proxy_parsed;
	break;
#ifdef USE_GOPHER
    case SCM_GOPHER:
	pu = &GOPHER_proxy_parsed;
	break;
#endif
#ifdef DEBUG
    default:
	abort();
#endif
    }
    return pu;
}


parseURL(char *url, ParsedURL *p_url, ParsedURL *current)
{
    char *p, *q, *qq;
    Str tmp;

    url = url_quote(url);	/* quote 0x01-0x20, 0x7F-0xFF */

    p = url;
    copyParsedURL(p_url, NULL);
    p_url->scheme = SCM_MISSING;

    /* RFC1808: Relative Uniform Resource Locators
     * 4.  Resolving Relative URLs
     */
    if (*url == '\0' || *url == '#') {
	if (current)
	    copyParsedURL(p_url, current);
	goto do_label;
    }
#if defined( __EMX__ ) || defined( __CYGWIN__ )
    if (!strncasecmp(url, "file://localhost/", 17)) {
	p_url->scheme = SCM_LOCAL;
	p += 17 - 1;
	url += 17 - 1;
    }
#endif
#ifdef SUPPORT_DOS_DRIVE_PREFIX
    if (IS_ALPHA(*p) && (p[1] == ':' || p[1] == '|')) {
	p_url->scheme = SCM_LOCAL;
	goto analyze_file;
    }
#endif				/* SUPPORT_DOS_DRIVE_PREFIX */
    /* search for scheme */
    p_url->scheme = getURLScheme(&p);
    if (p_url->scheme == SCM_MISSING) {
	/* scheme part is not found in the url. This means either
	 * (a) the url is relative to the current or (b) the url
	 * denotes a filename (therefore the scheme is SCM_LOCAL).
	 */
	if (current) {
	    switch (current->scheme) {
	    case SCM_LOCAL:
	    case SCM_LOCAL_CGI:
		p_url->scheme = SCM_LOCAL;
		break;
	    case SCM_FTP:
	    case SCM_FTPDIR:
		p_url->scheme = SCM_FTP;
		break;
#ifdef USE_NNTP
	    case SCM_NNTP:
	    case SCM_NNTP_GROUP:
		p_url->scheme = SCM_NNTP;
		break;
	    case SCM_NEWS:
	    case SCM_NEWS_GROUP:
		p_url->scheme = SCM_NEWS;
		break;
#endif
	    default:
		p_url->scheme = current->scheme;
		break;
	    }
	}
	else
	    p_url->scheme = SCM_LOCAL;
	p = url;
	if (!strncmp(p, "//", 2)) {
	    /* URL begins with // */
	    /* it means that 'scheme:' is abbreviated */
	    p += 2;
	    goto analyze_url;
	}
	/* the url doesn't begin with '//' */
	goto analyze_file;
    }
    /* scheme part has been found */
    if (p_url->scheme == SCM_UNKNOWN) {
	p_url->file = allocStr(url, -1);
	return;
    }
    /* get host and port */
    if (p[0] != '/' || p[1] != '/') {	/* scheme:foo or scheme:/foo */
	p_url->host = NULL;
	if (p_url->scheme != SCM_UNKNOWN)
	    p_url->port = DefaultPort[p_url->scheme];
	else
	    p_url->port = 0;
	goto analyze_file;
    }
    /* after here, p begins with // */
    if (p_url->scheme == SCM_LOCAL) {	/* file://foo           */
#ifdef __EMX__
	p += 2;
	goto analyze_file;
#else
	if (p[2] == '/' || p[2] == '~'
	    /* file:///foo  or file://~user */
#ifdef SUPPORT_DOS_DRIVE_PREFIX
	    || (IS_ALPHA(p[2]) && (p[3] == ':' || p[3] == '|'))
	    /* file://DRIVE/foo */
#endif				/* SUPPORT_DOS_DRIVE_PREFIX */
	    ) {
	    p += 2;
	    goto analyze_file;
	}
#endif				/* __EMX__ */
    }
    p += 2;			/* scheme://foo         */
    /*          ^p is here  */
  analyze_url:
    q = p;
#ifdef INET6
    if (*q == '[') {		/* rfc2732,rfc2373 compliance */
	p++;
	while (IS_XDIGIT(*p) || *p == ':' || *p == '.')
	    p++;
	if (*p != ']' || (*(p + 1) && strchr(":/?#", *(p + 1)) == NULL))
	    p = q;
    }
#endif
    while (*p && strchr(":/@?#", *p) == NULL)
	p++;
    switch (*p) {
    case ':':
	/* scheme://user:pass@host or
	 * scheme://host:port
	 */
	qq = q;
	q = ++p;
	while (*p && strchr("@/?#", *p) == NULL)
	    p++;
	if (*p == '@') {
	    /* scheme://user:pass@...       */
	    p_url->user = copyPath(qq, q - 1 - qq, COPYPATH_SPC_IGNORE);
	    p_url->pass = copyPath(q, p - q, COPYPATH_SPC_ALLOW);
	    p++;
	    goto analyze_url;
	}
	/* scheme://host:port/ */
	p_url->host = copyPath(qq, q - 1 - qq,
			       COPYPATH_SPC_IGNORE | COPYPATH_LOWERCASE);
	tmp = Strnew_charp_n(q, p - q);
	p_url->port = atoi(tmp->ptr);
	/* *p is one of ['\0', '/', '?', '#'] */
	break;
    case '@':
	/* scheme://user@...            */
	p_url->user = copyPath(q, p - q, COPYPATH_SPC_IGNORE);
	p++;
	goto analyze_url;
    case '\0':
	/* scheme://host                */
    case '/':
    case '?':
    case '#':
	p_url->host = copyPath(q, p - q,
			       COPYPATH_SPC_IGNORE | COPYPATH_LOWERCASE);
	if (p_url->scheme != SCM_UNKNOWN)
	    p_url->port = DefaultPort[p_url->scheme];
	else
	    p_url->port = 0;
	break;
    }
  analyze_file:
#ifndef SUPPORT_NETBIOS_SHARE
    if (p_url->scheme == SCM_LOCAL && p_url->user == NULL &&
	p_url->host != NULL && *p_url->host != '\0' &&
	strcmp(p_url->host, "localhost")) {
	/*
	 * In the environments other than CYGWIN, a URL like
	 * file://host/file is regarded as ftp://host/file.
	 * On the other hand, file://host/file on CYGWIN is
	 * regarded as local access to the file //host/file.
	 * `host' is a netbios-hostname, drive, or any other
	 * name; It is CYGWIN system call who interprets that.
	 */

	p_url->scheme = SCM_FTP;	/* ftp://host/... */
	if (p_url->port == 0)
	    p_url->port = DefaultPort[SCM_FTP];
    }
#endif
    if ((*p == '\0' || *p == '#' || *p == '?') && p_url->host == NULL) {
	p_url->file = "";
	goto do_query;
    }
#ifdef SUPPORT_DOS_DRIVE_PREFIX
    if (p_url->scheme == SCM_LOCAL) {
	q = p;
	if (*q == '/')
	    q++;
	if (IS_ALPHA(q[0]) && (q[1] == ':' || q[1] == '|')) {
	    if (q[1] == '|') {
		p = allocStr(q, -1);
		p[1] = ':';
	    }
	    else
		p = q;
	}
    }
#endif

    q = p;
#ifdef USE_GOPHER
    if (p_url->scheme == SCM_GOPHER) {
	if (*q == '/')
	    q++;
	if (*q && q[0] != '/' && q[1] != '/' && q[2] == '/')
	    q++;
    }
#endif				/* USE_GOPHER */
    if (*p == '/')
	p++;
    if (*p == '\0' || *p == '#' || *p == '?') {	/* scheme://host[:port]/ */
	p_url->file = DefaultFile(p_url->scheme);
	goto do_query;
    }
#ifdef USE_GOPHER
    if (p_url->scheme == SCM_GOPHER && *p == 'R') {
	p++;
	tmp = Strnew();
	Strcat_char(tmp, *(p++));
	while (*p && *p != '/')
	    p++;
	Strcat_charp(tmp, p);
	while (*p)
	    p++;
	p_url->file = copyPath(tmp->ptr, -1, COPYPATH_SPC_IGNORE);
    }
    else
#endif				/* USE_GOPHER */
    {
	char *cgi = strchr(p, '?');
      again:
	while (*p && *p != '#' && p != cgi)
	    p++;
	if (*p == '#' && p_url->scheme == SCM_LOCAL) {
	    /*
	     * According to RFC2396, # means the beginning of
	     * URI-reference, and # should be escaped.  But,
	     * if the scheme is SCM_LOCAL, the special
	     * treatment will apply to # for convinience.
	     */
	    if (p > q && *(p - 1) == '/' && (cgi == NULL || p < cgi)) {
		/*
		 * # comes as the first character of the file name
		 * that means, # is not a label but a part of the file
		 * name.
		 */
		p++;
		goto again;
	    }
	    else if (*(p + 1) == '\0') {
		/*
		 * # comes as the last character of the file name that
		 * means, # is not a label but a part of the file
		 * name.
		 */
		p++;
	    }
	}
	if (p_url->scheme == SCM_LOCAL || p_url->scheme == SCM_MISSING)
	    p_url->file = copyPath(q, p - q, COPYPATH_SPC_ALLOW);
	else
	    p_url->file = copyPath(q, p - q, COPYPATH_SPC_IGNORE);
    }

  do_query:
    if (*p == '?') {
	q = ++p;
	while (*p && *p != '#')
	    p++;
	p_url->query = copyPath(q, p - q, COPYPATH_SPC_ALLOW);
    }
  do_label:
    if (p_url->scheme == SCM_MISSING) {
	p_url->scheme = SCM_LOCAL;
	p_url->file = allocStr(p, -1);
	p_url->label = NULL;
    }
    else if (*p == '#')
	p_url->label = allocStr(p + 1, -1);
    else
	p_url->label = NULL;
}


free_ssl_ctx()
{
    if (ssl_ctx != NULL)
	SSL_CTX_free(ssl_ctx);
    ssl_ctx = NULL;
    ssl_accept_this_site(NULL);
}


domain_match(char *pat, char *domain)
{
    if (domain == NULL)
	return 0;
    if (*pat == '.')
	pat++;
    for (;;) {
	if (!strcasecmp(pat, domain))
	    return 1;
	domain = strchr(domain, '.');
	if (domain == NULL)
	    return 0;
	domain++;
    }
}


url_encode(const char *url, const ParsedURL *base, wc_ces doc_charset)
{
    return url_quote_conv((char *)url,
			  url_to_charset(url, base, doc_charset));
}


filename_extension(char *path, int is_url)
{
    char *last_dot = "", *p = path;
    int i;

    if (path == NULL)
	return last_dot;
    if (*p == '.')
	p++;
    for (; *p; p++) {
	if (*p == '.') {
	    last_dot = p;
	}
	else if (is_url && *p == '?')
	    break;
    }
    if (*last_dot == '.') {
	for (i = 1; i < 8 && last_dot[i]; i++) {
	    if (is_url && !IS_ALNUM(last_dot[i]))
		break;
	}
	return allocStr(last_dot, i);
    }
    else
	return last_dot;
}


wc_ucs_get_tag(int ntag)
{
    if (ntag == 0 || ntag > n_tag_map)
	return NULL;
    return tag_map[ntag];
}


wc_is_ucs_alpha(wc_uint32 ucs)
{
    return (ucs <= WC_C_UCS2_END &&
	wc_map_range_search((wc_uint16)ucs,
	ucs_isalpha_map, N_ucs_isalpha_map) != NULL);
}


wc_any_to_ucs(wc_wchar_t cc)
{
    int f;
    wc_uint16 *map = NULL;
    wc_uint32 map_size = 0x80;
    wc_map *map2;

    f = WC_CCS_INDEX(cc.ccs);
    switch (WC_CCS_TYPE(cc.ccs)) {
    case WC_CCS_A_CS94:
	if (cc.ccs == WC_CCS_US_ASCII)
	    return cc.code;
	if (f < WC_F_ISO_BASE || f > WC_F_CS94_END)
	    return WC_C_UCS4_ERROR;
	map = cs94_ucs_map[f - WC_F_ISO_BASE];
	cc.code &= 0x7f;
	break;
    case WC_CCS_A_CS94W:
	if (cc.ccs == WC_CCS_GB_2312 && WcOption.use_gb12345_map) {
	    cc.ccs = WC_CCS_GB_12345;
	    return wc_any_to_ucs(cc);
	} else if (cc.ccs == WC_CCS_JIS_X_0213_1) {
	    map2 = wc_map_search((wc_uint16)(cc.code & 0x7f7f),
		jisx02131_ucs_p2_map, N_jisx02131_ucs_p2_map);
	    if (map2)
		return map2->code2 | WC_C_UCS4_PLANE2;
	} else if (cc.ccs == WC_CCS_JIS_X_0213_2) {
	    map2 = wc_map_search((wc_uint16)(cc.code & 0x7f7f),
		jisx02132_ucs_p2_map, N_jisx02132_ucs_p2_map);
	    if (map2)
		return map2->code2 | WC_C_UCS4_PLANE2;
	}
	if (f < WC_F_ISO_BASE || f > WC_F_CS94W_END)
	    return 0;
	map = cs94w_ucs_map[f - WC_F_ISO_BASE];
	map_size = cs94w_ucs_map_size[f - WC_F_ISO_BASE];
	cc.code = WC_CS94W_N(cc.code);
	break;
    case WC_CCS_A_CS96:
	if (f < WC_F_ISO_BASE || f > WC_F_CS96_END)
	    return WC_C_UCS4_ERROR;
	map = cs96_ucs_map[f - WC_F_ISO_BASE];
	cc.code &= 0x7f;
	break;
    case WC_CCS_A_CS96W:
	if (f < WC_F_ISO_BASE || f > WC_F_CS96W_END)
	    return WC_C_UCS4_ERROR;
	map = cs96w_ucs_map[f - WC_F_ISO_BASE];
	map_size = cs96w_ucs_map_size[f - WC_F_ISO_BASE];
	cc.code = WC_CS96W_N(cc.code);
	break;
    case WC_CCS_A_CS942:
	if (f < WC_F_ISO_BASE || f > WC_F_CS942_END)
	    return WC_C_UCS4_ERROR;
	map = cs942_ucs_map[f - WC_F_ISO_BASE];
	cc.code &= 0x7f;
	break;
    case WC_CCS_A_PCS:
	if (f < WC_F_PCS_BASE || f > WC_F_PCS_END)
	    return WC_C_UCS4_ERROR;
	switch (cc.ccs) {
	case WC_CCS_CP1258_2:
	    map2 = wc_map_search((wc_uint16)cc.code,
		cp12582_ucs_map, N_cp12582_ucs_map);
	    if (map2)
		return map2->code2;
	    return WC_C_UCS4_ERROR;
	case WC_CCS_TCVN_5712_3:
	    return wc_any_to_ucs(wc_tcvn57123_to_tcvn5712(cc));
	case WC_CCS_GBK_80:
	    return WC_C_UCS2_EURO;
	}
	map = pcs_ucs_map[f - WC_F_PCS_BASE];
	map_size = pcs_ucs_map_size[f - WC_F_PCS_BASE];
	cc.code &= 0x7f;
	break;
    case WC_CCS_A_PCSW:
	if (f < WC_F_PCS_BASE || f > WC_F_PCSW_END)
	    return WC_C_UCS4_ERROR;
	map = pcsw_ucs_map[f - WC_F_PCS_BASE];
	map_size = pcsw_ucs_map_size[f - WC_F_PCS_BASE];
	switch (cc.ccs) {
	case WC_CCS_BIG5:
	    cc.code = WC_BIG5_N(cc.code);
	    break;
	case WC_CCS_BIG5_2:
	    cc.code = WC_CS94W_N(cc.code) + WC_C_BIG5_2_BASE;
	    break;
	case WC_CCS_HKSCS_1:
	case WC_CCS_HKSCS_2:
	    cc = wc_cs128w_to_hkscs(cc);
	case WC_CCS_HKSCS:
	    map2 = wc_map_search((wc_uint16)cc.code,
		hkscs_ucs_p2_map, N_hkscs_ucs_p2_map);
	    if (map2)
		return map2->code2 | WC_C_UCS4_PLANE2;
	    cc.code = wc_hkscs_to_N(cc.code);
	    break;
	case WC_CCS_JOHAB:
	    return wc_any_to_ucs(wc_johab_to_cs128w(cc));
	case WC_CCS_JOHAB_1:
	    return WC_CS94x128_N(cc.code) + WC_C_UCS2_HANGUL;
	case WC_CCS_JOHAB_2:
	    cc.code = WC_CS128W_N(cc.code);
	    cc.code = WC_N_JOHAB2(cc.code);
	    map2 = wc_map_search((wc_uint16)cc.code,
		johab2_ucs_map, N_johab2_ucs_map);
	    if (map2)
		return map2->code2;
	    return WC_C_UCS4_ERROR;
	case WC_CCS_JOHAB_3:
	    if ((cc.code & 0x7f7f) < 0x2121)
		return WC_C_UCS4_ERROR;
	case WC_CCS_SJIS_EXT:
	    return wc_any_to_ucs(wc_sjis_ext_to_cs94w(cc));
	case WC_CCS_SJIS_EXT_1:
	    cc.code = wc_sjis_ext1_to_N(cc.code);
	    if (cc.code == WC_C_SJIS_ERROR)
		return WC_C_UCS4_ERROR;
	    break;
	case WC_CCS_SJIS_EXT_2:
	    cc.code = wc_sjis_ext2_to_N(cc.code);
	    if (cc.code == WC_C_SJIS_ERROR)
		return WC_C_UCS4_ERROR;
	    break;
	case WC_CCS_GBK_1:
	case WC_CCS_GBK_2:
	    cc = wc_cs128w_to_gbk(cc);
	case WC_CCS_GBK:
	    cc.code = wc_gbk_to_N(cc.code);
	    break;
	case WC_CCS_GBK_EXT:
	case WC_CCS_GBK_EXT_1:
	case WC_CCS_GBK_EXT_2:
	    return wc_gb18030_to_ucs(cc);
	case WC_CCS_UHC_1:
	case WC_CCS_UHC_2:
	    cc = wc_cs128w_to_uhc(cc);
	case WC_CCS_UHC:
	    if (cc.code > WC_C_UHC_END)
		return WC_C_UCS4_ERROR;
	    cc.code = wc_uhc_to_N(cc.code);
	    break;
	default:
	    cc.code = WC_CS94W_N(cc.code);
	    break;
	}
	break;
    case WC_CCS_A_WCS16:
	switch (WC_CCS_SET(cc.ccs)) {
	case WC_CCS_UCS2:
	    return cc.code;
	}
	return WC_C_UCS4_ERROR;
    case WC_CCS_A_WCS32:
	switch (WC_CCS_SET(cc.ccs)) {
	case WC_CCS_UCS4:
	    return cc.code;
	case WC_CCS_UCS_TAG:
	    return wc_ucs_tag_to_ucs(cc.code);
	case WC_CCS_GB18030:
	    return wc_gb18030_to_ucs(cc);
	}
	return WC_C_UCS4_ERROR;
    case WC_CCS_A_UNKNOWN:
	if (cc.ccs == WC_CCS_C1)
	    return (cc.code | 0x80);
    default:
	return WC_C_UCS4_ERROR;
    }
    if (map == NULL)
	return WC_C_UCS4_ERROR;
    if (map_size == 0 || cc.code > map_size - 1)
	return WC_C_UCS4_ERROR;
    cc.code = map[cc.code];
    return cc.code ? cc.code : WC_C_UCS4_ERROR;
}


wc_is_ucs_upper(wc_uint32 ucs)
{
    return (ucs <= WC_C_UCS2_END &&
	wc_map_range_search((wc_uint16)ucs,
	ucs_isupper_map, N_ucs_isupper_map) != NULL);
}


static inline void RelinquishStreamPixels(CacheInfo *cache_info)
{
  assert(cache_info != (CacheInfo *) NULL);
  if (cache_info->mapped == MagickFalse)
    (void) RelinquishAlignedMemory(cache_info->pixels);
  else
    (void) UnmapBlob(cache_info->pixels,(size_t) cache_info->length);
  cache_info->pixels=(Quantum *) NULL;
  cache_info->metacontent=(void *) NULL;
  cache_info->length=0;
  cache_info->mapped=MagickFalse;
}


MagickExport Image *ReadImages(ImageInfo *image_info,const char *filename,
  ExceptionInfo *exception)
{
  char
    read_filename[MagickPathExtent];

  Image
    *image,
    *images;

  ImageInfo
    *read_info;

  /*
    Read image list from a file.
  */
  assert(image_info != (ImageInfo *) NULL);
  assert(image_info->signature == MagickCoreSignature);
  if (image_info->debug != MagickFalse)
    (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
      image_info->filename);
  assert(exception != (ExceptionInfo *) NULL);
  read_info=CloneImageInfo(image_info);
  *read_info->magick='\0';
  (void) SetImageOption(read_info,"filename",filename);
  (void) CopyMagickString(read_info->filename,filename,MagickPathExtent);
  (void) InterpretImageFilename(read_info,(Image *) NULL,filename,
    (int) read_info->scene,read_filename,exception);
  if (LocaleCompare(read_filename,read_info->filename) != 0)
    {
      ExceptionInfo
        *sans;

      ssize_t
        extent,
        scene;

      /*
        Images of the form image-%d.png[1-5].
      */
      sans=AcquireExceptionInfo();
      (void) SetImageInfo(read_info,0,sans);
      sans=DestroyExceptionInfo(sans);
      if (read_info->number_scenes != 0)
        {
          (void) CopyMagickString(read_filename,read_info->filename,
            MagickPathExtent);
          images=NewImageList();
          extent=(ssize_t) (read_info->scene+read_info->number_scenes);
          scene=(ssize_t) read_info->scene;
          for ( ; scene < (ssize_t) extent; scene++)
          {
            (void) InterpretImageFilename(image_info,(Image *) NULL,
              read_filename,(int) scene,read_info->filename,exception);
            image=ReadImage(read_info,exception);
            if (image == (Image *) NULL)
              continue;
            AppendImageToList(&images,image);
          }
          read_info=DestroyImageInfo(read_info);
          return(images);
        }
    }
  (void) CopyMagickString(read_info->filename,filename,MagickPathExtent);
  image=ReadImage(read_info,exception);
  read_info=DestroyImageInfo(read_info);
  return(image);
}


static size_t WriteStreamImage(const Image *image,const void *pixels,
  const size_t columns)
{
  CacheInfo
    *cache_info;

  RectangleInfo
    extract_info;

  size_t
    length,
    packet_size;

  ssize_t
    count;

  StreamInfo
    *stream_info;

  (void) pixels;
  stream_info=(StreamInfo *) image->client_data;
  switch (stream_info->storage_type)
  {
    default: packet_size=sizeof(unsigned char); break;
    case CharPixel: packet_size=sizeof(unsigned char); break;
    case DoublePixel: packet_size=sizeof(double); break;
    case FloatPixel: packet_size=sizeof(float); break;
    case LongPixel: packet_size=sizeof(unsigned int); break;
    case LongLongPixel: packet_size=sizeof(MagickSizeType); break;
    case QuantumPixel: packet_size=sizeof(Quantum); break;
    case ShortPixel: packet_size=sizeof(unsigned short); break;
  }
  cache_info=(CacheInfo *) image->cache;
  assert(cache_info->signature == MagickCoreSignature);
  packet_size*=strlen(stream_info->map);
  length=packet_size*cache_info->columns*cache_info->rows;
  if (image != stream_info->image)
    {
      ImageInfo
        *write_info;

      /*
        Prepare stream for writing.
      */
      (void) RelinquishAlignedMemory(stream_info->pixels);
      stream_info->pixels=(unsigned char *) AcquireAlignedMemory(1,length);
      if (stream_info->pixels == (unsigned char *) NULL)
        return(0);
      (void) ResetMagickMemory(stream_info->pixels,0,length);
      stream_info->image=image;
      write_info=CloneImageInfo(stream_info->image_info);
      (void) SetImageInfo(write_info,1,stream_info->exception);
      if (write_info->extract != (char *) NULL)
        (void) ParseAbsoluteGeometry(write_info->extract,
          &stream_info->extract_info);
      stream_info->y=0;
      write_info=DestroyImageInfo(write_info);
    }
  extract_info=stream_info->extract_info;
  if ((extract_info.width == 0) || (extract_info.height == 0))
    {
      /*
        Write all pixels to stream.
      */
      (void) StreamImagePixels(stream_info,image,stream_info->exception);
      count=WriteBlob(stream_info->stream,length,stream_info->pixels);
      stream_info->y++;
      return(count == 0 ? 0 : columns);
    }
  if ((stream_info->y < extract_info.y) ||
      (stream_info->y >= (ssize_t) (extract_info.y+extract_info.height)))
    {
      stream_info->y++;
      return(columns);
    }
  /*
    Write a portion of the pixel row to the stream.
  */
  (void) StreamImagePixels(stream_info,image,stream_info->exception);
  length=packet_size*extract_info.width;
  count=WriteBlob(stream_info->stream,length,stream_info->pixels+packet_size*
    extract_info.x);
  stream_info->y++;
  return(count == 0 ? 0 : columns);
}


MagickExport void SetStreamInfoMap(StreamInfo *stream_info,const char *map)
{
  assert(stream_info != (StreamInfo *) NULL);
  assert(stream_info->signature == MagickCoreSignature);
  (void) CloneString(&stream_info->map,map);
}


ar_type_find (GstTypeFind * tf, gpointer unused)
{
  const guint8 *data = gst_type_find_peek (tf, 0, 24);

  if (data && memcmp (data, "!<arch>", 7) == 0) {
    gint i;

    for (i = 7; i < 24; ++i) {
      if (!g_ascii_isprint (data[i]) && data[i] != '\n') {
        gst_type_find_suggest (tf, GST_TYPE_FIND_POSSIBLE, AR_CAPS);
      }
    }

    gst_type_find_suggest (tf, GST_TYPE_FIND_NEARLY_CERTAIN, AR_CAPS);
  }
}


ogmtext_type_find (GstTypeFind * tf, gpointer private)
{
  const guint8 *data = gst_type_find_peek (tf, 0, 9);

  if (data) {
    if (memcmp (data, "\001text\000\000\000\000", 9) != 0)
      return;
    gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, OGMTEXT_CAPS);
  }
}


multipart_type_find (GstTypeFind * tf, gpointer unused)
{
  const guint8 *data;
  const guint8 *x;

#define MULTIPART_MAX_BOUNDARY_OFFSET 16
  data = gst_type_find_peek (tf, 0, MULTIPART_MAX_BOUNDARY_OFFSET);
  if (!data)
    return;

  for (x = data;
      x - data < MULTIPART_MAX_BOUNDARY_OFFSET - 2 && g_ascii_isspace (*x);
      x++);
  if (x[0] != '-' || x[1] != '-')
    return;

  /* Could be okay, peek what should be enough for a complete header */
#define MULTIPART_MAX_HEADER_SIZE 256
  data = gst_type_find_peek (tf, 0, MULTIPART_MAX_HEADER_SIZE);
  if (!data)
    return;

  for (x = data; x - data < MULTIPART_MAX_HEADER_SIZE - 14; x++) {
    if (!isascii (*x)) {
      return;
    }
    if (*x == '\n' &&
        !g_ascii_strncasecmp ("content-type:", (gchar *) x + 1, 13)) {
      gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, MULTIPART_CAPS);
      return;
    }
  }
}


ogmaudio_type_find (GstTypeFind * tf, gpointer private)
{
  const guint8 *data = gst_type_find_peek (tf, 0, 9);

  if (data) {
    if (memcmp (data, "\001audio\000\000\000", 9) != 0)
      return;
    gst_type_find_suggest (tf, GST_TYPE_FIND_MAXIMUM, OGMAUDIO_CAPS);
  }
}


h265_video_type_find (GstTypeFind * tf, gpointer unused)
{
  DataScanCtx c = { 0, NULL, 0 };

  /* Stream consists of: a series of sync codes (00 00 00 01) followed
   * by NALs
   */
  gboolean seen_irap = FALSE;
  gboolean seen_vps = FALSE;
  gboolean seen_sps = FALSE;
  gboolean seen_pps = FALSE;
  int nut;
  int good = 0;
  int bad = 0;

  while (c.offset < H265_MAX_PROBE_LENGTH) {
    if (G_UNLIKELY (!data_scan_ctx_ensure_data (tf, &c, 5)))
      break;

    if (IS_MPEG_HEADER (c.data)) {
      /* forbiden_zero_bit | nal_unit_type */
      nut = c.data[3] & 0xfe;

      /* if forbidden bit is different to 0 won't be h265 */
      if (nut > 0x7e) {
        bad++;
        break;
      }
      nut = nut >> 1;

      /* if nuh_layer_id is not zero or nuh_temporal_id_plus1 is zero then
       * it won't be h265 */
      if ((c.data[3] & 0x01) || (c.data[4] & 0xf8) || !(c.data[4] & 0x07)) {
        bad++;
        break;
      }

      /* collect statistics about the NAL types */
      if ((nut >= 0 && nut <= 9) || (nut >= 16 && nut <= 21) || (nut >= 32
              && nut <= 40)) {
        if (nut == 32)
          seen_vps = TRUE;
        else if (nut == 33)
          seen_sps = TRUE;
        else if (nut == 34)
          seen_pps = TRUE;
        else if (nut >= 16 && nut <= 21) {
          /* BLA, IDR and CRA pictures are belongs to be IRAP picture */
          /* we are not counting the reserved IRAP pictures (22 and 23) to good */
          seen_irap = TRUE;
        }

        good++;
      } else if ((nut >= 10 && nut <= 15) || (nut >= 22 && nut <= 31)
          || (nut >= 41 && nut <= 47)) {
        /* reserved values are counting as bad */
        bad++;
      } else {
        /* unspecified (48..63), application specific */
        /* don't consider these as bad */
      }

      GST_LOG ("good:%d, bad:%d, pps:%d, sps:%d, vps:%d, irap:%d", good, bad,
          seen_pps, seen_sps, seen_vps, seen_irap);

      if (seen_sps && seen_pps && seen_irap && good >= 10 && bad < 4) {
        gst_type_find_suggest (tf, GST_TYPE_FIND_LIKELY, H265_VIDEO_CAPS);
        return;
      }

      data_scan_ctx_advance (tf, &c, 5);
    }
    data_scan_ctx_advance (tf, &c, 1);
  }

  GST_LOG ("good:%d, bad:%d, pps:%d, sps:%d, vps:%d, irap:%d", good, bad,
      seen_pps, seen_sps, seen_vps, seen_irap);

  if (good >= 2 && bad == 0) {
    gst_type_find_suggest (tf, GST_TYPE_FIND_POSSIBLE, H265_VIDEO_CAPS);
  }
}


_parse_pat (GstMpegtsSection * section)
{
  GPtrArray *pat;
  guint16 i, nb_programs;
  GstMpegtsPatProgram *program;
  guint8 *data, *end;

  /* Skip already parsed data */
  data = section->data + 8;

  /* stop at the CRC */
  end = section->data + section->section_length;

  /* Initialize program list */
  nb_programs = (end - 4 - data) / 4;
  pat =
      g_ptr_array_new_full (nb_programs,
      (GDestroyNotify) _mpegts_pat_program_free);

  GST_LOG ("nb_programs %u", nb_programs);

  for (i = 0; i < nb_programs; i++) {
    program = g_slice_new0 (GstMpegtsPatProgram);
    program->program_number = GST_READ_UINT16_BE (data);
    data += 2;

    program->network_or_program_map_PID = GST_READ_UINT16_BE (data) & 0x1FFF;
    data += 2;

    g_ptr_array_index (pat, i) = program;
  }
  pat->len = nb_programs;

  if (data != end - 4) {
    GST_ERROR ("at the end of PAT data != end - 4");
    g_ptr_array_unref (pat);

    return NULL;
  }

  return (gpointer) pat;
}


gst_mpegts_pmt_new (void)
{
  GstMpegtsPMT *pmt;

  pmt = g_slice_new0 (GstMpegtsPMT);

  pmt->descriptors = g_ptr_array_new_with_free_func ((GDestroyNotify)
      gst_mpegts_descriptor_free);
  pmt->streams = g_ptr_array_new_with_free_func ((GDestroyNotify)
      _gst_mpegts_pmt_stream_free);

  return pmt;
}


static bool ehci_verify_qh(EHCIQueue *q, EHCIqh *qh)
{
    uint32_t devaddr = get_field(qh->epchar, QH_EPCHAR_DEVADDR);
    uint32_t endp    = get_field(qh->epchar, QH_EPCHAR_EP);
    if ((devaddr != get_field(q->qh.epchar, QH_EPCHAR_DEVADDR)) ||
        (endp    != get_field(q->qh.epchar, QH_EPCHAR_EP)) ||
        (qh->current_qtd != q->qh.current_qtd) ||
        (q->async && qh->next_qtd != q->qh.next_qtd) ||
        (memcmp(&qh->altnext_qtd, &q->qh.altnext_qtd,
                                 7 * sizeof(uint32_t)) != 0) ||
        (q->dev != NULL && q->dev->addr != devaddr)) {
        return false;
    } else {
        return true;
    }
}


static void usb_ehci_vm_state_change(void *opaque, int running, RunState state)
{
    EHCIState *ehci = opaque;

    /*
     * We don't migrate the EHCIQueue-s, instead we rebuild them for the
     * schedule in guest memory. We must do the rebuilt ASAP, so that
     * USB-devices which have async handled packages have a packet in the
     * ep queue to match the completion with.
     */
    if (state == RUN_STATE_RUNNING) {
        ehci_advance_async_state(ehci);
    }

    /*
     * The schedule rebuilt from guest memory could cause the migration dest
     * to miss a QH unlink, and fail to cancel packets, since the unlinked QH
     * will never have existed on the destination. Therefor we must flush the
     * async schedule on savevm to catch any not yet noticed unlinks.
     */
    if (state == RUN_STATE_SAVE_VM) {
        ehci_advance_async_state(ehci);
        ehci_queues_rip_unseen(ehci, 1);
    }
}


static inline void ehci_raise_irq(EHCIState *s, int intr)
{
    if (intr & (USBSTS_PCD | USBSTS_FLR | USBSTS_HSE)) {
        s->usbsts |= intr;
        ehci_update_irq(s);
    } else {
        s->usbsts_pending |= intr;
    }
}


static int ehci_state_advqueue(EHCIQueue *q)
{
#if 0
    /* TO-DO: 4.10.2 - paragraph 2
     * if I-bit is set to 1 and QH is not active
     * go to horizontal QH
     */
    if (I-bit set) {
        ehci_set_state(ehci, async, EST_HORIZONTALQH);
        goto out;
    }
#endif

    /*
     * want data and alt-next qTD is valid
     */
    if (((q->qh.token & QTD_TOKEN_TBYTES_MASK) != 0) &&
        (NLPTR_TBIT(q->qh.altnext_qtd) == 0)) {
        q->qtdaddr = q->qh.altnext_qtd;
        ehci_set_state(q->ehci, q->async, EST_FETCHQTD);

    /*
     *  next qTD is valid
     */
    } else if (NLPTR_TBIT(q->qh.next_qtd) == 0) {
        q->qtdaddr = q->qh.next_qtd;
        ehci_set_state(q->ehci, q->async, EST_FETCHQTD);

    /*
     *  no valid qTD, try next QH
     */
    } else {
        ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
    }

    return 1;
}


static int ehci_fill_queue(EHCIPacket *p)
{
    USBEndpoint *ep = p->packet.ep;
    EHCIQueue *q = p->queue;
    EHCIqtd qtd = p->qtd;
    uint32_t qtdaddr;

    for (;;) {
        if (NLPTR_TBIT(qtd.next) != 0) {
            break;
        }
        qtdaddr = qtd.next;
        /*
         * Detect circular td lists, Windows creates these, counting on the
         * active bit going low after execution to make the queue stop.
         */
        QTAILQ_FOREACH(p, &q->packets, next) {
            if (p->qtdaddr == qtdaddr) {
                goto leave;
            }
        }
        if (get_dwords(q->ehci, NLPTR_GET(qtdaddr),
                       (uint32_t *) &qtd, sizeof(EHCIqtd) >> 2) < 0) {
            return -1;
        }
        ehci_trace_qtd(q, NLPTR_GET(qtdaddr), &qtd);
        if (!(qtd.token & QTD_TOKEN_ACTIVE)) {
            break;
        }
        if (!ehci_verify_pid(q, &qtd)) {
            ehci_trace_guest_bug(q->ehci, "guest queued token with wrong pid");
            break;
        }
        p = ehci_alloc_packet(q);
        p->qtdaddr = qtdaddr;
        p->qtd = qtd;
        if (ehci_execute(p, "queue") == -1) {
            return -1;
        }
        assert(p->packet.status == USB_RET_ASYNC);
        p->async = EHCI_ASYNC_INFLIGHT;
    }
leave:
    usb_device_flush_ep_queue(ep->dev, ep);
    return 1;
}


static const char *addr2str(hwaddr addr)
{
    return nr2str(ehci_mmio_names, ARRAY_SIZE(ehci_mmio_names), addr);
}


static void mux_chr_close(struct CharDriverState *chr)
{
    MuxDriver *d = chr->opaque;
    int i;

    for (i = 0; i < d->mux_cnt; i++) {
        CharBackend *be = d->backends[i];
        if (be) {
            be->chr = NULL;
        }
    }
    qemu_chr_fe_deinit(&d->chr);
    g_free(d);
}


static void tcp_chr_update_read_handler(CharDriverState *chr,
                                        GMainContext *context)
{
    TCPCharDriver *s = chr->opaque;

    if (!s->connected) {
        return;
    }

    remove_fd_in_watch(chr);
    if (s->ioc) {
        chr->fd_in_tag = io_add_watch_poll(s->ioc,
                                           tcp_chr_read_poll,
                                           tcp_chr_read, chr,
                                           context);
    }
}


ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend,
                               Error **errp)
{
    ChardevReturn *ret = g_new0(ChardevReturn, 1);
    CharDriverState *chr = NULL;
    Error *local_err = NULL;
    GSList *i;
    CharDriver *cd;

    chr = qemu_chr_find(id);
    if (chr) {
        error_setg(errp, "Chardev '%s' already exists", id);
        g_free(ret);
        return NULL;
    }

    for (i = backends; i; i = i->next) {
        cd = i->data;

        if (cd->kind == backend->type) {
            chr = cd->create(id, backend, ret, &local_err);
            if (local_err) {
                error_propagate(errp, local_err);
                goto out_error;
            }
            break;
        }
    }

    if (chr == NULL) {
        assert(!i);
        error_setg(errp, "chardev backend not available");
        goto out_error;
    }

    chr->label = g_strdup(id);
    chr->avail_connections =
        (backend->type == CHARDEV_BACKEND_KIND_MUX) ? MAX_MUX : 1;
    if (!chr->filename) {
        chr->filename = g_strdup(ChardevBackendKind_lookup[backend->type]);
    }
    if (!chr->explicit_be_open) {
        qemu_chr_be_event(chr, CHR_EVENT_OPENED);
    }
    QTAILQ_INSERT_TAIL(&chardevs, chr, next);
    return ret;

out_error:
    g_free(ret);
    return NULL;
}


static int ringbuf_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
    RingBufCharDriver *d = chr->opaque;
    int i;

    if (!buf || (len < 0)) {
        return -1;
    }

    for (i = 0; i < len; i++ ) {
        d->cbuf[d->prod++ & (d->size - 1)] = buf[i];
        if (d->prod - d->cons > d->size) {
            d->cons = d->prod - d->size;
        }
    }

    return len;
}


void qemu_chr_delete(CharDriverState *chr)
{
    QTAILQ_REMOVE(&chardevs, chr, next);
    qemu_chr_free(chr);
}


static bool qemu_chr_is_busy(CharDriverState *s)
{
    if (s->is_mux) {
        MuxDriver *d = s->opaque;
        return d->mux_cnt >= 0;
    } else {
        return s->be != NULL;
    }
}


static int csrhci_header_len(const uint8_t *pkt)
{
    switch (pkt[0]) {
    case H4_CMD_PKT:
        return HCI_COMMAND_HDR_SIZE;
    case H4_EVT_PKT:
        return HCI_EVENT_HDR_SIZE;
    case H4_ACL_PKT:
        return HCI_ACL_HDR_SIZE;
    case H4_SCO_PKT:
        return HCI_SCO_HDR_SIZE;
    case H4_NEG_PKT:
        return pkt[1] + 1;
    case H4_ALIVE_PKT:
        return 3;
    }

    exit(-1);
}


static char *SocketAddress_to_str(const char *prefix, SocketAddress *addr,
                                  bool is_listen, bool is_telnet)
{
    switch (addr->type) {
    case SOCKET_ADDRESS_KIND_INET:
        return g_strdup_printf("%s%s:%s:%s%s", prefix,
                               is_telnet ? "telnet" : "tcp",
                               addr->u.inet.data->host,
                               addr->u.inet.data->port,
                               is_listen ? ",server" : "");
        break;
    case SOCKET_ADDRESS_KIND_UNIX:
        return g_strdup_printf("%sunix:%s%s", prefix,
                               addr->u.q_unix.data->path,
                               is_listen ? ",server" : "");
        break;
    case SOCKET_ADDRESS_KIND_FD:
        return g_strdup_printf("%sfd:%s%s", prefix, addr->u.fd.data->str,
                               is_listen ? ",server" : "");
        break;
    default:
        abort();
    }
}


static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
{
    CharDriverState *chr = opaque;
    PtyCharDriver *s = chr->opaque;
    gsize len;
    uint8_t buf[READ_BUF_LEN];
    ssize_t ret;

    len = sizeof(buf);
    if (len > s->read_bytes)
        len = s->read_bytes;
    if (len == 0) {
        return TRUE;
    }
    ret = qio_channel_read(s->ioc, (char *)buf, len, NULL);
    if (ret <= 0) {
        pty_chr_state(chr, 0);
        return FALSE;
    } else {
        pty_chr_state(chr, 1);
        qemu_chr_be_write(chr, buf, ret);
    }
    return TRUE;
}


static void pp_close(CharDriverState *chr)
{
    ParallelCharDriver *drv = chr->opaque;
    int fd = drv->fd;

    pp_hw_mode(drv, IEEE1284_MODE_COMPAT);
    ioctl(fd, PPRELEASE);
    close(fd);
    g_free(drv);
    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}


void graphic_hw_update(QemuConsole *con)
{
    if (!con) {
        con = active_console;
    }
    if (con && con->hw_ops->gfx_update) {
        con->hw_ops->gfx_update(con->hw);
    }
}


static int mux_chr_can_read(void *opaque)
{
    CharDriverState *chr = opaque;
    MuxDriver *d = chr->opaque;
    int m = d->focus;
    CharBackend *be = d->backends[m];

    if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE) {
        return 1;
    }

    if (be && be->chr_can_read) {
        return be->chr_can_read(be->opaque);
    }

    return 0;
}


static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
{
    CharDriverState *chr = opaque;
    TCPCharDriver *s = chr->opaque;
    uint8_t buf[READ_BUF_LEN];
    int len, size;

    if (!s->connected || s->max_size <= 0) {
        return TRUE;
    }
    len = sizeof(buf);
    if (len > s->max_size)
        len = s->max_size;
    size = tcp_chr_recv(chr, (void *)buf, len);
    if (size == 0 || size == -1) {
        /* connection closed */
        tcp_chr_disconnect(chr);
    } else if (size > 0) {
        if (s->do_telnetopt)
            tcp_chr_process_IAC_bytes(chr, s, buf, &size);
        if (size > 0)
            qemu_chr_be_write(chr, buf, size);
    }

    return TRUE;
}


static inline uint8_t *csrhci_out_packet_event(struct csrhci_s *s,
                int evt, int len)
{
    uint8_t *ret = csrhci_out_packetz(s,
                    len + 1 + sizeof(struct hci_event_hdr));

    *ret ++ = H4_EVT_PKT;
    ((struct hci_event_hdr *) ret)->evt = evt;
    ((struct hci_event_hdr *) ret)->plen = len;

    return ret + sizeof(struct hci_event_hdr);
}


static void text_console_invalidate(void *opaque)
{
    QemuConsole *s = (QemuConsole *) opaque;

    if (s->ds->have_text && s->console_type == TEXT_CONSOLE) {
        text_console_resize(s);
    }
    console_refresh(s);
}


static gboolean socket_reconnect_timeout(gpointer opaque)
{
    CharDriverState *chr = opaque;
    TCPCharDriver *s = chr->opaque;
    QIOChannelSocket *sioc;

    s->reconnect_timer = 0;

    if (chr->be_open) {
        return false;
    }

    sioc = qio_channel_socket_new();
    qio_channel_socket_connect_async(sioc, s->addr,
                                     qemu_chr_socket_connected,
                                     chr, NULL);

    return false;
}


void qemu_chr_cleanup(void)
{
    CharDriverState *chr, *tmp;

    QTAILQ_FOREACH_SAFE(chr, &chardevs, next, tmp) {
        qemu_chr_delete(chr);
    }
}


static void win_chr_close(CharDriverState *chr)
{
    WinCharState *s = chr->opaque;

    if (s->hsend) {
        CloseHandle(s->hsend);
        s->hsend = NULL;
    }
    if (s->hrecv) {
        CloseHandle(s->hrecv);
        s->hrecv = NULL;
    }
    if (s->hcom) {
        CloseHandle(s->hcom);
        s->hcom = NULL;
    }
    if (s->fpipe)
        qemu_del_polling_cb(win_chr_pipe_poll, chr);
    else
        qemu_del_polling_cb(win_chr_poll, chr);

    qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
}


void qemu_chr_be_generic_open(CharDriverState *s)
{
    qemu_chr_be_event(s, CHR_EVENT_OPENED);
}


static void tcp_chr_tls_init(CharDriverState *chr)
{
    TCPCharDriver *s = chr->opaque;
    QIOChannelTLS *tioc;
    Error *err = NULL;

    if (s->is_listen) {
        tioc = qio_channel_tls_new_server(
            s->ioc, s->tls_creds,
            NULL, /* XXX Use an ACL */
            &err);
    } else {
        tioc = qio_channel_tls_new_client(
            s->ioc, s->tls_creds,
            s->addr->u.inet.data->host,
            &err);
    }
    if (tioc == NULL) {
        error_free(err);
        tcp_chr_disconnect(chr);
        return;
    }
    object_unref(OBJECT(s->ioc));
    s->ioc = QIO_CHANNEL(tioc);

    qio_channel_tls_handshake(tioc,
                              tcp_chr_tls_handshake,
                              chr,
                              NULL);
}


static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
    FDCharDriver *s = chr->opaque;

    return io_channel_send(s->ioc_out, buf, len);
}


DisplayState *init_displaystate(void)
{
    gchar *name;
    int i;

    get_alloc_displaystate();
    for (i = 0; i < nb_consoles; i++) {
        if (consoles[i]->console_type != GRAPHIC_CONSOLE &&
            consoles[i]->ds == NULL) {
            text_console_do_init(consoles[i]->chr, display_state);
        }

        /* Hook up into the qom tree here (not in new_console()), once
         * all QemuConsoles are created and the order / numbering
         * doesn't change any more */
        name = g_strdup_printf("console[%d]", i);
        object_property_add_child(container_get(object_get_root(), "/backend"),
                                  name, OBJECT(consoles[i]), &error_abort);
        g_free(name);
    }

    return display_state;
}


static CharDriverState *qemu_chr_open_mux(const char *id,
                                          ChardevBackend *backend,
                                          ChardevReturn *ret, Error **errp)
{
    ChardevMux *mux = backend->u.mux.data;
    CharDriverState *chr, *drv;
    MuxDriver *d;
    ChardevCommon *common = qapi_ChardevMux_base(mux);

    drv = qemu_chr_find(mux->chardev);
    if (drv == NULL) {
        error_setg(errp, "mux: base chardev %s not found", mux->chardev);
        return NULL;
    }

    chr = qemu_chr_alloc(common, errp);
    if (!chr) {
        return NULL;
    }
    d = g_new0(MuxDriver, 1);

    chr->opaque = d;
    d->focus = -1;
    chr->chr_close = mux_chr_close;
    chr->chr_write = mux_chr_write;
    chr->chr_accept_input = mux_chr_accept_input;
    /* Frontend guest-open / -close notification is not support with muxes */
    chr->chr_set_fe_open = NULL;
    if (drv->chr_add_watch) {
        chr->chr_add_watch = mux_chr_add_watch;
    }
    /* only default to opened state if we've realized the initial
     * set of muxes
     */
    chr->explicit_be_open = muxes_realized ? 0 : 1;
    chr->is_mux = 1;
    if (!qemu_chr_fe_init(&d->chr, drv, errp)) {
        qemu_chr_free(chr);
        return NULL;
    }

    return chr;
}


void register_char_driver(const char *name, ChardevBackendKind kind,
        void (*parse)(QemuOpts *opts, ChardevBackend *backend, Error **errp),
        CharDriverState *(*create)(const char *id, ChardevBackend *backend,
                                   ChardevReturn *ret, Error **errp))
{
    CharDriver *s;

    s = g_malloc0(sizeof(*s));
    s->name = g_strdup(name);
    s->kind = kind;
    s->parse = parse;
    s->create = create;

    backends = g_slist_append(backends, s);
}


static void csrhci_pins(void *opaque, int line, int level)
{
    struct csrhci_s *s = (struct csrhci_s *) opaque;
    int state = s->pin_state;

    s->pin_state &= ~(1 << line);
    s->pin_state |= (!!level) << line;

    if ((state & ~s->pin_state) & (1 << csrhci_pin_reset)) {
        /* TODO: Disappear from lower layers */
        csrhci_reset(s);
    }

    if (s->pin_state == 3 && state != 3) {
        s->enable = 1;
        /* TODO: Wake lower layers up */
    }
}


static int tcp_set_msgfds(CharDriverState *chr, int *fds, int num)
{
    TCPCharDriver *s = chr->opaque;

    /* clear old pending fd array */
    g_free(s->write_msgfds);
    s->write_msgfds = NULL;
    s->write_msgfds_num = 0;

    if (!s->connected ||
        !qio_channel_has_feature(s->ioc,
                                 QIO_CHANNEL_FEATURE_FD_PASS)) {
        return -1;
    }

    if (num) {
        s->write_msgfds = g_new(int, num);
        memcpy(s->write_msgfds, fds, num * sizeof(int));
    }

    s->write_msgfds_num = num;

    return 0;
}


static void qemu_chr_socket_restart_timer(CharDriverState *chr)
{
    TCPCharDriver *s = chr->opaque;
    assert(s->connected == 0);
    s->reconnect_timer = g_timeout_add_seconds(s->reconnect_time,
                                               socket_reconnect_timeout, chr);
}


void qemu_chr_fe_set_echo(CharBackend *be, bool echo)
{
    CharDriverState *chr = be->chr;

    if (chr && chr->chr_set_echo) {
        chr->chr_set_echo(chr, echo);
    }
}


static void tcp_v6_send_response(const struct sock *sk, struct sk_buff *skb, u32 seq,
				 u32 ack, u32 win, u32 tsval, u32 tsecr,
				 int oif, struct tcp_md5sig_key *key, int rst,
				 u8 tclass, u32 label)
{
	const struct tcphdr *th = tcp_hdr(skb);
	struct tcphdr *t1;
	struct sk_buff *buff;
	struct flowi6 fl6;
	struct net *net = sk ? sock_net(sk) : dev_net(skb_dst(skb)->dev);
	struct sock *ctl_sk = net->ipv6.tcp_sk;
	unsigned int tot_len = sizeof(struct tcphdr);
	struct dst_entry *dst;
	__be32 *topt;

	if (tsecr)
		tot_len += TCPOLEN_TSTAMP_ALIGNED;
#ifdef CONFIG_TCP_MD5SIG
	if (key)
		tot_len += TCPOLEN_MD5SIG_ALIGNED;
#endif

	buff = alloc_skb(MAX_HEADER + sizeof(struct ipv6hdr) + tot_len,
			 GFP_ATOMIC);
	if (!buff)
		return;

	skb_reserve(buff, MAX_HEADER + sizeof(struct ipv6hdr) + tot_len);

	t1 = (struct tcphdr *) skb_push(buff, tot_len);
	skb_reset_transport_header(buff);

	/* Swap the send and the receive. */
	memset(t1, 0, sizeof(*t1));
	t1->dest = th->source;
	t1->source = th->dest;
	t1->doff = tot_len / 4;
	t1->seq = htonl(seq);
	t1->ack_seq = htonl(ack);
	t1->ack = !rst || !th->ack;
	t1->rst = rst;
	t1->window = htons(win);

	topt = (__be32 *)(t1 + 1);

	if (tsecr) {
		*topt++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
				(TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP);
		*topt++ = htonl(tsval);
		*topt++ = htonl(tsecr);
	}

#ifdef CONFIG_TCP_MD5SIG
	if (key) {
		*topt++ = htonl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |
				(TCPOPT_MD5SIG << 8) | TCPOLEN_MD5SIG);
		tcp_v6_md5_hash_hdr((__u8 *)topt, key,
				    &ipv6_hdr(skb)->saddr,
				    &ipv6_hdr(skb)->daddr, t1);
	}
#endif

	memset(&fl6, 0, sizeof(fl6));
	fl6.daddr = ipv6_hdr(skb)->saddr;
	fl6.saddr = ipv6_hdr(skb)->daddr;
	fl6.flowlabel = label;

	buff->ip_summed = CHECKSUM_PARTIAL;
	buff->csum = 0;

	__tcp_v6_send_check(buff, &fl6.saddr, &fl6.daddr);

	fl6.flowi6_proto = IPPROTO_TCP;
	if (rt6_need_strict(&fl6.daddr) && !oif)
		fl6.flowi6_oif = tcp_v6_iif(skb);
	else {
		if (!oif && netif_index_is_l3_master(net, skb->skb_iif))
			oif = skb->skb_iif;

		fl6.flowi6_oif = oif;
	}

	fl6.flowi6_mark = IP6_REPLY_MARK(net, skb->mark);
	fl6.fl6_dport = t1->dest;
	fl6.fl6_sport = t1->source;
	security_skb_classify_flow(skb, flowi6_to_flowi(&fl6));

	/* Pass a socket to ip6_dst_lookup either it is for RST
	 * Underlying function will use this to retrieve the network
	 * namespace
	 */
	dst = ip6_dst_lookup_flow(ctl_sk, &fl6, NULL);
	if (!IS_ERR(dst)) {
		skb_dst_set(buff, dst);
		ip6_xmit(ctl_sk, buff, &fl6, NULL, tclass);
		TCP_INC_STATS_BH(net, TCP_MIB_OUTSEGS);
		if (rst)
			TCP_INC_STATS_BH(net, TCP_MIB_OUTRSTS);
		return;
	}

	kfree_skb(buff);
}


static int tcp_v6_rcv(struct sk_buff *skb)
{
	const struct tcphdr *th;
	const struct ipv6hdr *hdr;
	struct sock *sk;
	int ret;
	struct net *net = dev_net(skb->dev);

	if (skb->pkt_type != PACKET_HOST)
		goto discard_it;

	/*
	 *	Count it even if it's bad.
	 */
	TCP_INC_STATS_BH(net, TCP_MIB_INSEGS);

	if (!pskb_may_pull(skb, sizeof(struct tcphdr)))
		goto discard_it;

	th = tcp_hdr(skb);

	if (th->doff < sizeof(struct tcphdr)/4)
		goto bad_packet;
	if (!pskb_may_pull(skb, th->doff*4))
		goto discard_it;

	if (skb_checksum_init(skb, IPPROTO_TCP, ip6_compute_pseudo))
		goto csum_error;

	th = tcp_hdr(skb);
	hdr = ipv6_hdr(skb);

lookup:
	sk = __inet6_lookup_skb(&tcp_hashinfo, skb, __tcp_hdrlen(th),
				th->source, th->dest, inet6_iif(skb));
	if (!sk)
		goto no_tcp_socket;

process:
	if (sk->sk_state == TCP_TIME_WAIT)
		goto do_time_wait;

	if (sk->sk_state == TCP_NEW_SYN_RECV) {
		struct request_sock *req = inet_reqsk(sk);
		struct sock *nsk;

		sk = req->rsk_listener;
		tcp_v6_fill_cb(skb, hdr, th);
		if (tcp_v6_inbound_md5_hash(sk, skb)) {
			reqsk_put(req);
			goto discard_it;
		}
		if (unlikely(sk->sk_state != TCP_LISTEN)) {
			inet_csk_reqsk_queue_drop_and_put(sk, req);
			goto lookup;
		}
		sock_hold(sk);
		nsk = tcp_check_req(sk, skb, req, false);
		if (!nsk) {
			reqsk_put(req);
			goto discard_and_relse;
		}
		if (nsk == sk) {
			reqsk_put(req);
			tcp_v6_restore_cb(skb);
		} else if (tcp_child_process(sk, nsk, skb)) {
			tcp_v6_send_reset(nsk, skb);
			goto discard_and_relse;
		} else {
			sock_put(sk);
			return 0;
		}
	}
	if (hdr->hop_limit < inet6_sk(sk)->min_hopcount) {
		NET_INC_STATS_BH(net, LINUX_MIB_TCPMINTTLDROP);
		goto discard_and_relse;
	}

	if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))
		goto discard_and_relse;

	tcp_v6_fill_cb(skb, hdr, th);

	if (tcp_v6_inbound_md5_hash(sk, skb))
		goto discard_and_relse;

	if (sk_filter(sk, skb))
		goto discard_and_relse;

	skb->dev = NULL;

	if (sk->sk_state == TCP_LISTEN) {
		ret = tcp_v6_do_rcv(sk, skb);
		goto put_and_return;
	}

	sk_incoming_cpu_update(sk);

	bh_lock_sock_nested(sk);
	tcp_segs_in(tcp_sk(sk), skb);
	ret = 0;
	if (!sock_owned_by_user(sk)) {
		if (!tcp_prequeue(sk, skb))
			ret = tcp_v6_do_rcv(sk, skb);
	} else if (unlikely(sk_add_backlog(sk, skb,
					   sk->sk_rcvbuf + sk->sk_sndbuf))) {
		bh_unlock_sock(sk);
		NET_INC_STATS_BH(net, LINUX_MIB_TCPBACKLOGDROP);
		goto discard_and_relse;
	}
	bh_unlock_sock(sk);

put_and_return:
	sock_put(sk);
	return ret ? -1 : 0;

no_tcp_socket:
	if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb))
		goto discard_it;

	tcp_v6_fill_cb(skb, hdr, th);

	if (tcp_checksum_complete(skb)) {
csum_error:
		TCP_INC_STATS_BH(net, TCP_MIB_CSUMERRORS);
bad_packet:
		TCP_INC_STATS_BH(net, TCP_MIB_INERRS);
	} else {
		tcp_v6_send_reset(NULL, skb);
	}

discard_it:
	kfree_skb(skb);
	return 0;

discard_and_relse:
	sock_put(sk);
	goto discard_it;

do_time_wait:
	if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) {
		inet_twsk_put(inet_twsk(sk));
		goto discard_it;
	}

	tcp_v6_fill_cb(skb, hdr, th);

	if (tcp_checksum_complete(skb)) {
		inet_twsk_put(inet_twsk(sk));
		goto csum_error;
	}

	switch (tcp_timewait_state_process(inet_twsk(sk), skb, th)) {
	case TCP_TW_SYN:
	{
		struct sock *sk2;

		sk2 = inet6_lookup_listener(dev_net(skb->dev), &tcp_hashinfo,
					    skb, __tcp_hdrlen(th),
					    &ipv6_hdr(skb)->saddr, th->source,
					    &ipv6_hdr(skb)->daddr,
					    ntohs(th->dest), tcp_v6_iif(skb));
		if (sk2) {
			struct inet_timewait_sock *tw = inet_twsk(sk);
			inet_twsk_deschedule_put(tw);
			sk = sk2;
			tcp_v6_restore_cb(skb);
			goto process;
		}
		/* Fall through to ACK */
	}
	case TCP_TW_ACK:
		tcp_v6_timewait_ack(sk, skb);
		break;
	case TCP_TW_RST:
		tcp_v6_restore_cb(skb);
		tcp_v6_send_reset(sk, skb);
		inet_twsk_deschedule_put(inet_twsk(sk));
		goto discard_it;
	case TCP_TW_SUCCESS:
		;
	}
	goto discard_it;
}


find_all_by_keyword(smartlist_t *s, directory_keyword k)
{
  smartlist_t *out = NULL;
  SMARTLIST_FOREACH(s, directory_token_t *, t,
                    if (t->tp == k) {
                      if (!out)
                        out = smartlist_new();
                      smartlist_add(out, t);
                    });
  return out;
}


microdescs_parse_from_string(const char *s, const char *eos,
                             int allow_annotations,
                             saved_location_t where)
{
  smartlist_t *tokens;
  smartlist_t *result;
  microdesc_t *md = NULL;
  memarea_t *area;
  const char *start = s;
  const char *start_of_next_microdesc;
  int flags = allow_annotations ? TS_ANNOTATIONS_OK : 0;
  const int copy_body = (where != SAVED_IN_CACHE);

  directory_token_t *tok;

  if (!eos)
    eos = s + strlen(s);

  s = eat_whitespace_eos(s, eos);
  area = memarea_new();
  result = smartlist_new();
  tokens = smartlist_new();

  while (s < eos) {
    start_of_next_microdesc = find_start_of_next_microdesc(s, eos);
    if (!start_of_next_microdesc)
      start_of_next_microdesc = eos;

    if (tokenize_string(area, s, start_of_next_microdesc, tokens,
                        microdesc_token_table, flags)) {
      log_warn(LD_DIR, "Unparseable microdescriptor");
      goto next;
    }

    md = tor_malloc_zero(sizeof(microdesc_t));
    {
      const char *cp = tor_memstr(s, start_of_next_microdesc-s,
                                  "onion-key");
      tor_assert(cp);

      md->bodylen = start_of_next_microdesc - cp;
      md->saved_location = where;
      if (copy_body)
        md->body = tor_memdup_nulterm(cp, md->bodylen);
      else
        md->body = (char*)cp;
      md->off = cp - start;
    }

    if ((tok = find_opt_by_keyword(tokens, A_LAST_LISTED))) {
      if (parse_iso_time(tok->args[0], &md->last_listed)) {
        log_warn(LD_DIR, "Bad last-listed time in microdescriptor");
        goto next;
      }
    }

    tok = find_by_keyword(tokens, K_ONION_KEY);
    if (!crypto_pk_public_exponent_ok(tok->key)) {
      log_warn(LD_DIR,
               "Relay's onion key had invalid exponent.");
      goto next;
    }
    md->onion_pkey = tok->key;
    tok->key = NULL;

    if ((tok = find_opt_by_keyword(tokens, K_ONION_KEY_NTOR))) {
      curve25519_public_key_t k;
      tor_assert(tok->n_args >= 1);
      if (curve25519_public_from_base64(&k, tok->args[0]) < 0) {
        log_warn(LD_DIR, "Bogus ntor-onion-key in microdesc");
        goto next;
      }
      md->onion_curve25519_pkey =
        tor_memdup(&k, sizeof(curve25519_public_key_t));
    }

    {
      smartlist_t *a_lines = find_all_by_keyword(tokens, K_A);
      if (a_lines) {
        find_single_ipv6_orport(a_lines, &md->ipv6_addr, &md->ipv6_orport);
        smartlist_free(a_lines);
      }
    }

    if ((tok = find_opt_by_keyword(tokens, K_FAMILY))) {
      int i;
      md->family = smartlist_new();
      for (i=0;i<tok->n_args;++i) {
        if (!is_legal_nickname_or_hexdigest(tok->args[i])) {
          log_warn(LD_DIR, "Illegal nickname %s in family line",
                   escaped(tok->args[i]));
          goto next;
        }
        smartlist_add(md->family, tor_strdup(tok->args[i]));
      }
    }

    if ((tok = find_opt_by_keyword(tokens, K_P))) {
      md->exit_policy = parse_short_policy(tok->args[0]);
    }
    if ((tok = find_opt_by_keyword(tokens, K_P6))) {
      md->ipv6_exit_policy = parse_short_policy(tok->args[0]);
    }

    crypto_digest256(md->digest, md->body, md->bodylen, DIGEST_SHA256);

    smartlist_add(result, md);

    md = NULL;
  next:
    microdesc_free(md);
    md = NULL;

    SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
    memarea_clear(area);
    smartlist_clear(tokens);
    s = start_of_next_microdesc;
  }

  SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
  memarea_drop_all(area);
  smartlist_free(tokens);

  return result;
}


static pixman_format_code_t get_pixman_format(uint32_t virtio_gpu_format)
{
    switch (virtio_gpu_format) {
#ifdef HOST_WORDS_BIGENDIAN
    case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM:
        return PIXMAN_b8g8r8x8;
    case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM:
        return PIXMAN_b8g8r8a8;
    case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM:
        return PIXMAN_x8r8g8b8;
    case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM:
        return PIXMAN_a8r8g8b8;
    case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM:
        return PIXMAN_r8g8b8x8;
    case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM:
        return PIXMAN_r8g8b8a8;
    case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM:
        return PIXMAN_x8b8g8r8;
    case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM:
        return PIXMAN_a8b8g8r8;
#else
    case VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM:
        return PIXMAN_x8r8g8b8;
    case VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM:
        return PIXMAN_a8r8g8b8;
    case VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM:
        return PIXMAN_b8g8r8x8;
    case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM:
        return PIXMAN_b8g8r8a8;
    case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM:
        return PIXMAN_x8b8g8r8;
    case VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM:
        return PIXMAN_a8b8g8r8;
    case VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM:
        return PIXMAN_r8g8b8x8;
    case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM:
        return PIXMAN_r8g8b8a8;
#endif
    default:
        return 0;
    }
}


static void virtio_gpu_transfer_to_host_2d(VirtIOGPU *g,
                                           struct virtio_gpu_ctrl_command *cmd)
{
    struct virtio_gpu_simple_resource *res;
    int h;
    uint32_t src_offset, dst_offset, stride;
    int bpp;
    pixman_format_code_t format;
    struct virtio_gpu_transfer_to_host_2d t2d;

    VIRTIO_GPU_FILL_CMD(t2d);
    trace_virtio_gpu_cmd_res_xfer_toh_2d(t2d.resource_id);

    res = virtio_gpu_find_resource(g, t2d.resource_id);
    if (!res || !res->iov) {
        qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal resource specified %d\n",
                      __func__, t2d.resource_id);
        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
        return;
    }

    if (t2d.r.x > res->width ||
        t2d.r.y > res->height ||
        t2d.r.width > res->width ||
        t2d.r.height > res->height ||
        t2d.r.x + t2d.r.width > res->width ||
        t2d.r.y + t2d.r.height > res->height) {
        qemu_log_mask(LOG_GUEST_ERROR, "%s: transfer bounds outside resource"
                      " bounds for resource %d: %d %d %d %d vs %d %d\n",
                      __func__, t2d.resource_id, t2d.r.x, t2d.r.y,
                      t2d.r.width, t2d.r.height, res->width, res->height);
        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER;
        return;
    }

    format = pixman_image_get_format(res->image);
    bpp = (PIXMAN_FORMAT_BPP(format) + 7) / 8;
    stride = pixman_image_get_stride(res->image);

    if (t2d.offset || t2d.r.x || t2d.r.y ||
        t2d.r.width != pixman_image_get_width(res->image)) {
        void *img_data = pixman_image_get_data(res->image);
        for (h = 0; h < t2d.r.height; h++) {
            src_offset = t2d.offset + stride * h;
            dst_offset = (t2d.r.y + h) * stride + (t2d.r.x * bpp);

            iov_to_buf(res->iov, res->iov_cnt, src_offset,
                       (uint8_t *)img_data
                       + dst_offset, t2d.r.width * bpp);
        }
    } else {
        iov_to_buf(res->iov, res->iov_cnt, 0,
                   pixman_image_get_data(res->image),
                   pixman_image_get_stride(res->image)
                   * pixman_image_get_height(res->image));
    }
}


static void virgl_cmd_set_scanout(VirtIOGPU *g,
                                  struct virtio_gpu_ctrl_command *cmd)
{
    struct virtio_gpu_set_scanout ss;
    struct virgl_renderer_resource_info info;
    int ret;

    VIRTIO_GPU_FILL_CMD(ss);
    trace_virtio_gpu_cmd_set_scanout(ss.scanout_id, ss.resource_id,
                                     ss.r.width, ss.r.height, ss.r.x, ss.r.y);

    if (ss.scanout_id >= g->conf.max_outputs) {
        qemu_log_mask(LOG_GUEST_ERROR, "%s: illegal scanout id specified %d",
                      __func__, ss.scanout_id);
        cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID;
        return;
    }
    g->enable = 1;

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

    if (ss.resource_id && ss.r.width && ss.r.height) {
        ret = virgl_renderer_resource_get_info(ss.resource_id, &info);
        if (ret == -1) {
            qemu_log_mask(LOG_GUEST_ERROR,
                          "%s: illegal resource specified %d\n",
                          __func__, ss.resource_id);
            cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID;
            return;
        }
        qemu_console_resize(g->scanout[ss.scanout_id].con,
                            ss.r.width, ss.r.height);
        virgl_renderer_force_ctx_0();
        dpy_gl_scanout(g->scanout[ss.scanout_id].con, info.tex_id,
                       info.flags & 1 /* FIXME: Y_0_TOP */,
                       ss.r.x, ss.r.y, ss.r.width, ss.r.height);
    } else {
        if (ss.scanout_id != 0) {
            dpy_gfx_replace_surface(g->scanout[ss.scanout_id].con, NULL);
        }
        dpy_gl_scanout(g->scanout[ss.scanout_id].con, 0, false,
                       0, 0, 0, 0);
    }
    g->scanout[ss.scanout_id].resource_id = ss.resource_id;
}


static void tcmpt_destroy(jpc_enc_tcmpt_t *tcmpt)
{
	jpc_enc_rlvl_t *rlvl;
	uint_fast16_t rlvlno;

	if (tcmpt->rlvls) {
		for (rlvlno = 0, rlvl = tcmpt->rlvls; rlvlno < tcmpt->numrlvls;
		  ++rlvlno, ++rlvl) {
			rlvl_destroy(rlvl);
		}
		jas_free(tcmpt->rlvls);
		tcmpt->rlvls = NULL;
	}

	if (tcmpt->data) {
		jas_seq2d_destroy(tcmpt->data);
		tcmpt->data = NULL;
	}
	if (tcmpt->tsfb) {
		jpc_tsfb_destroy(tcmpt->tsfb);
		tcmpt->tsfb = NULL;
	}
}


png_pow10(int power)
{
   int recip = 0;
   double d = 1;

   /* Handle negative exponent with a reciprocal at the end because
    * 10 is exact whereas .1 is inexact in base 2
    */
   if (power < 0)
   {
      if (power < DBL_MIN_10_EXP) return 0;
      recip = 1, power = -power;
   }

   if (power > 0)
   {
      /* Decompose power bitwise. */
      double mult = 10;
      do
      {
         if (power & 1) d *= mult;
         mult *= mult;
         power >>= 1;
      }
      while (power > 0);

      if (recip != 0) d = 1/d;
   }
   /* else power is 0 and d is 1 */

   return d;
}


png_icc_profile_error(png_const_structrp png_ptr, png_colorspacerp colorspace,
    png_const_charp name, png_alloc_size_t value, png_const_charp reason)
{
   size_t pos;
   char message[196]; /* see below for calculation */

   if (colorspace != NULL)
      colorspace->flags |= PNG_COLORSPACE_INVALID;

   pos = png_safecat(message, (sizeof message), 0, "profile '"); /* 9 chars */
   pos = png_safecat(message, pos+79, pos, name); /* Truncate to 79 chars */
   pos = png_safecat(message, (sizeof message), pos, "': "); /* +2 = 90 */
   if (is_ICC_signature(value) != 0)
   {
      /* So 'value' is at most 4 bytes and the following cast is safe */
      png_icc_tag_name(message+pos, (png_uint_32)value);
      pos += 6; /* total +8; less than the else clause */
      message[pos++] = ':';
      message[pos++] = ' ';
   }
#  ifdef PNG_WARNINGS_SUPPORTED
   else
      {
         char number[PNG_NUMBER_BUFFER_SIZE]; /* +24 = 114*/

         pos = png_safecat(message, (sizeof message), pos,
             png_format_number(number, number+(sizeof number),
             PNG_NUMBER_FORMAT_x, value));
         pos = png_safecat(message, (sizeof message), pos, "h: "); /*+2 = 116*/
      }
#  endif
   /* The 'reason' is an arbitrary message, allow +79 maximum 195 */
   pos = png_safecat(message, (sizeof message), pos, reason);
   PNG_UNUSED(pos)

   /* This is recoverable, but make it unconditionally an app_error on write to
    * avoid writing invalid ICC profiles into PNG files (i.e., we handle them
    * on read, with a warning, but on write unless the app turns off
    * application errors the PNG won't be written.)
    */
   png_chunk_report(png_ptr, message,
       (colorspace != NULL) ? PNG_CHUNK_ERROR : PNG_CHUNK_WRITE_ERROR);

   return 0;
}


png_user_version_check(png_structrp png_ptr, png_const_charp user_png_ver)
{
   /* Libpng versions 1.0.0 and later are binary compatible if the version
    * string matches through the second '.'; we must recompile any
    * applications that use any older library version.
    */

   if (user_png_ver != NULL)
   {
      int i = -1;
      int found_dots = 0;

      do
      {
         i++;
         if (user_png_ver[i] != PNG_LIBPNG_VER_STRING[i])
            png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;
         if (user_png_ver[i] == '.')
            found_dots++;
      } while (found_dots < 2 && user_png_ver[i] != 0 &&
            PNG_LIBPNG_VER_STRING[i] != 0);
   }

   else
      png_ptr->flags |= PNG_FLAG_LIBRARY_MISMATCH;

   if ((png_ptr->flags & PNG_FLAG_LIBRARY_MISMATCH) != 0)
   {
#ifdef PNG_WARNINGS_SUPPORTED
      size_t pos = 0;
      char m[128];

      pos = png_safecat(m, (sizeof m), pos,
          "Application built with libpng-");
      pos = png_safecat(m, (sizeof m), pos, user_png_ver);
      pos = png_safecat(m, (sizeof m), pos, " but running with ");
      pos = png_safecat(m, (sizeof m), pos, PNG_LIBPNG_VER_STRING);
      PNG_UNUSED(pos)

      png_warning(png_ptr, m);
#endif

#ifdef PNG_ERROR_NUMBERS_SUPPORTED
      png_ptr->flags = 0;
#endif

      return 0;
   }

   /* Success return. */
   return 1;
}


png_get_libpng_ver(png_const_structrp png_ptr)
{
   /* Version of *.c files used when building libpng */
   return png_get_header_ver(png_ptr);
}


PNG_FUNCTION(png_infop,PNGAPI
png_create_info_struct,(png_const_structrp png_ptr),PNG_ALLOCATED)
{
   png_inforp info_ptr;

   png_debug(1, "in png_create_info_struct");

   if (png_ptr == NULL)
      return NULL;

   /* Use the internal API that does not (or at least should not) error out, so
    * that this call always returns ok.  The application typically sets up the
    * error handling *after* creating the info_struct because this is the way it
    * has always been done in 'example.c'.
    */
   info_ptr = png_voidcast(png_inforp, png_malloc_base(png_ptr,
       (sizeof *info_ptr)));

   if (info_ptr != NULL)
      memset(info_ptr, 0, (sizeof *info_ptr));

   return info_ptr;
}


png_exp(png_fixed_point x)
{
   if (x > 0 && x <= 0xfffff) /* Else overflow or zero (underflow) */
   {
      /* Obtain a 4-bit approximation */
      png_uint_32 e = png_32bit_exp[(x >> 12) & 0x0f];

      /* Incorporate the low 12 bits - these decrease the returned value by
       * multiplying by a number less than 1 if the bit is set.  The multiplier
       * is determined by the above table and the shift. Notice that the values
       * converge on 45426 and this is used to allow linear interpolation of the
       * low bits.
       */
      if (x & 0x800)
         e -= (((e >> 16) * 44938U) +  16U) >> 5;

      if (x & 0x400)
         e -= (((e >> 16) * 45181U) +  32U) >> 6;

      if (x & 0x200)
         e -= (((e >> 16) * 45303U) +  64U) >> 7;

      if (x & 0x100)
         e -= (((e >> 16) * 45365U) + 128U) >> 8;

      if (x & 0x080)
         e -= (((e >> 16) * 45395U) + 256U) >> 9;

      if (x & 0x040)
         e -= (((e >> 16) * 45410U) + 512U) >> 10;

      /* And handle the low 6 bits in a single block. */
      e -= (((e >> 16) * 355U * (x & 0x3fU)) + 256U) >> 9;

      /* Handle the upper bits of x. */
      e >>= x >> 16;
      return e;
   }

   /* Check for overflow */
   if (x <= 0)
      return png_32bit_exp[0];

   /* Else underflow */
   return 0;
}


png_destroy_gamma_table(png_structrp png_ptr)
{
   png_free(png_ptr, png_ptr->gamma_table);
   png_ptr->gamma_table = NULL;

#ifdef PNG_16BIT_SUPPORTED
   if (png_ptr->gamma_16_table != NULL)
   {
      int i;
      int istop = (1 << (8 - png_ptr->gamma_shift));
      for (i = 0; i < istop; i++)
      {
         png_free(png_ptr, png_ptr->gamma_16_table[i]);
      }
   png_free(png_ptr, png_ptr->gamma_16_table);
   png_ptr->gamma_16_table = NULL;
   }
#endif /* 16BIT */

#if defined(PNG_READ_BACKGROUND_SUPPORTED) || \
   defined(PNG_READ_ALPHA_MODE_SUPPORTED) || \
   defined(PNG_READ_RGB_TO_GRAY_SUPPORTED)
   png_free(png_ptr, png_ptr->gamma_from_1);
   png_ptr->gamma_from_1 = NULL;
   png_free(png_ptr, png_ptr->gamma_to_1);
   png_ptr->gamma_to_1 = NULL;

#ifdef PNG_16BIT_SUPPORTED
   if (png_ptr->gamma_16_from_1 != NULL)
   {
      int i;
      int istop = (1 << (8 - png_ptr->gamma_shift));
      for (i = 0; i < istop; i++)
      {
         png_free(png_ptr, png_ptr->gamma_16_from_1[i]);
      }
   png_free(png_ptr, png_ptr->gamma_16_from_1);
   png_ptr->gamma_16_from_1 = NULL;
   }
   if (png_ptr->gamma_16_to_1 != NULL)
   {
      int i;
      int istop = (1 << (8 - png_ptr->gamma_shift));
      for (i = 0; i < istop; i++)
      {
         png_free(png_ptr, png_ptr->gamma_16_to_1[i]);
      }
   png_free(png_ptr, png_ptr->gamma_16_to_1);
   png_ptr->gamma_16_to_1 = NULL;
   }
#endif /* 16BIT */
#endif /* READ_BACKGROUND || READ_ALPHA_MODE || RGB_TO_GRAY */
}