uint64_t Tag::SimpleTag::Write(IMkvWriter* writer) const {
  uint64_t payload_size = EbmlElementSize(libwebm::kMkvTagName, tag_name_);

  payload_size += EbmlElementSize(libwebm::kMkvTagString, tag_string_);

  const uint64_t simple_tag_size =
      EbmlMasterElementSize(libwebm::kMkvSimpleTag, payload_size) +
      payload_size;

  if (writer == NULL)
    return simple_tag_size;

  const int64_t start = writer->Position();

  if (!WriteEbmlMasterElement(writer, libwebm::kMkvSimpleTag, payload_size))
    return 0;

  if (!WriteEbmlElement(writer, libwebm::kMkvTagName, tag_name_))
    return 0;

  if (!WriteEbmlElement(writer, libwebm::kMkvTagString, tag_string_))
    return 0;

  const int64_t stop = writer->Position();

  if (stop >= start && uint64_t(stop - start) != simple_tag_size)
    return 0;

  return simple_tag_size;
}


const PrimaryChromaticity* r() const { return r_; }


int MkvReader::Read(long long offset, long len, unsigned char* buffer) {
  if (m_file == NULL)
    return -1;

  if (offset < 0)
    return -1;

  if (len < 0)
    return -1;

  if (len == 0)
    return 0;

  if (offset >= m_length)
    return -1;

#ifdef _MSC_VER
  const int status = _fseeki64(m_file, offset, SEEK_SET);

  if (status)
    return -1;  // error
#elif defined(_WIN32)
  fseeko64(m_file, static_cast<off_t>(offset), SEEK_SET);
#else
  fseeko(m_file, static_cast<off_t>(offset), SEEK_SET);
#endif

  const size_t size = fread(buffer, 1, len, m_file);

  if (size < size_t(len))
    return -1;  // error

  return 0;  // success
}


uint64_t primaries() const { return primaries_; }


void set_date_utc(int64_t date_utc) { date_utc_ = date_utc; }


bool CopyChromaticity(const PrimaryChromaticity* src,
                      PrimaryChromaticityPtr* dst) {
  if (!dst)
    return false;

  dst->reset(new (std::nothrow) PrimaryChromaticity(src->x(), src->y()));
  if (!dst->get())
    return false;

  return true;
}


MkvWriter::MkvWriter() : file_(NULL), writer_owns_file_(true) {}


static u32 *gen12_emit_fini_breadcrumb(struct i915_request *request, u32 *cs)
{
	cs = gen8_emit_ggtt_write(cs,
				  request->fence.seqno,
				  i915_request_active_timeline(request)->hwsp_offset,
				  0);

	return gen12_emit_fini_breadcrumb_footer(request, cs);
}


static u32 *gen8_emit_fini_breadcrumb(struct i915_request *request, u32 *cs)
{
	cs = gen8_emit_ggtt_write(cs,
				  request->fence.seqno,
				  i915_request_active_timeline(request)->hwsp_offset,
				  0);

	return gen8_emit_fini_breadcrumb_footer(request, cs);
}


__execlists_update_reg_state(const struct intel_context *ce,
			     const struct intel_engine_cs *engine)
{
	struct intel_ring *ring = ce->ring;
	u32 *regs = ce->lrc_reg_state;

	GEM_BUG_ON(!intel_ring_offset_valid(ring, ring->head));
	GEM_BUG_ON(!intel_ring_offset_valid(ring, ring->tail));

	regs[CTX_RING_BUFFER_START] = i915_ggtt_offset(ring->vma);
	regs[CTX_RING_HEAD] = ring->head;
	regs[CTX_RING_TAIL] = ring->tail;

	/* RPCS */
	if (engine->class == RENDER_CLASS) {
		regs[CTX_R_PWR_CLK_STATE] =
			intel_sseu_make_rpcs(engine->i915, &ce->sseu);

		i915_oa_init_reg_state(ce, engine);
	}
}


void intel_execlists_set_default_submission(struct intel_engine_cs *engine)
{
	engine->submit_request = execlists_submit_request;
	engine->cancel_requests = execlists_cancel_requests;
	engine->schedule = i915_schedule;
	engine->execlists.tasklet.func = execlists_submission_tasklet;

	engine->reset.prepare = execlists_reset_prepare;
	engine->reset.reset = execlists_reset;
	engine->reset.finish = execlists_reset_finish;

	engine->park = execlists_park;
	engine->unpark = NULL;

	engine->flags |= I915_ENGINE_SUPPORTS_STATS;
	if (!intel_vgpu_active(engine->i915)) {
		engine->flags |= I915_ENGINE_HAS_SEMAPHORES;
		if (HAS_LOGICAL_RING_PREEMPTION(engine->i915))
			engine->flags |= I915_ENGINE_HAS_PREEMPTION;
	}

	if (INTEL_GEN(engine->i915) >= 12)
		engine->flags |= I915_ENGINE_HAS_RELATIVE_MMIO;
}


static u32 *emit_lri(u32 *batch, const struct lri *lri, unsigned int count)
{
	GEM_BUG_ON(!count || count > 63);

	*batch++ = MI_LOAD_REGISTER_IMM(count);
	do {
		*batch++ = i915_mmio_reg_offset(lri->reg);
		*batch++ = lri->value;
	} while (lri++, --count);
	*batch++ = MI_NOOP;

	return batch;
}


static void enable_execlists(struct intel_engine_cs *engine)
{
	u32 mode;

	assert_forcewakes_active(engine->uncore, FORCEWAKE_ALL);

	intel_engine_set_hwsp_writemask(engine, ~0u); /* HWSTAM */

	if (INTEL_GEN(engine->i915) >= 11)
		mode = _MASKED_BIT_ENABLE(GEN11_GFX_DISABLE_LEGACY_MODE);
	else
		mode = _MASKED_BIT_ENABLE(GFX_RUN_LIST_ENABLE);
	ENGINE_WRITE_FW(engine, RING_MODE_GEN7, mode);

	ENGINE_WRITE_FW(engine, RING_MI_MODE, _MASKED_BIT_DISABLE(STOP_RING));

	ENGINE_WRITE_FW(engine,
			RING_HWS_PGA,
			i915_ggtt_offset(engine->status_page.vma));
	ENGINE_POSTING_READ(engine, RING_HWS_PGA);
}


static void execlists_reset_prepare(struct intel_engine_cs *engine)
{
	struct intel_engine_execlists * const execlists = &engine->execlists;
	unsigned long flags;

	GEM_TRACE("%s: depth<-%d\n", engine->name,
		  atomic_read(&execlists->tasklet.count));

	/*
	 * Prevent request submission to the hardware until we have
	 * completed the reset in i915_gem_reset_finish(). If a request
	 * is completed by one engine, it may then queue a request
	 * to a second via its execlists->tasklet *just* as we are
	 * calling engine->resume() and also writing the ELSP.
	 * Turning off the execlists->tasklet until the reset is over
	 * prevents the race.
	 */
	__tasklet_disable_sync_once(&execlists->tasklet);
	GEM_BUG_ON(!reset_in_progress(execlists));

	/* And flush any current direct submission. */
	spin_lock_irqsave(&engine->active.lock, flags);
	spin_unlock_irqrestore(&engine->active.lock, flags);

	/*
	 * We stop engines, otherwise we might get failed reset and a
	 * dead gpu (on elk). Also as modern gpu as kbl can suffer
	 * from system hang if batchbuffer is progressing when
	 * the reset is issued, regardless of READY_TO_RESET ack.
	 * Thus assume it is best to stop engines on all gens
	 * where we have a gpu reset.
	 *
	 * WaKBLVECSSemaphoreWaitPoll:kbl (on ALL_ENGINES)
	 *
	 * FIXME: Wa for more modern gens needs to be validated
	 */
	intel_engine_stop_cs(engine);
}


static void gen8_logical_ring_disable_irq(struct intel_engine_cs *engine)
{
	ENGINE_WRITE(engine, RING_IMR, ~engine->irq_keep_mask);
}


static inline void write_desc(struct intel_engine_execlists *execlists, u64 desc, u32 port)
{
	if (execlists->ctrl_reg) {
		writel(lower_32_bits(desc), execlists->submit_reg + port * 2);
		writel(upper_32_bits(desc), execlists->submit_reg + port * 2 + 1);
	} else {
		writel(upper_32_bits(desc), execlists->submit_reg);
		writel(lower_32_bits(desc), execlists->submit_reg);
	}
}


assert_priority_queue(const struct i915_request *prev,
		      const struct i915_request *next)
{
	/*
	 * Without preemption, the prev may refer to the still active element
	 * which we refuse to let go.
	 *
	 * Even with preemption, there are times when we think it is better not
	 * to preempt and leave an ostensibly lower priority request in flight.
	 */
	if (i915_request_is_active(prev))
		return true;

	return rq_prio(prev) >= rq_prio(next);
}


set_redzone(void *vaddr, const struct intel_engine_cs *engine)
{
	if (!IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM))
		return;

	vaddr += engine->context_size;

	memset(vaddr, POISON_INUSE, I915_GTT_PAGE_SIZE);
}


static bool unexpected_starting_state(struct intel_engine_cs *engine)
{
	bool unexpected = false;

	if (ENGINE_READ_FW(engine, RING_MI_MODE) & STOP_RING) {
		DRM_DEBUG_DRIVER("STOP_RING still set in RING_MI_MODE\n");
		unexpected = true;
	}

	return unexpected;
}


static int __execlists_context_alloc(struct intel_context *ce,
				     struct intel_engine_cs *engine)
{
	struct drm_i915_gem_object *ctx_obj;
	struct intel_ring *ring;
	struct i915_vma *vma;
	u32 context_size;
	int ret;

	GEM_BUG_ON(ce->state);
	context_size = round_up(engine->context_size, I915_GTT_PAGE_SIZE);

	if (IS_ENABLED(CONFIG_DRM_I915_DEBUG_GEM))
		context_size += I915_GTT_PAGE_SIZE; /* for redzone */

	ctx_obj = i915_gem_object_create_shmem(engine->i915, context_size);
	if (IS_ERR(ctx_obj))
		return PTR_ERR(ctx_obj);

	vma = i915_vma_instance(ctx_obj, &engine->gt->ggtt->vm, NULL);
	if (IS_ERR(vma)) {
		ret = PTR_ERR(vma);
		goto error_deref_obj;
	}

	if (!ce->timeline) {
		struct intel_timeline *tl;

		tl = intel_timeline_create(engine->gt, NULL);
		if (IS_ERR(tl)) {
			ret = PTR_ERR(tl);
			goto error_deref_obj;
		}

		ce->timeline = tl;
	}

	ring = intel_engine_create_ring(engine, (unsigned long)ce->ring);
	if (IS_ERR(ring)) {
		ret = PTR_ERR(ring);
		goto error_deref_obj;
	}

	ret = populate_lr_context(ce, ctx_obj, engine, ring);
	if (ret) {
		DRM_DEBUG_DRIVER("Failed to populate LRC: %d\n", ret);
		goto error_ring_free;
	}

	ce->ring = ring;
	ce->state = vma;

	return 0;

error_ring_free:
	intel_ring_put(ring);
error_deref_obj:
	i915_gem_object_put(ctx_obj);
	return ret;
}


static void execlists_park(struct intel_engine_cs *engine)
{
	cancel_timer(&engine->execlists.timer);
	cancel_timer(&engine->execlists.preempt);
}


static void lrc_destroy_wa_ctx(struct intel_engine_cs *engine)
{
	i915_vma_unpin_and_release(&engine->wa_ctx.vma, 0);
}


execlists_schedule_in(struct i915_request *rq, int idx)
{
	struct intel_context * const ce = rq->hw_context;
	struct intel_engine_cs *old;

	GEM_BUG_ON(!intel_engine_pm_is_awake(rq->engine));
	trace_i915_request_in(rq, idx);

	old = READ_ONCE(ce->inflight);
	do {
		if (!old) {
			WRITE_ONCE(ce->inflight, __execlists_schedule_in(rq));
			break;
		}
	} while (!try_cmpxchg(&ce->inflight, &old, ptr_inc(old)));

	GEM_BUG_ON(intel_context_inflight(ce) != rq->engine);
	return i915_request_get(rq);
}


dirent_match_brace(const char *pattern, VALUE val, void *enc)
{
    struct dirent_brace_args *arg = (struct dirent_brace_args *)val;

    return dirent_match(pattern, enc, arg->name, arg->dp, arg->flags);
}


last_window(void)
{
    return (one_window() && first_tabpage->tp_next == NULL);
}


win_equal_rec(
    win_T	*next_curwin,	/* pointer to current window to be or NULL */
    int		current,	/* do only frame with current window */
    frame_T	*topfr,		/* frame to set size off */
    int		dir,		/* 'v', 'h' or 'b', see win_equal() */
    int		col,		/* horizontal position for frame */
    int		row,		/* vertical position for frame */
    int		width,		/* new width of frame */
    int		height)		/* new height of frame */
{
    int		n, m;
    int		extra_sep = 0;
    int		wincount, totwincount = 0;
    frame_T	*fr;
    int		next_curwin_size = 0;
    int		room = 0;
    int		new_size;
    int		has_next_curwin = 0;
    int		hnc;

    if (topfr->fr_layout == FR_LEAF)
    {
	/* Set the width/height of this frame.
	 * Redraw when size or position changes */
	if (topfr->fr_height != height || topfr->fr_win->w_winrow != row
		|| topfr->fr_width != width || topfr->fr_win->w_wincol != col
	   )
	{
	    topfr->fr_win->w_winrow = row;
	    frame_new_height(topfr, height, FALSE, FALSE);
	    topfr->fr_win->w_wincol = col;
	    frame_new_width(topfr, width, FALSE, FALSE);
	    redraw_all_later(NOT_VALID);
	}
    }
    else if (topfr->fr_layout == FR_ROW)
    {
	topfr->fr_width = width;
	topfr->fr_height = height;

	if (dir != 'v')			/* equalize frame widths */
	{
	    /* Compute the maximum number of windows horizontally in this
	     * frame. */
	    n = frame_minwidth(topfr, NOWIN);
	    /* add one for the rightmost window, it doesn't have a separator */
	    if (col + width == Columns)
		extra_sep = 1;
	    else
		extra_sep = 0;
	    totwincount = (n + extra_sep) / (p_wmw + 1);
	    has_next_curwin = frame_has_win(topfr, next_curwin);

	    /*
	     * Compute width for "next_curwin" window and room available for
	     * other windows.
	     * "m" is the minimal width when counting p_wiw for "next_curwin".
	     */
	    m = frame_minwidth(topfr, next_curwin);
	    room = width - m;
	    if (room < 0)
	    {
		next_curwin_size = p_wiw + room;
		room = 0;
	    }
	    else
	    {
		next_curwin_size = -1;
		FOR_ALL_FRAMES(fr, topfr->fr_child)
		{
		    /* If 'winfixwidth' set keep the window width if
		     * possible.
		     * Watch out for this window being the next_curwin. */
		    if (frame_fixed_width(fr))
		    {
			n = frame_minwidth(fr, NOWIN);
			new_size = fr->fr_width;
			if (frame_has_win(fr, next_curwin))
			{
			    room += p_wiw - p_wmw;
			    next_curwin_size = 0;
			    if (new_size < p_wiw)
				new_size = p_wiw;
			}
			else
			    /* These windows don't use up room. */
			    totwincount -= (n + (fr->fr_next == NULL
					      ? extra_sep : 0)) / (p_wmw + 1);
			room -= new_size - n;
			if (room < 0)
			{
			    new_size += room;
			    room = 0;
			}
			fr->fr_newwidth = new_size;
		    }
		}
		if (next_curwin_size == -1)
		{
		    if (!has_next_curwin)
			next_curwin_size = 0;
		    else if (totwincount > 1
			    && (room + (totwincount - 2))
						  / (totwincount - 1) > p_wiw)
		    {
			/* Can make all windows wider than 'winwidth', spread
			 * the room equally. */
			next_curwin_size = (room + p_wiw
					    + (totwincount - 1) * p_wmw
					    + (totwincount - 1)) / totwincount;
			room -= next_curwin_size - p_wiw;
		    }
		    else
			next_curwin_size = p_wiw;
		}
	    }

	    if (has_next_curwin)
		--totwincount;		/* don't count curwin */
	}

	FOR_ALL_FRAMES(fr, topfr->fr_child)
	{
	    wincount = 1;
	    if (fr->fr_next == NULL)
		/* last frame gets all that remains (avoid roundoff error) */
		new_size = width;
	    else if (dir == 'v')
		new_size = fr->fr_width;
	    else if (frame_fixed_width(fr))
	    {
		new_size = fr->fr_newwidth;
		wincount = 0;	    /* doesn't count as a sizeable window */
	    }
	    else
	    {
		/* Compute the maximum number of windows horiz. in "fr". */
		n = frame_minwidth(fr, NOWIN);
		wincount = (n + (fr->fr_next == NULL ? extra_sep : 0))
								/ (p_wmw + 1);
		m = frame_minwidth(fr, next_curwin);
		if (has_next_curwin)
		    hnc = frame_has_win(fr, next_curwin);
		else
		    hnc = FALSE;
		if (hnc)	    /* don't count next_curwin */
		    --wincount;
		if (totwincount == 0)
		    new_size = room;
		else
		    new_size = (wincount * room + ((unsigned)totwincount >> 1))
								/ totwincount;
		if (hnc)	    /* add next_curwin size */
		{
		    next_curwin_size -= p_wiw - (m - n);
		    new_size += next_curwin_size;
		    room -= new_size - next_curwin_size;
		}
		else
		    room -= new_size;
		new_size += n;
	    }

	    /* Skip frame that is full width when splitting or closing a
	     * window, unless equalizing all frames. */
	    if (!current || dir != 'v' || topfr->fr_parent != NULL
		    || (new_size != fr->fr_width)
		    || frame_has_win(fr, next_curwin))
		win_equal_rec(next_curwin, current, fr, dir, col, row,
							    new_size, height);
	    col += new_size;
	    width -= new_size;
	    totwincount -= wincount;
	}
    }
    else /* topfr->fr_layout == FR_COL */
    {
	topfr->fr_width = width;
	topfr->fr_height = height;

	if (dir != 'h')			/* equalize frame heights */
	{
	    /* Compute maximum number of windows vertically in this frame. */
	    n = frame_minheight(topfr, NOWIN);
	    /* add one for the bottom window if it doesn't have a statusline */
	    if (row + height == cmdline_row && p_ls == 0)
		extra_sep = 1;
	    else
		extra_sep = 0;
	    totwincount = (n + extra_sep) / (p_wmh + 1);
	    has_next_curwin = frame_has_win(topfr, next_curwin);

	    /*
	     * Compute height for "next_curwin" window and room available for
	     * other windows.
	     * "m" is the minimal height when counting p_wh for "next_curwin".
	     */
	    m = frame_minheight(topfr, next_curwin);
	    room = height - m;
	    if (room < 0)
	    {
		/* The room is less then 'winheight', use all space for the
		 * current window. */
		next_curwin_size = p_wh + room;
		room = 0;
	    }
	    else
	    {
		next_curwin_size = -1;
		FOR_ALL_FRAMES(fr, topfr->fr_child)
		{
		    /* If 'winfixheight' set keep the window height if
		     * possible.
		     * Watch out for this window being the next_curwin. */
		    if (frame_fixed_height(fr))
		    {
			n = frame_minheight(fr, NOWIN);
			new_size = fr->fr_height;
			if (frame_has_win(fr, next_curwin))
			{
			    room += p_wh - p_wmh;
			    next_curwin_size = 0;
			    if (new_size < p_wh)
				new_size = p_wh;
			}
			else
			    /* These windows don't use up room. */
			    totwincount -= (n + (fr->fr_next == NULL
					      ? extra_sep : 0)) / (p_wmh + 1);
			room -= new_size - n;
			if (room < 0)
			{
			    new_size += room;
			    room = 0;
			}
			fr->fr_newheight = new_size;
		    }
		}
		if (next_curwin_size == -1)
		{
		    if (!has_next_curwin)
			next_curwin_size = 0;
		    else if (totwincount > 1
			    && (room + (totwincount - 2))
						   / (totwincount - 1) > p_wh)
		    {
			/* can make all windows higher than 'winheight',
			 * spread the room equally. */
			next_curwin_size = (room + p_wh
					   + (totwincount - 1) * p_wmh
					   + (totwincount - 1)) / totwincount;
			room -= next_curwin_size - p_wh;
		    }
		    else
			next_curwin_size = p_wh;
		}
	    }

	    if (has_next_curwin)
		--totwincount;		/* don't count curwin */
	}

	FOR_ALL_FRAMES(fr, topfr->fr_child)
	{
	    wincount = 1;
	    if (fr->fr_next == NULL)
		/* last frame gets all that remains (avoid roundoff error) */
		new_size = height;
	    else if (dir == 'h')
		new_size = fr->fr_height;
	    else if (frame_fixed_height(fr))
	    {
		new_size = fr->fr_newheight;
		wincount = 0;	    /* doesn't count as a sizeable window */
	    }
	    else
	    {
		/* Compute the maximum number of windows vert. in "fr". */
		n = frame_minheight(fr, NOWIN);
		wincount = (n + (fr->fr_next == NULL ? extra_sep : 0))
								/ (p_wmh + 1);
		m = frame_minheight(fr, next_curwin);
		if (has_next_curwin)
		    hnc = frame_has_win(fr, next_curwin);
		else
		    hnc = FALSE;
		if (hnc)	    /* don't count next_curwin */
		    --wincount;
		if (totwincount == 0)
		    new_size = room;
		else
		    new_size = (wincount * room + ((unsigned)totwincount >> 1))
								/ totwincount;
		if (hnc)	    /* add next_curwin size */
		{
		    next_curwin_size -= p_wh - (m - n);
		    new_size += next_curwin_size;
		    room -= new_size - next_curwin_size;
		}
		else
		    room -= new_size;
		new_size += n;
	    }
	    /* Skip frame that is full width when splitting or closing a
	     * window, unless equalizing all frames. */
	    if (!current || dir != 'h' || topfr->fr_parent != NULL
		    || (new_size != fr->fr_height)
		    || frame_has_win(fr, next_curwin))
		win_equal_rec(next_curwin, current, fr, dir, col, row,
							     width, new_size);
	    row += new_size;
	    height -= new_size;
	    totwincount -= wincount;
	}
    }
}


win_free_all(void)
{
    int		dummy;

    while (first_tabpage->tp_next != NULL)
	tabpage_close(TRUE);

    if (aucmd_win != NULL)
    {
	(void)win_free_mem(aucmd_win, &dummy, NULL);
	aucmd_win = NULL;
    }
# ifdef FEAT_TEXT_PROP
    close_all_popups();
# endif

    while (firstwin != NULL)
	(void)win_free_mem(firstwin, &dummy, NULL);

    /* No window should be used after this. Set curwin to NULL to crash
     * instead of using freed memory. */
    curwin = NULL;
}


win_setminheight(void)
{
    int		room;
    int		needed;
    int		first = TRUE;

    // loop until there is a 'winminheight' that is possible
    while (p_wmh > 0)
    {
	room = Rows - p_ch;
	needed = frame_minheight(topframe, NULL);
	if (room >= needed)
	    break;
	--p_wmh;
	if (first)
	{
	    emsg(_(e_noroom));
	    first = FALSE;
	}
    }
}


win_goto_ver(
    int		up,		// TRUE to go to win above
    long	count)
{
    win_T	*win;

    win = win_vert_neighbor(curtab, curwin, up, count);
    if (win != NULL)
	win_goto(win);
}


win_enter(win_T *wp, int undo_sync)
{
    win_enter_ext(wp, undo_sync, FALSE, FALSE, TRUE, TRUE);
}


win_equal(
    win_T	*next_curwin,	/* pointer to current window to be or NULL */
    int		current,	/* do only frame with current window */
    int		dir)		/* 'v' for vertically, 'h' for horizontally,
				   'b' for both, 0 for using p_ead */
{
    if (dir == 0)
	dir = *p_ead;
    win_equal_rec(next_curwin == NULL ? curwin : next_curwin, current,
		      topframe, dir, 0, tabline_height(),
					   (int)Columns, topframe->fr_height);
}


frame_new_height(
    frame_T	*topfrp,
    int		height,
    int		topfirst,	/* resize topmost contained frame first */
    int		wfh)		/* obey 'winfixheight' when there is a choice;
				   may cause the height not to be set */
{
    frame_T	*frp;
    int		extra_lines;
    int		h;

    if (topfrp->fr_win != NULL)
    {
	/* Simple case: just one window. */
	win_new_height(topfrp->fr_win,
				    height - topfrp->fr_win->w_status_height
					      - WINBAR_HEIGHT(topfrp->fr_win));
    }
    else if (topfrp->fr_layout == FR_ROW)
    {
	do
	{
	    /* All frames in this row get the same new height. */
	    FOR_ALL_FRAMES(frp, topfrp->fr_child)
	    {
		frame_new_height(frp, height, topfirst, wfh);
		if (frp->fr_height > height)
		{
		    /* Could not fit the windows, make the whole row higher. */
		    height = frp->fr_height;
		    break;
		}
	    }
	}
	while (frp != NULL);
    }
    else    /* fr_layout == FR_COL */
    {
	/* Complicated case: Resize a column of frames.  Resize the bottom
	 * frame first, frames above that when needed. */

	frp = topfrp->fr_child;
	if (wfh)
	    /* Advance past frames with one window with 'wfh' set. */
	    while (frame_fixed_height(frp))
	    {
		frp = frp->fr_next;
		if (frp == NULL)
		    return;	    /* no frame without 'wfh', give up */
	    }
	if (!topfirst)
	{
	    /* Find the bottom frame of this column */
	    while (frp->fr_next != NULL)
		frp = frp->fr_next;
	    if (wfh)
		/* Advance back for frames with one window with 'wfh' set. */
		while (frame_fixed_height(frp))
		    frp = frp->fr_prev;
	}

	extra_lines = height - topfrp->fr_height;
	if (extra_lines < 0)
	{
	    /* reduce height of contained frames, bottom or top frame first */
	    while (frp != NULL)
	    {
		h = frame_minheight(frp, NULL);
		if (frp->fr_height + extra_lines < h)
		{
		    extra_lines += frp->fr_height - h;
		    frame_new_height(frp, h, topfirst, wfh);
		}
		else
		{
		    frame_new_height(frp, frp->fr_height + extra_lines,
							       topfirst, wfh);
		    break;
		}
		if (topfirst)
		{
		    do
			frp = frp->fr_next;
		    while (wfh && frp != NULL && frame_fixed_height(frp));
		}
		else
		{
		    do
			frp = frp->fr_prev;
		    while (wfh && frp != NULL && frame_fixed_height(frp));
		}
		/* Increase "height" if we could not reduce enough frames. */
		if (frp == NULL)
		    height -= extra_lines;
	    }
	}
	else if (extra_lines > 0)
	{
	    /* increase height of bottom or top frame */
	    frame_new_height(frp, frp->fr_height + extra_lines, topfirst, wfh);
	}
    }
    topfrp->fr_height = height;
}


clear_snapshot(tabpage_T *tp, int idx)
{
    clear_snapshot_rec(tp->tp_snapshot[idx]);
    tp->tp_snapshot[idx] = NULL;
}


win_horz_neighbor(tabpage_T *tp, win_T *wp, int left, long count)
{
    frame_T	*fr;
    frame_T	*nfr;
    frame_T	*foundfr;

    foundfr = wp->w_frame;
    while (count--)
    {
	/*
	 * First go upwards in the tree of frames until we find a left or
	 * right neighbor.
	 */
	fr = foundfr;
	for (;;)
	{
	    if (fr == tp->tp_topframe)
		goto end;
	    if (left)
		nfr = fr->fr_prev;
	    else
		nfr = fr->fr_next;
	    if (fr->fr_parent->fr_layout == FR_ROW && nfr != NULL)
		break;
	    fr = fr->fr_parent;
	}

	/*
	 * Now go downwards to find the leftmost or rightmost frame in it.
	 */
	for (;;)
	{
	    if (nfr->fr_layout == FR_LEAF)
	    {
		foundfr = nfr;
		break;
	    }
	    fr = nfr->fr_child;
	    if (nfr->fr_layout == FR_COL)
	    {
		/* Find the frame at the cursor row. */
		while (fr->fr_next != NULL
			&& frame2win(fr)->w_winrow + fr->fr_height
					 <= wp->w_winrow + wp->w_wrow)
		    fr = fr->fr_next;
	    }
	    if (nfr->fr_layout == FR_ROW && left)
		while (fr->fr_next != NULL)
		    fr = fr->fr_next;
	    nfr = fr;
	}
    }
end:
    return foundfr != NULL ? foundfr->fr_win : NULL;
}


win_goto_hor(
    int		left,		// TRUE to go to left win
    long	count)
{
    win_T	*win;

    win = win_horz_neighbor(curtab, curwin, left, count);
    if (win != NULL)
	win_goto(win);
}


win_setwidth(int width)
{
    win_setwidth_win(width, curwin);
}


buf_jump_open_win(buf_T *buf)
{
    win_T	*wp = NULL;

    if (curwin->w_buffer == buf)
	wp = curwin;
    else
	FOR_ALL_WINDOWS(wp)
	    if (wp->w_buffer == buf)
		break;
    if (wp != NULL)
	win_enter(wp, FALSE);
    return wp;
}


win_move_after(win_T *win1, win_T *win2)
{
    int		height;

    /* check if the arguments are reasonable */
    if (win1 == win2)
	return;

    /* check if there is something to do */
    if (win2->w_next != win1)
    {
	if (win1->w_frame->fr_parent != win2->w_frame->fr_parent)
	{
	    iemsg("INTERNAL: trying to move a window into another frame");
	    return;
	}

	/* may need move the status line/vertical separator of the last window
	 * */
	if (win1 == lastwin)
	{
	    height = win1->w_prev->w_status_height;
	    win1->w_prev->w_status_height = win1->w_status_height;
	    win1->w_status_height = height;
	    if (win1->w_prev->w_vsep_width == 1)
	    {
		/* Remove the vertical separator from the last-but-one window,
		 * add it to the last window.  Adjust the frame widths. */
		win1->w_prev->w_vsep_width = 0;
		win1->w_prev->w_frame->fr_width -= 1;
		win1->w_vsep_width = 1;
		win1->w_frame->fr_width += 1;
	    }
	}
	else if (win2 == lastwin)
	{
	    height = win1->w_status_height;
	    win1->w_status_height = win2->w_status_height;
	    win2->w_status_height = height;
	    if (win1->w_vsep_width == 1)
	    {
		/* Remove the vertical separator from win1, add it to the last
		 * window, win2.  Adjust the frame widths. */
		win2->w_vsep_width = 1;
		win2->w_frame->fr_width += 1;
		win1->w_vsep_width = 0;
		win1->w_frame->fr_width -= 1;
	    }
	}
	win_remove(win1, NULL);
	frame_remove(win1->w_frame);
	win_append(win2, win1);
	frame_append(win2->w_frame, win1->w_frame);

	(void)win_comp_pos();	/* recompute w_winrow for all windows */
	redraw_later(NOT_VALID);
    }
    win_enter(win1, FALSE);
}


int ImagingLibTiffEncode(Imaging im, ImagingCodecState state, UINT8* buffer, int bytes) {
    /* One shot encoder. Encode everything to the tiff in the clientstate.
       If we're running off of a FD, then run once, we're good, everything
       ends up in the file, we close and we're done.

       If we're going to memory, then we need to write the whole file into memory, then
       parcel it back out to the pystring buffer bytes at a time.

    */

    TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
    TIFF *tiff = clientstate->tiff;

    TRACE(("in encoder: bytes %d\n", bytes));
    TRACE(("State: count %d, state %d, x %d, y %d, ystep %d\n", state->count, state->state,
           state->x, state->y, state->ystep));
    TRACE(("State: xsize %d, ysize %d, xoff %d, yoff %d \n", state->xsize, state->ysize,
           state->xoff, state->yoff));
    TRACE(("State: bits %d, bytes %d \n", state->bits, state->bytes));
    TRACE(("Buffer: %p: %c%c%c%c\n", buffer, (char)buffer[0], (char)buffer[1],(char)buffer[2], (char)buffer[3]));
    TRACE(("State->Buffer: %c%c%c%c\n", (char)state->buffer[0], (char)state->buffer[1],(char)state->buffer[2], (char)state->buffer[3]));
    TRACE(("Image: mode %s, type %d, bands: %d, xsize %d, ysize %d \n",
           im->mode, im->type, im->bands, im->xsize, im->ysize));
    TRACE(("Image: image8 %p, image32 %p, image %p, block %p \n",
           im->image8, im->image32, im->image, im->block));
    TRACE(("Image: pixelsize: %d, linesize %d \n",
           im->pixelsize, im->linesize));

    dump_state(clientstate);

    if (state->state == 0) {
        TRACE(("Encoding line bt line"));
        while(state->y < state->ysize){
            state->shuffle(state->buffer,
                           (UINT8*) im->image[state->y + state->yoff] +
                           state->xoff * im->pixelsize,
                           state->xsize);

            if (TIFFWriteScanline(tiff, (tdata_t)(state->buffer), (uint32)state->y, 0) == -1) {
                TRACE(("Encode Error, row %d\n", state->y));
                state->errcode = IMAGING_CODEC_BROKEN;
                TIFFClose(tiff);
                if (!clientstate->fp){
                    free(clientstate->data);
                }
                return -1;
            }
            state->y++;
        }

        if (state->y == state->ysize) {
            state->state=1;

            TRACE(("Flushing \n"));
            if (!TIFFFlush(tiff)) {
                TRACE(("Error flushing the tiff"));
                // likely reason is memory.
                state->errcode = IMAGING_CODEC_MEMORY;
                TIFFClose(tiff);
                if (!clientstate->fp){
                    free(clientstate->data);
                }
                return -1;
            }
            TRACE(("Closing \n"));
            TIFFClose(tiff);
            // reset the clientstate metadata to use it to read out the buffer.
            clientstate->loc = 0;
            clientstate->size = clientstate->eof; // redundant?
        }
    }

    if (state->state == 1 && !clientstate->fp) {
        int read = (int)_tiffReadProc(clientstate, (tdata_t)buffer, (tsize_t)bytes);
        TRACE(("Buffer: %p: %c%c%c%c\n", buffer, (char)buffer[0], (char)buffer[1],(char)buffer[2], (char)buffer[3]));
        if (clientstate->loc == clientstate->eof) {
            TRACE(("Hit EOF, calling an end, freeing data"));
            state->errcode = IMAGING_CODEC_END;
            free(clientstate->data);
        }
        return read;
    }

    state->errcode = IMAGING_CODEC_END;
    return 0;
}


void dump_state(const TIFFSTATE *state){
	TRACE(("State: Location %u size %d eof %d data: %p ifd: %d\n", (uint)state->loc,
		   (int)state->size, (uint)state->eof, state->data, state->ifd));
}


void sta_set_rate_info_tx(struct sta_info *sta,
			  const struct ieee80211_tx_rate *rate,
			  struct rate_info *rinfo)
{
	rinfo->flags = 0;
	if (rate->flags & IEEE80211_TX_RC_MCS) {
		rinfo->flags |= RATE_INFO_FLAGS_MCS;
		rinfo->mcs = rate->idx;
	} else if (rate->flags & IEEE80211_TX_RC_VHT_MCS) {
		rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS;
		rinfo->mcs = ieee80211_rate_get_vht_mcs(rate);
		rinfo->nss = ieee80211_rate_get_vht_nss(rate);
	} else {
		struct ieee80211_supported_band *sband;
		int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
		u16 brate;

		sband = ieee80211_get_sband(sta->sdata);
		if (sband) {
			brate = sband->bitrates[rate->idx].bitrate;
			rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
		}
	}
	if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
		rinfo->bw = RATE_INFO_BW_40;
	else if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
		rinfo->bw = RATE_INFO_BW_80;
	else if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH)
		rinfo->bw = RATE_INFO_BW_160;
	else
		rinfo->bw = RATE_INFO_BW_20;
	if (rate->flags & IEEE80211_TX_RC_SHORT_GI)
		rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
}


static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
					 struct cfg80211_chan_def *chandef)
{
	struct ieee80211_local *local = wiphy_priv(wiphy);
	struct ieee80211_sub_if_data *sdata;
	int ret = 0;

	if (cfg80211_chandef_identical(&local->monitor_chandef, chandef))
		return 0;

	mutex_lock(&local->mtx);
	if (local->use_chanctx) {
		sdata = rtnl_dereference(local->monitor_sdata);
		if (sdata) {
			ieee80211_vif_release_channel(sdata);
			ret = ieee80211_vif_use_channel(sdata, chandef,
					IEEE80211_CHANCTX_EXCLUSIVE);
		}
	} else if (local->open_count == local->monitors) {
		local->_oper_chandef = *chandef;
		ieee80211_hw_config(local, 0);
	}

	if (ret == 0)
		local->monitor_chandef = *chandef;
	mutex_unlock(&local->mtx);

	return ret;
}


struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,
				  const u8 *addr)
{
	struct ieee80211_local *local = sdata->local;
	struct rhlist_head *tmp;
	struct sta_info *sta;

	rcu_read_lock();
	for_each_sta_info(local, addr, sta, tmp) {
		if (sta->sdata == sdata ||
		    (sta->sdata->bss && sta->sdata->bss == sdata->bss)) {
			rcu_read_unlock();
			/* this is safe as the caller must already hold
			 * another rcu read section or the mutex
			 */
			return sta;
		}
	}
	rcu_read_unlock();
	return NULL;
}


static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
				int idx, u8 *dst, u8 *next_hop,
				struct mpath_info *pinfo)
{
	struct ieee80211_sub_if_data *sdata;
	struct mesh_path *mpath;

	sdata = IEEE80211_DEV_TO_SUB_IF(dev);

	rcu_read_lock();
	mpath = mesh_path_lookup_by_idx(sdata, idx);
	if (!mpath) {
		rcu_read_unlock();
		return -ENOENT;
	}
	memcpy(dst, mpath->dst, ETH_ALEN);
	mpath_set_pinfo(mpath, next_hop, pinfo);
	rcu_read_unlock();
	return 0;
}


static void sta_update_codel_params(struct sta_info *sta, u32 thr)
{
	if (!sta->sdata->local->ops->wake_tx_queue)
		return;

	if (thr && thr < STA_SLOW_THRESHOLD * sta->local->num_sta) {
		sta->cparams.target = MS2TIME(50);
		sta->cparams.interval = MS2TIME(300);
		sta->cparams.ecn = false;
	} else {
		sta->cparams.target = MS2TIME(20);
		sta->cparams.interval = MS2TIME(100);
		sta->cparams.ecn = true;
	}
}


static inline void __bss_tim_set(u8 *tim, u16 id)
{
	/*
	 * This format has been mandated by the IEEE specifications,
	 * so this line may not be changed to use the __set_bit() format.
	 */
	tim[id / 8] |= (1 << (id % 8));
}


static int ieee80211_add_tx_ts(struct wiphy *wiphy, struct net_device *dev,
			       u8 tsid, const u8 *peer, u8 up,
			       u16 admitted_time)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
	int ac = ieee802_1d_to_ac[up];

	if (sdata->vif.type != NL80211_IFTYPE_STATION)
		return -EOPNOTSUPP;

	if (!(sdata->wmm_acm & BIT(up)))
		return -EINVAL;

	if (ifmgd->tx_tspec[ac].admitted_time)
		return -EBUSY;

	if (admitted_time) {
		ifmgd->tx_tspec[ac].admitted_time = 32 * admitted_time;
		ifmgd->tx_tspec[ac].tsid = tsid;
		ifmgd->tx_tspec[ac].up = up;
	}

	return 0;
}


static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev,
			   struct cfg80211_assoc_request *req)
{
	return ieee80211_mgd_assoc(IEEE80211_DEV_TO_SUB_IF(dev), req);
}


static int ieee80211_dump_survey(struct wiphy *wiphy, struct net_device *dev,
				 int idx, struct survey_info *survey)
{
	struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);

	return drv_get_survey(local, idx, survey);
}


static int __must_check __sta_info_destroy_part1(struct sta_info *sta)
{
	struct ieee80211_local *local;
	struct ieee80211_sub_if_data *sdata;
	int ret;

	might_sleep();

	if (!sta)
		return -ENOENT;

	local = sta->local;
	sdata = sta->sdata;

	lockdep_assert_held(&local->sta_mtx);

	/*
	 * Before removing the station from the driver and
	 * rate control, it might still start new aggregation
	 * sessions -- block that to make sure the tear-down
	 * will be sufficient.
	 */
	set_sta_flag(sta, WLAN_STA_BLOCK_BA);
	ieee80211_sta_tear_down_BA_sessions(sta, AGG_STOP_DESTROY_STA);

	/*
	 * Before removing the station from the driver there might be pending
	 * rx frames on RSS queues sent prior to the disassociation - wait for
	 * all such frames to be processed.
	 */
	drv_sync_rx_queues(local, sta);

	ret = sta_info_hash_del(local, sta);
	if (WARN_ON(ret))
		return ret;

	/*
	 * for TDLS peers, make sure to return to the base channel before
	 * removal.
	 */
	if (test_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL)) {
		drv_tdls_cancel_channel_switch(local, sdata, &sta->sta);
		clear_sta_flag(sta, WLAN_STA_TDLS_OFF_CHANNEL);
	}

	list_del_rcu(&sta->list);
	sta->removed = true;

	drv_sta_pre_rcu_remove(local, sta->sdata, sta);

	if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
	    rcu_access_pointer(sdata->u.vlan.sta) == sta)
		RCU_INIT_POINTER(sdata->u.vlan.sta, NULL);

	return 0;
}


struct ieee80211_sta *ieee80211_find_sta(struct ieee80211_vif *vif,
					 const u8 *addr)
{
	struct sta_info *sta;

	if (!vif)
		return NULL;

	sta = sta_info_get_bss(vif_to_sdata(vif), addr);
	if (!sta)
		return NULL;

	if (!sta->uploaded)
		return NULL;

	return &sta->sta;
}


static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
			       const struct mesh_config *conf,
			       const struct mesh_setup *setup)
{
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
	struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
	int err;

	memcpy(&ifmsh->mshcfg, conf, sizeof(struct mesh_config));
	err = copy_mesh_setup(ifmsh, setup);
	if (err)
		return err;

	sdata->control_port_over_nl80211 = setup->control_port_over_nl80211;

	/* can mesh use other SMPS modes? */
	sdata->smps_mode = IEEE80211_SMPS_OFF;
	sdata->needed_rx_chains = sdata->local->rx_chains;

	mutex_lock(&sdata->local->mtx);
	err = ieee80211_vif_use_channel(sdata, &setup->chandef,
					IEEE80211_CHANCTX_SHARED);
	mutex_unlock(&sdata->local->mtx);
	if (err)
		return err;

	return ieee80211_start_mesh(sdata);
}


struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
				const u8 *addr, gfp_t gfp)
{
	struct ieee80211_local *local = sdata->local;
	struct ieee80211_hw *hw = &local->hw;
	struct sta_info *sta;
	int i;

	sta = kzalloc(sizeof(*sta) + hw->sta_data_size, gfp);
	if (!sta)
		return NULL;

	if (ieee80211_hw_check(hw, USES_RSS)) {
		sta->pcpu_rx_stats =
			alloc_percpu_gfp(struct ieee80211_sta_rx_stats, gfp);
		if (!sta->pcpu_rx_stats)
			goto free;
	}

	spin_lock_init(&sta->lock);
	spin_lock_init(&sta->ps_lock);
	INIT_WORK(&sta->drv_deliver_wk, sta_deliver_ps_frames);
	INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
	mutex_init(&sta->ampdu_mlme.mtx);
#ifdef CONFIG_MAC80211_MESH
	if (ieee80211_vif_is_mesh(&sdata->vif)) {
		sta->mesh = kzalloc(sizeof(*sta->mesh), gfp);
		if (!sta->mesh)
			goto free;
		sta->mesh->plink_sta = sta;
		spin_lock_init(&sta->mesh->plink_lock);
		if (ieee80211_vif_is_mesh(&sdata->vif) &&
		    !sdata->u.mesh.user_mpm)
			timer_setup(&sta->mesh->plink_timer, mesh_plink_timer,
				    0);
		sta->mesh->nonpeer_pm = NL80211_MESH_POWER_ACTIVE;
	}
#endif

	memcpy(sta->addr, addr, ETH_ALEN);
	memcpy(sta->sta.addr, addr, ETH_ALEN);
	sta->sta.max_rx_aggregation_subframes =
		local->hw.max_rx_aggregation_subframes;

	/* Extended Key ID needs to install keys for keyid 0 and 1 Rx-only.
	 * The Tx path starts to use a key as soon as the key slot ptk_idx
	 * references to is not NULL. To not use the initial Rx-only key
	 * prematurely for Tx initialize ptk_idx to an impossible PTK keyid
	 * which always will refer to a NULL key.
	 */
	BUILD_BUG_ON(ARRAY_SIZE(sta->ptk) <= INVALID_PTK_KEYIDX);
	sta->ptk_idx = INVALID_PTK_KEYIDX;

	sta->local = local;
	sta->sdata = sdata;
	sta->rx_stats.last_rx = jiffies;

	u64_stats_init(&sta->rx_stats.syncp);

	sta->sta_state = IEEE80211_STA_NONE;

	/* Mark TID as unreserved */
	sta->reserved_tid = IEEE80211_TID_UNRESERVED;

	sta->last_connected = ktime_get_seconds();
	ewma_signal_init(&sta->rx_stats_avg.signal);
	ewma_avg_signal_init(&sta->status_stats.avg_ack_signal);
	for (i = 0; i < ARRAY_SIZE(sta->rx_stats_avg.chain_signal); i++)
		ewma_signal_init(&sta->rx_stats_avg.chain_signal[i]);

	if (local->ops->wake_tx_queue) {
		void *txq_data;
		int size = sizeof(struct txq_info) +
			   ALIGN(hw->txq_data_size, sizeof(void *));

		txq_data = kcalloc(ARRAY_SIZE(sta->sta.txq), size, gfp);
		if (!txq_data)
			goto free;

		for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
			struct txq_info *txq = txq_data + i * size;

			/* might not do anything for the bufferable MMPDU TXQ */
			ieee80211_txq_init(sdata, sta, txq, i);
		}
	}

	if (sta_prepare_rate_control(local, sta, gfp))
		goto free_txq;

	sta->airtime_weight = IEEE80211_DEFAULT_AIRTIME_WEIGHT;

	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
		skb_queue_head_init(&sta->ps_tx_buf[i]);
		skb_queue_head_init(&sta->tx_filtered[i]);
		sta->airtime[i].deficit = sta->airtime_weight;
	}

	for (i = 0; i < IEEE80211_NUM_TIDS; i++)
		sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX);

	for (i = 0; i < NUM_NL80211_BANDS; i++) {
		u32 mandatory = 0;
		int r;

		if (!hw->wiphy->bands[i])
			continue;

		switch (i) {
		case NL80211_BAND_2GHZ:
			/*
			 * We use both here, even if we cannot really know for
			 * sure the station will support both, but the only use
			 * for this is when we don't know anything yet and send
			 * management frames, and then we'll pick the lowest
			 * possible rate anyway.
			 * If we don't include _G here, we cannot find a rate
			 * in P2P, and thus trigger the WARN_ONCE() in rate.c
			 */
			mandatory = IEEE80211_RATE_MANDATORY_B |
				    IEEE80211_RATE_MANDATORY_G;
			break;
		case NL80211_BAND_5GHZ:
			mandatory = IEEE80211_RATE_MANDATORY_A;
			break;
		case NL80211_BAND_60GHZ:
			WARN_ON(1);
			mandatory = 0;
			break;
		}

		for (r = 0; r < hw->wiphy->bands[i]->n_bitrates; r++) {
			struct ieee80211_rate *rate;

			rate = &hw->wiphy->bands[i]->bitrates[r];

			if (!(rate->flags & mandatory))
				continue;
			sta->sta.supp_rates[i] |= BIT(r);
		}
	}

	sta->sta.smps_mode = IEEE80211_SMPS_OFF;
	if (sdata->vif.type == NL80211_IFTYPE_AP ||
	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
		struct ieee80211_supported_band *sband;
		u8 smps;

		sband = ieee80211_get_sband(sdata);
		if (!sband)
			goto free_txq;

		smps = (sband->ht_cap.cap & IEEE80211_HT_CAP_SM_PS) >>
			IEEE80211_HT_CAP_SM_PS_SHIFT;
		/*
		 * Assume that hostapd advertises our caps in the beacon and
		 * this is the known_smps_mode for a station that just assciated
		 */
		switch (smps) {
		case WLAN_HT_SMPS_CONTROL_DISABLED:
			sta->known_smps_mode = IEEE80211_SMPS_OFF;
			break;
		case WLAN_HT_SMPS_CONTROL_STATIC:
			sta->known_smps_mode = IEEE80211_SMPS_STATIC;
			break;
		case WLAN_HT_SMPS_CONTROL_DYNAMIC:
			sta->known_smps_mode = IEEE80211_SMPS_DYNAMIC;
			break;
		default:
			WARN_ON(1);
		}
	}

	sta->sta.max_rc_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_BA;

	sta->cparams.ce_threshold = CODEL_DISABLED_THRESHOLD;
	sta->cparams.target = MS2TIME(20);
	sta->cparams.interval = MS2TIME(100);
	sta->cparams.ecn = true;

	sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);

	return sta;

free_txq:
	if (sta->sta.txq[0])
		kfree(to_txq_info(sta->sta.txq[0]));
free:
	free_percpu(sta->pcpu_rx_stats);
#ifdef CONFIG_MAC80211_MESH
	kfree(sta->mesh);
#endif
	kfree(sta);
	return NULL;
}


static int ieee80211_set_rekey_data(struct wiphy *wiphy,
				    struct net_device *dev,
				    struct cfg80211_gtk_rekey_data *data)
{
	struct ieee80211_local *local = wiphy_priv(wiphy);
	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);

	if (!local->ops->set_rekey_data)
		return -EOPNOTSUPP;

	drv_set_rekey_data(local, sdata, data);

	return 0;
}


static void __cleanup_single_sta(struct sta_info *sta)
{
	int ac, i;
	struct tid_ampdu_tx *tid_tx;
	struct ieee80211_sub_if_data *sdata = sta->sdata;
	struct ieee80211_local *local = sdata->local;
	struct ps_data *ps;

	if (test_sta_flag(sta, WLAN_STA_PS_STA) ||
	    test_sta_flag(sta, WLAN_STA_PS_DRIVER) ||
	    test_sta_flag(sta, WLAN_STA_PS_DELIVER)) {
		if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
		    sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
			ps = &sdata->bss->ps;
		else if (ieee80211_vif_is_mesh(&sdata->vif))
			ps = &sdata->u.mesh.ps;
		else
			return;

		clear_sta_flag(sta, WLAN_STA_PS_STA);
		clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
		clear_sta_flag(sta, WLAN_STA_PS_DELIVER);

		atomic_dec(&ps->num_sta_ps);
	}

	if (sta->sta.txq[0]) {
		for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) {
			struct txq_info *txqi;

			if (!sta->sta.txq[i])
				continue;

			txqi = to_txq_info(sta->sta.txq[i]);

			ieee80211_txq_purge(local, txqi);
		}
	}

	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
		local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]);
		ieee80211_purge_tx_queue(&local->hw, &sta->ps_tx_buf[ac]);
		ieee80211_purge_tx_queue(&local->hw, &sta->tx_filtered[ac]);
	}

	if (ieee80211_vif_is_mesh(&sdata->vif))
		mesh_sta_cleanup(sta);

	cancel_work_sync(&sta->drv_deliver_wk);

	/*
	 * Destroy aggregation state here. It would be nice to wait for the
	 * driver to finish aggregation stop and then clean up, but for now
	 * drivers have to handle aggregation stop being requested, followed
	 * directly by station destruction.
	 */
	for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
		kfree(sta->ampdu_mlme.tid_start_tx[i]);
		tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]);
		if (!tid_tx)
			continue;
		ieee80211_purge_tx_queue(&local->hw, &tid_tx->pending);
		kfree(tid_tx);
	}
}


static void __sta_info_destroy_part2(struct sta_info *sta)
{
	struct ieee80211_local *local = sta->local;
	struct ieee80211_sub_if_data *sdata = sta->sdata;
	struct station_info *sinfo;
	int ret;

	/*
	 * NOTE: This assumes at least synchronize_net() was done
	 *	 after _part1 and before _part2!
	 */

	might_sleep();
	lockdep_assert_held(&local->sta_mtx);

	/* now keys can no longer be reached */
	ieee80211_free_sta_keys(local, sta);

	/* disable TIM bit - last chance to tell driver */
	__sta_info_recalc_tim(sta, true);

	sta->dead = true;

	local->num_sta--;
	local->sta_generation++;

	while (sta->sta_state > IEEE80211_STA_NONE) {
		ret = sta_info_move_state(sta, sta->sta_state - 1);
		if (ret) {
			WARN_ON_ONCE(1);
			break;
		}
	}

	if (sta->uploaded) {
		ret = drv_sta_state(local, sdata, sta, IEEE80211_STA_NONE,
				    IEEE80211_STA_NOTEXIST);
		WARN_ON_ONCE(ret != 0);
	}

	sta_dbg(sdata, "Removed STA %pM\n", sta->sta.addr);

	sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
	if (sinfo)
		sta_set_sinfo(sta, sinfo, true);
	cfg80211_del_sta_sinfo(sdata->dev, sta->sta.addr, sinfo, GFP_KERNEL);
	kfree(sinfo);

	rate_control_remove_sta_debugfs(sta);
	ieee80211_sta_debugfs_remove(sta);

	cleanup_single_sta(sta);
}


static int ieee80211_get_txq_stats(struct wiphy *wiphy,
				   struct wireless_dev *wdev,
				   struct cfg80211_txq_stats *txqstats)
{
	struct ieee80211_local *local = wiphy_priv(wiphy);
	struct ieee80211_sub_if_data *sdata;
	int ret = 0;

	if (!local->ops->wake_tx_queue)
		return 1;

	spin_lock_bh(&local->fq.lock);
	rcu_read_lock();

	if (wdev) {
		sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
		if (!sdata->vif.txq) {
			ret = 1;
			goto out;
		}
		ieee80211_fill_txq_stats(txqstats, to_txq_info(sdata->vif.txq));
	} else {
		/* phy stats */
		txqstats->filled |= BIT(NL80211_TXQ_STATS_BACKLOG_PACKETS) |
				    BIT(NL80211_TXQ_STATS_BACKLOG_BYTES) |
				    BIT(NL80211_TXQ_STATS_OVERLIMIT) |
				    BIT(NL80211_TXQ_STATS_OVERMEMORY) |
				    BIT(NL80211_TXQ_STATS_COLLISIONS) |
				    BIT(NL80211_TXQ_STATS_MAX_FLOWS);
		txqstats->backlog_packets = local->fq.backlog;
		txqstats->backlog_bytes = local->fq.memory_usage;
		txqstats->overlimit = local->fq.overlimit;
		txqstats->overmemory = local->fq.overmemory;
		txqstats->collisions = local->fq.collisions;
		txqstats->max_flows = local->fq.flows_cnt;
	}

out:
	rcu_read_unlock();
	spin_unlock_bh(&local->fq.lock);

	return ret;
}


sta_get_last_rx_stats(struct sta_info *sta)
{
	struct ieee80211_sta_rx_stats *stats = &sta->rx_stats;
	struct ieee80211_local *local = sta->local;
	int cpu;

	if (!ieee80211_hw_check(&local->hw, USES_RSS))
		return stats;

	for_each_possible_cpu(cpu) {
		struct ieee80211_sta_rx_stats *cpustats;

		cpustats = per_cpu_ptr(sta->pcpu_rx_stats, cpu);

		if (time_after(cpustats->last_rx, stats->last_rx))
			stats = cpustats;
	}

	return stats;
}


static int ieee80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
{
	ieee80211_if_remove(IEEE80211_WDEV_TO_SUB_IF(wdev));

	return 0;
}


static int getusint(FILE *sfd, uint16 *val) {
    int val2;
    int ret = getint(sfd,&val2);
    *val = val2;
return( ret );
}


static void SFDSetEncMap(SplineFont *sf,int orig_pos,int enc) {
    EncMap *map = sf->map;

    if ( map==NULL )
return;

    if ( orig_pos>=map->backmax ) {
	int old = map->backmax;
	map->backmax = orig_pos+10;
	map->backmap = realloc(map->backmap,map->backmax*sizeof(int));
	memset(map->backmap+old,-1,(map->backmax-old)*sizeof(int));
    }
    if ( map->backmap[orig_pos] == -1 )		/* backmap will not be unique if multiple encodings come from same glyph */
	map->backmap[orig_pos] = enc;
    if ( enc>=map->encmax ) {
	int old = map->encmax;
	map->encmax = enc+10;
	map->map = realloc(map->map,map->encmax*sizeof(int));
	memset(map->map+old,-1,(map->encmax-old)*sizeof(int));
    }
    if ( enc>=map->enccount )
	map->enccount = enc+1;
    if ( enc!=-1 )
	map->map[enc] = orig_pos;
}


int SFDWriteBak(char *filename,SplineFont *sf,EncMap *map,EncMap *normal) {
    char *buf=0, *buf2=NULL;
    int ret;

    if ( sf->save_to_dir )
    {
	ret = SFDWrite(filename,sf,map,normal,true);
	return(ret);
    }

    if ( sf->cidmaster!=NULL )
	sf=sf->cidmaster;
    buf = malloc(strlen(filename)+10);
    if ( sf->compression!=0 )
    {
	buf2 = malloc(strlen(filename)+10);
	strcpy(buf2,filename);
	strcat(buf2,compressors[sf->compression-1].ext);
	strcpy(buf,buf2);
	strcat(buf,"~");
	if ( rename(buf2,buf)==0 )
	    sf->backedup = bs_backedup;
    }
    else
    {
	sf->backedup = bs_dontknow;

	if( prefRevisionsToRetain )
	{
	    char path[PATH_MAX];
	    char pathnew[PATH_MAX];
	    int idx = 0;

	    snprintf( path,    PATH_MAX, "%s", filename );
	    snprintf( pathnew, PATH_MAX, "%s-%02d", filename, idx );
	    (void)rename( path, pathnew );

	    for( idx=prefRevisionsToRetain; idx > 0; idx-- )
	    {
		snprintf( path, PATH_MAX, "%s-%02d", filename, idx-1 );
		snprintf( pathnew, PATH_MAX, "%s-%02d", filename, idx );

		int rc = rename( path, pathnew );
		if( !idx && !rc )
		    sf->backedup = bs_backedup;
	    }
	    idx = prefRevisionsToRetain+1;
	    snprintf( path, PATH_MAX, "%s-%02d", filename, idx );
	    unlink(path);
	}

    }
    free(buf);

    ret = SFDWrite(filename,sf,map,normal,false);
    if ( ret && sf->compression!=0 ) {
	unlink(buf2);
	buf = malloc(strlen(filename)+40);
	sprintf( buf, "%s %s", compressors[sf->compression-1].recomp, filename );
	if ( system( buf )!=0 )
	    sf->compression = 0;
	free(buf);
    }
    free(buf2);
    return( ret );
}


static int gethexints(FILE *sfd, uint32 *val, int cnt) {
    int i, ch;

    for ( i=0; i<cnt; ++i ) {
	if ( i!=0 ) {
	    ch = nlgetc(sfd);
	    if ( ch!='.' ) ungetc(ch,sfd);
	}
	if ( !gethex(sfd,&val[i]))
return( false );
    }
return( true );
}


static OTLookup *CreateMacLookup(SplineFont1 *sf,ASM1 *sm) {
    OTLookup *otl = chunkalloc(sizeof(OTLookup));
    int i, ch;
    char *pt, *start;
    SplineChar *sc;

    otl->features = chunkalloc(sizeof(FeatureScriptLangList));
    if ( sm->sm.type == asm_kern ) {
	otl->lookup_type = kern_statemachine;
	otl->next = sf->sf.gpos_lookups;
	sf->sf.gpos_lookups = otl;
	otl->features->featuretag = (sm->sm.flags&0x8000) ? CHR('v','k','r','n') : CHR('k','e','r','n');
    } else {
	otl->lookup_type = sm->sm.type==asm_indic ? morx_indic : sm->sm.type==asm_context ? morx_context : morx_insert;
	otl->next = sf->sf.gsub_lookups;
	sf->sf.gsub_lookups = otl;
	otl->features->featuretag = (sm->feature<<16) | (sm->setting);
	otl->features->ismac = true;
    }
    otl->lookup_flags = 0;

    for ( i=4; i<sm->sm.class_cnt; ++i ) {
	for ( start=sm->sm.classes[i]; ; start = pt ) {
	    while ( *start==' ' ) ++start;
	    if ( *start=='\0' )
	break;
	    for ( pt=start ; *pt!='\0' && *pt!=' '; ++pt );
	    ch = *pt; *pt = '\0';
	    sc = SFGetChar(&sf->sf,-1,start);
	    if ( sc!=NULL )
		FListAppendScriptLang(otl->features,SCScriptFromUnicode(sc),
			DEFAULT_LANG);
	    *pt = ch;
	}
    }

    /* We will set the lookup_index after we've ordered the list */
    /* We will set the lookup_name after we've assigned the index */
    /* We will add one subtable soon */
return( otl );
}


static void ASMReplaceTagsWithLookups(ASM *sm,SplineFont1 *sf) {
    int i;

    if ( sm->type != asm_context )
return;
    for ( i=0; i<sm->class_cnt*sm->state_cnt; ++i ) {
	if ( sm->state[i].u.context.mark_lookup!=NULL )
	    sm->state[i].u.context.mark_lookup = FindNestedLookupByTag(sf,(uint32) (intpt) (sm->state[i].u.context.mark_lookup) );
	if ( sm->state[i].u.context.cur_lookup!=NULL )
	    sm->state[i].u.context.cur_lookup = FindNestedLookupByTag(sf,(uint32) (intpt) (sm->state[i].u.context.cur_lookup) );
    }
}


static void SFDDumpGuidelines(FILE *sfd, GuidelineSet *gl) {
    if (gl==NULL) {
	return;
    }

    for ( ; gl!=NULL; gl=gl->next )
    {
	fprintf( sfd, "Guideline: " );
	SFDDumpUTF7Str(sfd,gl->name);
	putc(' ',sfd);
	SFDDumpUTF7Str(sfd,gl->identifier);
	putc(' ',sfd);
	fprintf( sfd, "%g %g %g %u %d",
		(double) gl->point.x, (double) gl->point.y,
		(double) gl->angle, gl->color, gl->flags);
	putc('\n',sfd);
    }
}


static void SFDDumpBaseLang(FILE *sfd,struct baselangextent *bl) {

    if ( bl->lang==0 )
	fprintf( sfd, " { %d %d", bl->descent, bl->ascent );
    else
	fprintf( sfd, " { '%c%c%c%c' %d %d",
		bl->lang>>24, bl->lang>>16, bl->lang>>8, bl->lang,
		bl->descent, bl->ascent );
    for ( bl=bl->features; bl!=NULL; bl=bl->next )
	SFDDumpBaseLang(sfd,bl);
    putc('}',sfd);
}


static void SFDDumpGradient(FILE *sfd, const char *keyword, struct gradient *gradient) {
    int i;

    /* Use ";" as a coord separator because we treat "," as a potential decimal point */
    fprintf( sfd, "%s %g;%g %g;%g %g %s %d ", keyword,
	    (double) gradient->start.x, (double) gradient->start.y,
	    (double) gradient->stop.x, (double) gradient->stop.y,
	    (double) gradient->radius,
	    spreads[gradient->sm],
	    gradient->stop_cnt );
    for ( i=0 ; i<gradient->stop_cnt; ++i ) {
	fprintf( sfd, "{%g #%06x %g} ", (double) gradient->grad_stops[i].offset,
		gradient->grad_stops[i].col, (double) gradient->grad_stops[i].opacity );
    }
    putc('\n',sfd);
}


static void SFDGetTtInstrs(FILE *sfd, SplineChar *sc) {
    /* We've read the TtInstr token, it is followed by text versions of */
    /*  the instructions, slurp it all into a big buffer, and then parse that */
    char *buf=NULL, *pt=buf, *end=buf;
    int ch;
    int backlen = strlen(end_tt_instrs);
    int instr_len;

    while ( (ch=nlgetc(sfd))!=EOF ) {
	if ( pt>=end ) {
	    char *newbuf = realloc(buf,(end-buf+200));
	    pt = newbuf+(pt-buf);
	    end = newbuf+(end+200-buf);
	    buf = newbuf;
	}
	*pt++ = ch;
	if ( pt-buf>backlen && strncmp(pt-backlen,end_tt_instrs,backlen)==0 ) {
	    pt -= backlen;
    break;
	}
    }
    *pt = '\0';

    sc->ttf_instrs = _IVParse(sc->parent,buf,&instr_len,tterr,NULL);
    sc->ttf_instrs_len = instr_len;

    free(buf);
}


static DeviceTable *SFDReadDeviceTable(FILE *sfd,DeviceTable *adjust) {
    int i, junk, first, last, ch, len;

    while ( (ch=nlgetc(sfd))==' ' );
    if ( ch=='{' ) {
	while ( (ch=nlgetc(sfd))==' ' );
	if ( ch=='}' )
return(NULL);
	else
	    ungetc(ch,sfd);
	getint(sfd,&first);
	ch = nlgetc(sfd);		/* Should be '-' */
	getint(sfd,&last);
	len = last-first+1;
	if ( len<=0 ) {
	    IError( "Bad device table, invalid length.\n" );
return(NULL);
	}
	if ( adjust==NULL )
	    adjust = chunkalloc(sizeof(DeviceTable));
	adjust->first_pixel_size = first;
	adjust->last_pixel_size = last;
	adjust->corrections = malloc(len);
	for ( i=0; i<len; ++i ) {
	    while ( (ch=nlgetc(sfd))==' ' );
	    if ( ch!=',' ) ungetc(ch,sfd);
	    getint(sfd,&junk);
	    adjust->corrections[i] = junk;
	}
	while ( (ch=nlgetc(sfd))==' ' );
	if ( ch!='}' ) ungetc(ch,sfd);
    } else
	ungetc(ch,sfd);
return( adjust );
}


enum uni_interp interp_from_encoding(Encoding *enc,enum uni_interp interp) {

    if ( enc==NULL )
return( interp );

    if ( enc->is_japanese )
	interp = ui_japanese;
    else if ( enc->is_korean )
	interp = ui_korean;
    else if ( enc->is_tradchinese )
	interp = ui_trad_chinese;
    else if ( enc->is_simplechinese )
	interp = ui_simp_chinese;
return( interp );
}


static int SFD_Dump( FILE *sfd, SplineFont *sf, EncMap *map, EncMap *normal,
		     int todir, char *dirname)
{
    int i, realcnt;
    BDFFont *bdf;
    int *newgids = NULL;
    int err = false;

    if ( normal!=NULL )
	map = normal;

    SFD_DumpSplineFontMetadata( sfd, sf ); //, map, normal, todir, dirname );

    if ( sf->MATH!=NULL ) {
	struct MATH *math = sf->MATH;
	for ( i=0; math_constants_descriptor[i].script_name!=NULL; ++i ) {
	    fprintf( sfd, "MATH:%s: %d", math_constants_descriptor[i].script_name,
		    *((int16 *) (((char *) (math)) + math_constants_descriptor[i].offset)) );
	    if ( math_constants_descriptor[i].devtab_offset>=0 ) {
		DeviceTable **devtab = (DeviceTable **) (((char *) (math)) + math_constants_descriptor[i].devtab_offset );
		putc(' ',sfd);
		SFDDumpDeviceTable(sfd,*devtab);
	    }
	    putc('\n',sfd);
	}
    }
    if ( sf->python_persistent!=NULL )
	SFDPickleMe(sfd,sf->python_persistent, sf->python_persistent_has_lists);
    if ( sf->subfontcnt!=0 ) {
	/* CID fonts have no encodings, they have registry info instead */
	fprintf(sfd, "Registry: %s\n", sf->cidregistry );
	fprintf(sfd, "Ordering: %s\n", sf->ordering );
	fprintf(sfd, "Supplement: %d\n", sf->supplement );
	fprintf(sfd, "CIDVersion: %g\n", (double) sf->cidversion );	/* This is a number whereas "version" is a string */
    } else
	SFDDumpEncoding(sfd,map->enc,"Encoding");
    if ( normal!=NULL )
	fprintf(sfd, "Compacted: 1\n" );
    fprintf( sfd, "UnicodeInterp: %s\n", unicode_interp_names[sf->uni_interp]);
    fprintf( sfd, "NameList: %s\n", sf->for_new_glyphs->title );

    if ( map->remap!=NULL ) {
	struct remap *remap;
	int n;
	for ( n=0,remap = map->remap; remap->infont!=-1; ++n, ++remap );
	fprintf( sfd, "RemapN: %d\n", n );
	for ( remap = map->remap; remap->infont!=-1; ++remap )
	    fprintf(sfd, "Remap: %x %x %d\n", (int) remap->firstenc, (int) remap->lastenc, (int) remap->infont );
    }
    if ( sf->display_size!=0 )
	fprintf( sfd, "DisplaySize: %d\n", sf->display_size );
    if ( sf->display_layer!=ly_fore )
	fprintf( sfd, "DisplayLayer: %d\n", sf->display_layer );
    fprintf( sfd, "AntiAlias: %d\n", sf->display_antialias );
    fprintf( sfd, "FitToEm: %d\n", sf->display_bbsized );
    if ( sf->extrema_bound!=0 )
	fprintf( sfd, "ExtremaBound: %d\n", sf->extrema_bound );
    if ( sf->width_separation!=0 )
	fprintf( sfd, "WidthSeparation: %d\n", sf->width_separation );
    {
	int rc, cc, te;
	if ( (te = FVWinInfo(sf->fv,&cc,&rc))!= -1 )
	    fprintf( sfd, "WinInfo: %d %d %d\n", te, cc, rc );
	else if ( sf->top_enc!=-1 )
	    fprintf( sfd, "WinInfo: %d %d %d\n", sf->top_enc, sf->desired_col_cnt,
		sf->desired_row_cnt);
    }
    if ( sf->onlybitmaps!=0 )
	fprintf( sfd, "OnlyBitmaps: %d\n", sf->onlybitmaps );
    if ( sf->private!=NULL )
	SFDDumpPrivate(sfd,sf->private);
#if HANYANG
    if ( sf->rules!=NULL )
	SFDDumpCompositionRules(sfd,sf->rules);
#endif
    if ( sf->grid.splines!=NULL ) {
	if ( sf->grid.order2 )
	    fprintf(sfd, "GridOrder2: %d\n", sf->grid.order2 );
	fprintf(sfd, "Grid\n" );
	SFDDumpSplineSet(sfd,sf->grid.splines,sf->grid.order2);
    }
    if ( sf->texdata.type!=tex_unset ) {
	fprintf(sfd, "TeXData: %d %d", (int) sf->texdata.type, (int) ((sf->design_size<<19)+2)/5 );
	for ( i=0; i<22; ++i )
	    fprintf(sfd, " %d", (int) sf->texdata.params[i]);
	putc('\n',sfd);
    }
    if ( sf->anchor!=NULL ) {
	AnchorClass *an;
	fprintf(sfd, "AnchorClass2:");
	for ( an=sf->anchor; an!=NULL; an=an->next ) {
	    putc(' ',sfd);
	    SFDDumpUTF7Str(sfd,an->name);
            if ( an->subtable!=NULL ) {
	        putc(' ',sfd);
	        SFDDumpUTF7Str(sfd,an->subtable->subtable_name);
            }
            else
                fprintf(sfd, "\"\" ");
	}
	putc('\n',sfd);
    }

    if ( sf->subfontcnt!=0 ) {
	if ( todir ) {
	    for ( i=0; i<sf->subfontcnt; ++i ) {
		char *subfont = malloc(strlen(dirname)+1+strlen(sf->subfonts[i]->fontname)+20);
		char *fontprops;
		FILE *ssfd;
		sprintf( subfont,"%s/%s" SUBFONT_EXT, dirname, sf->subfonts[i]->fontname );
		GFileMkDir(subfont, 0755);
		fontprops = malloc(strlen(subfont)+strlen("/" FONT_PROPS)+1);
		strcpy(fontprops,subfont); strcat(fontprops,"/" FONT_PROPS);
		ssfd = fopen( fontprops,"w");
		if ( ssfd!=NULL ) {
		    err |= SFD_Dump(ssfd,sf->subfonts[i],map,NULL,todir,subfont);
		    if ( ferror(ssfd) ) err = true;
		    if ( fclose(ssfd)) err = true;
		} else
		    err = true;
		free(fontprops);
		free(subfont);
	    }
	} else {
	    int max;
	    for ( i=max=0; i<sf->subfontcnt; ++i )
		if ( max<sf->subfonts[i]->glyphcnt )
		    max = sf->subfonts[i]->glyphcnt;
	    fprintf(sfd, "BeginSubFonts: %d %d\n", sf->subfontcnt, max );
	    for ( i=0; i<sf->subfontcnt; ++i )
		SFD_Dump(sfd,sf->subfonts[i],map,NULL,false, NULL);
	    fprintf(sfd, "EndSubFonts\n" );
	}
    } else {
	int enccount = map->enccount;
	if ( sf->cidmaster!=NULL ) {
	    realcnt = -1;
	    enccount = sf->glyphcnt;
	} else {
	    realcnt = 0;
	    for ( i=0; i<sf->glyphcnt; ++i ) if ( !SFDOmit(sf->glyphs[i]) )
		++realcnt;
	    if ( realcnt!=sf->glyphcnt ) {
		newgids = malloc(sf->glyphcnt*sizeof(int));
		realcnt = 0;
		for ( i=0; i<sf->glyphcnt; ++i )
		    if ( SFDOmit(sf->glyphs[i]) )
			newgids[i] = -1;
		    else
			newgids[i] = realcnt++;
	    }
	}
	if ( !todir )
	    fprintf(sfd, "BeginChars: %d %d\n",
	        enccount<map->enc->char_cnt? map->enc->char_cnt : enccount,
	        realcnt );
	for ( i=0; i<sf->glyphcnt; ++i ) {
	    if ( !SFDOmit(sf->glyphs[i]) ) {
		if ( !todir )
		SFDDumpChar(sfd,sf->glyphs[i],map,newgids,todir,1);
		else {
		    char *glyphfile = malloc(strlen(dirname)+2*strlen(sf->glyphs[i]->name)+20);
		    FILE *gsfd;
		    appendnames(glyphfile,dirname,"/",sf->glyphs[i]->name,GLYPH_EXT );
		    gsfd = fopen(glyphfile,"w");
		    if ( gsfd!=NULL ) {
			SFDDumpChar(gsfd,sf->glyphs[i],map,newgids,todir,1);
			if ( ferror(gsfd)) err = true;
			if ( fclose(gsfd)) err = true;
		    } else
			err = true;
		    free(glyphfile);
		}
	    }
	    ff_progress_next();
	}
	if ( !todir )
	    fprintf(sfd, "EndChars\n" );
    }

    if ( sf->bitmaps!=NULL )
	ff_progress_change_line2(_("Saving Bitmaps"));
    for ( bdf = sf->bitmaps; bdf!=NULL; bdf=bdf->next ) {
	if ( todir ) {
	    char *strike = malloc(strlen(dirname)+1+20+20);
	    char *strikeprops;
	    FILE *ssfd;
	    sprintf( strike,"%s/%d" STRIKE_EXT, dirname, bdf->pixelsize );
	    GFileMkDir(strike, 0755);
	    strikeprops = malloc(strlen(strike)+strlen("/" STRIKE_PROPS)+1);
	    strcpy(strikeprops,strike); strcat(strikeprops,"/" STRIKE_PROPS);
	    ssfd = fopen( strikeprops,"w");
	    if ( ssfd!=NULL ) {
		err |= SFDDumpBitmapFont(ssfd,bdf,map,newgids,todir,strike);
		if ( ferror(ssfd) ) err = true;
		if ( fclose(ssfd)) err = true;
	    } else
		err = true;
	    free(strikeprops);
	    free(strike);
	} else
	    SFDDumpBitmapFont(sfd,bdf,map,newgids,todir,dirname);
    }
    fprintf(sfd, sf->cidmaster==NULL?"EndSplineFont\n":"EndSubSplineFont\n" );
    free(newgids);
return( err );
}


static struct macname *SFDParseMacNames(FILE *sfd, char *tok) {
    struct macname *head=NULL, *last=NULL, *cur;
    int enc, lang, len;
    char *pt;
    int ch;

    while ( strcmp(tok,"MacName:")==0 ) {
	cur = chunkalloc(sizeof(struct macname));
	if ( last==NULL )
	    head = cur;
	else
	    last->next = cur;
	last = cur;

	getint(sfd,&enc);
	getint(sfd,&lang);
	getint(sfd,&len);
	cur->enc = enc;
	cur->lang = lang;
	cur->name = pt = malloc(len+1);

	while ( (ch=nlgetc(sfd))==' ');
	if ( ch=='"' )
	    ch = nlgetc(sfd);
	while ( ch!='"' && ch!=EOF && pt<cur->name+len ) {
	    if ( ch=='\\' ) {
		*pt  = (nlgetc(sfd)-'0')<<6;
		*pt |= (nlgetc(sfd)-'0')<<3;
		*pt |= (nlgetc(sfd)-'0');
	    } else
		*pt++ = ch;
	    ch = nlgetc(sfd);
	}
	*pt = '\0';
	getname(sfd,tok);
    }
return( head );
}


static SplineFont *SFD_GetFont( FILE *sfd,SplineFont *cidmaster,char *tok,
				int fromdir, char *dirname, float sfdversion )
{
    SplineFont *sf;
    int realcnt, i, eof, mappos=-1, ch;
    struct table_ordering *lastord = NULL;
    struct axismap *lastaxismap = NULL;
    struct named_instance *lastnamedinstance = NULL;
    int pushedbacktok = false;
    Encoding *enc = &custom;
    struct remap *remap = NULL;
    int haddupenc;
    int old_style_order2 = false;
    int had_layer_cnt=false;

    orig_pos = 0;		/* Only used for compatibility with extremely old sfd files */

    sf = SplineFontEmpty();
    if ( sfdversion>0 && sfdversion<2 ) {
	/* If it's an old style sfd file with old style features we need some */
	/*  extra data space to do the conversion from old to new */
	sf = realloc(sf,sizeof(SplineFont1));
	memset(((uint8 *) sf) + sizeof(SplineFont),0,sizeof(SplineFont1)-sizeof(SplineFont));
    }
    sf->sfd_version = sfdversion;
    sf->cidmaster = cidmaster;
    sf->uni_interp = ui_unset;
	SFD_GetFontMetaDataData d;
	SFD_GetFontMetaDataData_Init( &d );
    while ( 1 ) {
	if ( pushedbacktok )
	    pushedbacktok = false;
	else if ( (eof = getname(sfd,tok))!=1 ) {
	    if ( eof==-1 )
    break;
	    geteol(sfd,tok);
    continue;
	}

	bool wasMetadata = SFD_GetFontMetaData( sfd, tok, sf, &d );
	had_layer_cnt = d.had_layer_cnt;
        if( wasMetadata )
        {
            // we have handled the token entirely
            // inside SFD_GetFontMetaData() move to next token.
            continue;
        }

	if ( strmatch(tok,"DisplaySize:")==0 )
	{
	    getint(sfd,&sf->display_size);
	}
	else if ( strmatch(tok,"DisplayLayer:")==0 )
	{
	    getint(sfd,&sf->display_layer);
	}
	else if ( strmatch(tok,"ExtremaBound:")==0 )
	{
	    getint(sfd,&sf->extrema_bound);
	}
	else if ( strmatch(tok,"WidthSeparation:")==0 )
	{
	    getint(sfd,&sf->width_separation);
	}
	else if ( strmatch(tok,"WinInfo:")==0 )
	{
	    int temp1, temp2;
	    getint(sfd,&sf->top_enc);
	    getint(sfd,&temp1);
	    getint(sfd,&temp2);
	    if ( sf->top_enc<=0 ) sf->top_enc=-1;
	    if ( temp1<=0 ) temp1 = 16;
	    if ( temp2<=0 ) temp2 = 4;
	    sf->desired_col_cnt = temp1;
	    sf->desired_row_cnt = temp2;
	}
	else if ( strmatch(tok,"AntiAlias:")==0 )
	{
	    int temp;
	    getint(sfd,&temp);
	    sf->display_antialias = temp;
	}
	else if ( strmatch(tok,"FitToEm:")==0 )
	{
	    int temp;
	    getint(sfd,&temp);
	    sf->display_bbsized = temp;
	}
	else if ( strmatch(tok,"OnlyBitmaps:")==0 )
	{
	    int temp;
	    getint(sfd,&temp);
	    sf->onlybitmaps = temp;
	}
	else if ( strmatch(tok,"Order2:")==0 )
	{
	    getint(sfd,&old_style_order2);
	    sf->grid.order2 = old_style_order2;
	    sf->layers[ly_back].order2 = old_style_order2;
	    sf->layers[ly_fore].order2 = old_style_order2;
	}
	else if ( strmatch(tok,"GridOrder2:")==0 )
	{
	    int o2;
	    getint(sfd,&o2);
	    sf->grid.order2 = o2;
	}
	else if ( strmatch(tok,"Encoding:")==0 )
	{
	    enc = SFDGetEncoding(sfd,tok);
	    if ( sf->map!=NULL ) sf->map->enc = enc;
	}
	else if ( strmatch(tok,"OldEncoding:")==0 )
	{
	    /* old_encname =*/ (void) SFDGetEncoding(sfd,tok);
	}
	else if ( strmatch(tok,"UnicodeInterp:")==0 )
	{
	    sf->uni_interp = SFDGetUniInterp(sfd,tok,sf);
	}
	else if ( strmatch(tok,"NameList:")==0 )
	{
	    SFDGetNameList(sfd,tok,sf);
	}
	else if ( strmatch(tok,"Compacted:")==0 )
	{
	    int temp;
	    getint(sfd,&temp);
	    sf->compacted = temp;
	}
	else if ( strmatch(tok,"Registry:")==0 )
	{
	    geteol(sfd,tok);
	    sf->cidregistry = copy(tok);
	}

	//////////

	else if ( strmatch(tok,"Ordering:")==0 ) {
	    geteol(sfd,tok);
	    sf->ordering = copy(tok);
	} else if ( strmatch(tok,"Supplement:")==0 ) {
	    getint(sfd,&sf->supplement);
	} else if ( strmatch(tok,"RemapN:")==0 ) {
	    int n;
	    getint(sfd,&n);
	    remap = calloc(n+1,sizeof(struct remap));
	    remap[n].infont = -1;
	    mappos = 0;
	    if ( sf->map!=NULL ) sf->map->remap = remap;
	} else if ( strmatch(tok,"Remap:")==0 ) {
	    uint32 f, l; int p;
	    gethex(sfd,&f);
	    gethex(sfd,&l);
	    getint(sfd,&p);
	    if ( remap!=NULL && remap[mappos].infont!=-1 ) {
		remap[mappos].firstenc = f;
		remap[mappos].lastenc = l;
		remap[mappos].infont = p;
		mappos++;
	    }
	} else if ( strmatch(tok,"CIDVersion:")==0 ) {
	    real temp;
	    getreal(sfd,&temp);
	    sf->cidversion = temp;
	} else if ( strmatch(tok,"Grid")==0 ) {
	    sf->grid.splines = SFDGetSplineSet(sfd,sf->grid.order2);
	} else if ( strmatch(tok,"ScriptLang:")==0 ) {
	    int i,j,k;
	    int imax, jmax, kmax;
	    if ( sf->sfd_version==0 || sf->sfd_version>=2 ) {
		IError( "Script lang lists should not happen in version 2 sfd files." );
                SplineFontFree(sf);
                return NULL;
	    }
	    getint(sfd,&imax);
	    ((SplineFont1 *) sf)->sli_cnt = imax;
	    ((SplineFont1 *) sf)->script_lang = malloc((imax+1)*sizeof(struct script_record *));
	    ((SplineFont1 *) sf)->script_lang[imax] = NULL;
	    for ( i=0; i<imax; ++i ) {
		getint(sfd,&jmax);
		((SplineFont1 *) sf)->script_lang[i] = malloc((jmax+1)*sizeof(struct script_record));
		((SplineFont1 *) sf)->script_lang[i][jmax].script = 0;
		for ( j=0; j<jmax; ++j ) {
		    ((SplineFont1 *) sf)->script_lang[i][j].script = gettag(sfd);
		    getint(sfd,&kmax);
		    ((SplineFont1 *) sf)->script_lang[i][j].langs = malloc((kmax+1)*sizeof(uint32));
		    ((SplineFont1 *) sf)->script_lang[i][j].langs[kmax] = 0;
		    for ( k=0; k<kmax; ++k ) {
			((SplineFont1 *) sf)->script_lang[i][j].langs[k] = gettag(sfd);
		    }
		}
	    }
	} else if ( strmatch(tok,"TeXData:")==0 ) {
	    int temp;
	    getint(sfd,&temp);
	    sf->texdata.type = temp;
	    getint(sfd, &temp);
	    if ( sf->design_size==0 ) {
	    	sf->design_size = (5*temp+(1<<18))>>19;
	    }
	    for ( i=0; i<22; ++i ) {
		int foo;
		getint(sfd,&foo);
		sf->texdata.params[i]=foo;
	    }
	} else if ( strnmatch(tok,"AnchorClass",11)==0 ) {
	    char *name;
	    AnchorClass *lastan = NULL, *an;
	    int old = strchr(tok,'2')==NULL;
	    while ( (name=SFDReadUTF7Str(sfd))!=NULL ) {
		an = chunkalloc(old ? sizeof(AnchorClass1) : sizeof(AnchorClass));
		an->name = name;
		if ( old ) {
		    getname(sfd,tok);
		    if ( tok[0]=='0' && tok[1]=='\0' )
			((AnchorClass1 *) an)->feature_tag = 0;
		    else {
			if ( tok[1]=='\0' ) { tok[1]=' '; tok[2] = 0; }
			if ( tok[2]=='\0' ) { tok[2]=' '; tok[3] = 0; }
			if ( tok[3]=='\0' ) { tok[3]=' '; tok[4] = 0; }
			((AnchorClass1 *) an)->feature_tag = (tok[0]<<24) | (tok[1]<<16) | (tok[2]<<8) | tok[3];
		    }
		    while ( (ch=nlgetc(sfd))==' ' || ch=='\t' );
		    ungetc(ch,sfd);
		    if ( isdigit(ch)) {
			int temp;
			getint(sfd,&temp);
			((AnchorClass1 *) an)->flags = temp;
		    }
		    while ( (ch=nlgetc(sfd))==' ' || ch=='\t' );
		    ungetc(ch,sfd);
		    if ( isdigit(ch)) {
			int temp;
			getint(sfd,&temp);
			((AnchorClass1 *) an)->script_lang_index = temp;
		    } else
			((AnchorClass1 *) an)->script_lang_index = 0xffff;		/* Will be fixed up later */
		    while ( (ch=nlgetc(sfd))==' ' || ch=='\t' );
		    ungetc(ch,sfd);
		    if ( isdigit(ch)) {
			int temp;
			getint(sfd,&temp);
			((AnchorClass1 *) an)->merge_with = temp;
		    } else
			((AnchorClass1 *) an)->merge_with = 0xffff;			/* Will be fixed up later */
		} else {
                    char *subtable_name = SFDReadUTF7Str(sfd);
                    if ( subtable_name!=NULL)                                           /* subtable is optional */
		        an->subtable = SFFindLookupSubtableAndFreeName(sf,subtable_name);
                }
		while ( (ch=nlgetc(sfd))==' ' || ch=='\t' );
		ungetc(ch,sfd);
		if ( isdigit(ch) ) {
		    /* Early versions of SfdFormat 2 had a number here */
		    int temp;
		    getint(sfd,&temp);
		    an->type = temp;
		} else if ( old ) {
		    if ( ((AnchorClass1 *) an)->feature_tag==CHR('c','u','r','s'))
			an->type = act_curs;
		    else if ( ((AnchorClass1 *) an)->feature_tag==CHR('m','k','m','k'))
			an->type = act_mkmk;
		    else
			an->type = act_mark;
		} else {
		    an->type = act_mark;
		    if( an->subtable && an->subtable->lookup )
		    {
			switch ( an->subtable->lookup->lookup_type )
			{
			case gpos_cursive:
			    an->type = act_curs;
			    break;
			case gpos_mark2base:
			    an->type = act_mark;
			    break;
			case gpos_mark2ligature:
			    an->type = act_mklg;
			    break;
			case gpos_mark2mark:
			    an->type = act_mkmk;
			    break;
			default:
			    an->type = act_mark;
			    break;
			}
		    }
		}
		if ( lastan==NULL )
		    sf->anchor = an;
		else
		    lastan->next = an;
		lastan = an;
	    }
	} else if ( strncmp(tok,"MATH:",5)==0 ) {
	    SFDParseMathItem(sfd,sf,tok);
	} else if ( strmatch(tok,"TableOrder:")==0 ) {
	    int temp;
	    struct table_ordering *ord;
	    if ( sfdversion==0 || sfdversion>=2 ) {
		IError("Table ordering specified in version 2 sfd file.\n" );
                SplineFontFree(sf);
                return NULL;
	    }
	    ord = chunkalloc(sizeof(struct table_ordering));
	    ord->table_tag = gettag(sfd);
	    getint(sfd,&temp);
	    ord->ordered_features = malloc((temp+1)*sizeof(uint32));
	    ord->ordered_features[temp] = 0;
	    for ( i=0; i<temp; ++i ) {
		while ( isspace((ch=nlgetc(sfd))) );
		if ( ch=='\'' ) {
		    ungetc(ch,sfd);
		    ord->ordered_features[i] = gettag(sfd);
		} else if ( ch=='<' ) {
		    int f,s;
		    fscanf(sfd,"%d,%d>", &f, &s );
		    ord->ordered_features[i] = (f<<16)|s;
		}
	    }
	    if ( lastord==NULL )
		((SplineFont1 *) sf)->orders = ord;
	    else
		lastord->next = ord;
	    lastord = ord;
	} else if ( strmatch(tok,"BeginPrivate:")==0 ) {
	    SFDGetPrivate(sfd,sf);
	} else if ( strmatch(tok,"BeginSubrs:")==0 ) {	/* leave in so we don't croak on old sfd files */
	    SFDGetSubrs(sfd);
	} else if ( strmatch(tok,"PickledData:")==0 ) {
	    if (sf->python_persistent != NULL) {
#if defined(_NO_PYTHON)
	      free( sf->python_persistent );	/* It's a string of pickled data which we leave as a string */
#else
	      PyFF_FreePythonPersistent(sf->python_persistent);
#endif
	      sf->python_persistent = NULL;
	    }
	    sf->python_persistent = SFDUnPickle(sfd, 0);
	    sf->python_persistent_has_lists = 0;
	} else if ( strmatch(tok,"PickledDataWithLists:")==0 ) {
	    if (sf->python_persistent != NULL) {
#if defined(_NO_PYTHON)
	      free( sf->python_persistent );	/* It's a string of pickled data which we leave as a string */
#else
	      PyFF_FreePythonPersistent(sf->python_persistent);
#endif
	      sf->python_persistent = NULL;
	    }
	    sf->python_persistent = SFDUnPickle(sfd, 1);
	    sf->python_persistent_has_lists = 1;
	} else if ( strmatch(tok,"MMCounts:")==0 ) {
	    MMSet *mm = sf->mm = chunkalloc(sizeof(MMSet));
	    getint(sfd,&mm->instance_count);
	    getint(sfd,&mm->axis_count);
	    ch = nlgetc(sfd);
	    if ( ch!=' ' )
		ungetc(ch,sfd);
	    else { int temp;
		getint(sfd,&temp);
		mm->apple = temp;
		getint(sfd,&mm->named_instance_count);
	    }
	    mm->instances = calloc(mm->instance_count,sizeof(SplineFont *));
	    mm->positions = malloc(mm->instance_count*mm->axis_count*sizeof(real));
	    mm->defweights = malloc(mm->instance_count*sizeof(real));
	    mm->axismaps = calloc(mm->axis_count,sizeof(struct axismap));
	    if ( mm->named_instance_count!=0 )
		mm->named_instances = calloc(mm->named_instance_count,sizeof(struct named_instance));
	} else if ( strmatch(tok,"MMAxis:")==0 ) {
	    MMSet *mm = sf->mm;
	    if ( mm!=NULL ) {
		for ( i=0; i<mm->axis_count; ++i ) {
		    getname(sfd,tok);
		    mm->axes[i] = copy(tok);
		}
	    }
	} else if ( strmatch(tok,"MMPositions:")==0 ) {
	    MMSet *mm = sf->mm;
	    if ( mm!=NULL ) {
		for ( i=0; i<mm->axis_count*mm->instance_count; ++i )
		    getreal(sfd,&mm->positions[i]);
	    }
	} else if ( strmatch(tok,"MMWeights:")==0 ) {
	    MMSet *mm = sf->mm;
	    if ( mm!=NULL ) {
		for ( i=0; i<mm->instance_count; ++i )
		    getreal(sfd,&mm->defweights[i]);
	    }
	} else if ( strmatch(tok,"MMAxisMap:")==0 ) {
	    MMSet *mm = sf->mm;
	    if ( mm!=NULL ) {
		int index, points;
		getint(sfd,&index); getint(sfd,&points);
		mm->axismaps[index].points = points;
		mm->axismaps[index].blends = malloc(points*sizeof(real));
		mm->axismaps[index].designs = malloc(points*sizeof(real));
		for ( i=0; i<points; ++i ) {
		    getreal(sfd,&mm->axismaps[index].blends[i]);
		    while ( (ch=nlgetc(sfd))!=EOF && isspace(ch));
		    ungetc(ch,sfd);
		    if ( (ch=nlgetc(sfd))!='=' )
			ungetc(ch,sfd);
		    else if ( (ch=nlgetc(sfd))!='>' )
			ungetc(ch,sfd);
		    getreal(sfd,&mm->axismaps[index].designs[i]);
		}
		lastaxismap = &mm->axismaps[index];
		lastnamedinstance = NULL;
	    }
	} else if ( strmatch(tok,"MMNamedInstance:")==0 ) {
	    MMSet *mm = sf->mm;
	    if ( mm!=NULL ) {
		int index;
		getint(sfd,&index);
		mm->named_instances[index].coords = malloc(mm->axis_count*sizeof(real));
		for ( i=0; i<mm->axis_count; ++i )
		    getreal(sfd,&mm->named_instances[index].coords[i]);
		lastnamedinstance = &mm->named_instances[index];
		lastaxismap = NULL;
	    }
	} else if ( strmatch(tok,"MacName:")==0 ) {
	    struct macname *names = SFDParseMacNames(sfd,tok);
	    if ( lastaxismap!=NULL )
		lastaxismap->axisnames = names;
	    else if ( lastnamedinstance !=NULL )
		lastnamedinstance->names = names;
	    pushedbacktok = true;
	} else if ( strmatch(tok,"MMCDV:")==0 ) {
	    MMSet *mm = sf->mm;
	    if ( mm!=NULL )
		mm->cdv = SFDParseMMSubroutine(sfd);
	} else if ( strmatch(tok,"MMNDV:")==0 ) {
	    MMSet *mm = sf->mm;
	    if ( mm!=NULL )
		mm->ndv = SFDParseMMSubroutine(sfd);
	} else if ( strmatch(tok,"BeginMMFonts:")==0 ) {
	    int cnt;
	    getint(sfd,&cnt);
	    getint(sfd,&realcnt);
	    ff_progress_change_stages(cnt);
	    ff_progress_change_total(realcnt);
	    MMInferStuff(sf->mm);
    break;
	} else if ( strmatch(tok,"BeginSubFonts:")==0 ) {
	    getint(sfd,&sf->subfontcnt);
	    sf->subfonts = calloc(sf->subfontcnt,sizeof(SplineFont *));
	    getint(sfd,&realcnt);
	    sf->map = EncMap1to1(realcnt);
	    ff_progress_change_stages(2);
	    ff_progress_change_total(realcnt);
    break;
	} else if ( strmatch(tok,"BeginChars:")==0 ) {
	    int charcnt;
	    getint(sfd,&charcnt);
	    if (charcnt<enc->char_cnt) {
		IError("SFD file specifies too few slots for its encoding.\n" );
exit( 1 );
	    }
	    if ( getint(sfd,&realcnt)!=1 || realcnt==-1 )
		realcnt = charcnt;
	    else
		++realcnt;		/* value saved is max glyph, not glyph cnt */
	    ff_progress_change_total(realcnt);
	    sf->glyphcnt = sf->glyphmax = realcnt;
	    sf->glyphs = calloc(realcnt,sizeof(SplineChar *));
	    if ( cidmaster!=NULL ) {
		sf->map = cidmaster->map;
	    } else {
		sf->map = EncMapNew(charcnt,realcnt,enc);
		sf->map->remap = remap;
	    }
	    SFDSizeMap(sf->map,sf->glyphcnt,charcnt);
    break;
#if HANYANG
	} else if ( strmatch(tok,"BeginCompositionRules")==0 ) {
	    sf->rules = SFDReadCompositionRules(sfd);
#endif
	} else {
	    /* If we don't understand it, skip it */
	    geteol(sfd,tok);
	}
    }

    // Many downstream functions assume this isn't NULL (use strlen, etc.)
    if ( sf->fontname==NULL)
	sf->fontname = copy("");

    if ( fromdir )
	sf = SFD_FigureDirType(sf,tok,dirname,enc,remap,had_layer_cnt);
    else if ( sf->subfontcnt!=0 ) {
	ff_progress_change_stages(2*sf->subfontcnt);
	for ( i=0; i<sf->subfontcnt; ++i ) {
	    if ( i!=0 )
		ff_progress_next_stage();
	    sf->subfonts[i] = SFD_GetFont(sfd,sf,tok,fromdir,dirname,sfdversion);
	}
    } else if ( sf->mm!=NULL ) {
	MMSet *mm = sf->mm;
	ff_progress_change_stages(2*(mm->instance_count+1));
	for ( i=0; i<mm->instance_count; ++i ) {
	    if ( i!=0 )
		ff_progress_next_stage();
	    mm->instances[i] = SFD_GetFont(sfd,NULL,tok,fromdir,dirname,sfdversion);
	    EncMapFree(mm->instances[i]->map); mm->instances[i]->map=NULL;
	    mm->instances[i]->mm = mm;
	}
	ff_progress_next_stage();
	mm->normal = SFD_GetFont(sfd,NULL,tok,fromdir,dirname,sfdversion);
	mm->normal->mm = mm;
	sf->mm = NULL;
	SplineFontFree(sf);
	sf = mm->normal;
	if ( sf->map->enc!=&custom ) {
	    EncMap *map;
	    MMMatchGlyphs(mm);		/* sfd files from before the encoding change can have mismatched orig pos */
	    map = EncMapFromEncoding(sf,sf->map->enc);
	    EncMapFree(sf->map);
	    sf->map = map;
	}
    } else {
	while ( SFDGetChar(sfd,sf,had_layer_cnt)!=NULL ) {
	    ff_progress_next();
	}
	ff_progress_next_stage();
    }
    haddupenc = false;
    while ( getname(sfd,tok)==1 ) {
	if ( strcmp(tok,"EndSplineFont")==0 || strcmp(tok,"EndSubSplineFont")==0 )
    break;
	else if ( strcmp(tok,"BitmapFont:")==0 )
	    SFDGetBitmapFont(sfd,sf,false,NULL);
	else if ( strmatch(tok,"DupEnc:")==0 ) {
	    int enc, orig;
	    haddupenc = true;
	    if ( getint(sfd,&enc) && getint(sfd,&orig) && sf->map!=NULL ) {
		SFDSetEncMap(sf,orig,enc);
	    }
	}
    }
    if ( sf->cidmaster==NULL )
	SFDFixupRefs(sf);

    if ( !haddupenc )
	SFD_DoAltUnis(sf);
    else
	AltUniFigure(sf,sf->map,true);
    if ( sf->sfd_version<2 )
	SFD_AssignLookups((SplineFont1 *) sf);
    if ( !d.hadtimes )
	SFTimesFromFile(sf,sfd);
    // Make a blank encoding if there are no characters so as to avoid crashes later.
    if (sf->map == NULL) sf->map = EncMapNew(sf->glyphcnt,sf->glyphcnt,&custom);

    SFDFixupUndoRefs(sf);
return( sf );
}


static int SLContains(struct script_record *sr, uint32 script, uint32 lang) {
    int i, j;

    if ( script==DEFAULT_SCRIPT || script == 0 )
return( true );
    for ( i=0; sr[i].script!=0; ++i ) {
	if ( sr[i].script==script ) {
	    if ( lang==0 )
return( true );
	    for ( j=0; sr[i].langs[j]!=0; ++j )
		if ( sr[i].langs[j]==lang )
return( true );

return( false );	/* this script entry didn't contain the language. won't be any other scripts to check */
	}
    }
return( false );	/* Never found script */
}


static void SFDDumpRefs(FILE *sfd,RefChar *refs, int *newgids) {
    RefChar *ref;

    for ( ref=refs; ref!=NULL; ref=ref->next ) if ( ref->sc!=NULL ) {
	fprintf(sfd, "Refer: %d %d %c %g %g %g %g %g %g %d",
		    newgids!=NULL ? newgids[ref->sc->orig_pos]:ref->sc->orig_pos,
		    ref->sc->unicodeenc,
		    ref->selected?'S':'N',
		    (double) ref->transform[0], (double) ref->transform[1], (double) ref->transform[2],
		    (double) ref->transform[3], (double) ref->transform[4], (double) ref->transform[5],
		    ref->use_my_metrics|(ref->round_translation_to_grid<<1)|
		     (ref->point_match<<2));
	if ( ref->point_match ) {
	    fprintf(sfd, " %d %d", ref->match_pt_base, ref->match_pt_ref );
	    if ( ref->point_match_out_of_date )
		fprintf( sfd, " O" );
	}
	putc('\n',sfd);
    }
}


static void appendnames(char *dest,char *dir,const char *dir_char,char *name,const char *ext ) {
    strcpy(dest,dir);
    dest += strlen(dest);
    strcpy(dest,dir_char);
    dest += strlen(dest);
    /* Some file systems are case-insensitive, so we can't just */
    /* copy the glyph name blindly (else "A" and "a" would map to the same file */
    for (;;) {
	if ( strncmp(name,"uni",3)==0 && ishexdigit(name[3]) && ishexdigit(name[4]) &&
		ishexdigit(name[5]) && ishexdigit(name[6])) {
	    /* but in a name like uni00AD case is irrelevant. Even under unix its */
	    /*  the same as uni00ad -- and it looks ugly */
	    strncpy(dest,name,7);
	    dest += 7; name += 7;
	    while ( ishexdigit(name[0]) && ishexdigit(name[1]) &&
		    ishexdigit(name[2]) && ishexdigit(name[3]) ) {
		strncpy(dest,name,4);
		dest += 4; name += 4;
	    }
	} else if ( name[0]=='u' && ishexdigit(name[1]) && ishexdigit(name[2]) &&
		ishexdigit(name[3]) && ishexdigit(name[4]) &&
		ishexdigit(name[5]) ) {
	    strncpy(dest,name,5);
	    dest += 5; name += 5;
	} else
    break;
	if ( *name!='_' )
    break;
	*dest++ = '_';
	++name;
    }
    while ( *name ) {
	if ( isupper(*name)) {
	    *dest++ = '_';
	    *dest++ = *name;
	} else
	    *dest++ = *name;
	++name;
    }
    strcpy(dest,ext);
}


static int SFDGetBitmapChar(FILE *sfd,BDFFont *bdf) {
    BDFChar *bfc;
    struct enc85 dec;
    int i, enc, orig;
    int width,xmax,xmin,ymax,ymin, vwidth=-1;
    EncMap *map;
    int ch;

    map = bdf->sf->map;

    if ( getint(sfd,&orig)!=1 || orig<0 )
return( 0 );
    if ( getint(sfd,&enc)!=1 )
return( 0 );
    if ( getint(sfd,&width)!=1 )
return( 0 );
    if ( getint(sfd,&xmin)!=1 )
return( 0 );
    if ( getint(sfd,&xmax)!=1 )
return( 0 );
    if ( getint(sfd,&ymin)!=1 )
return( 0 );
    while ( (ch=nlgetc(sfd))==' ');
    ungetc(ch,sfd);
    if ( ch=='\n' || ch=='\r' || getint(sfd,&ymax)!=1 ) {
	/* Old style format, no orig_pos given, shift everything by 1 */
	ymax = ymin;
	ymin = xmax;
	xmax = xmin;
	xmin = width;
	width = enc;
	enc = orig;
	orig = map->map[enc];
    } else {
	while ( (ch=nlgetc(sfd))==' ');
	ungetc(ch,sfd);
	if ( ch!='\n' && ch!='\r' )
	    getint(sfd,&vwidth);
    }
    if ( enc<0 ||xmax<xmin || ymax<ymin )
return( 0 );

    bfc = chunkalloc(sizeof(BDFChar));
    if (bfc == NULL)
        return 0;

    if ( orig==-1 ) {
	bfc->sc = SFMakeChar(bdf->sf,map,enc);
	orig = bfc->sc->orig_pos;
    }

    bfc->orig_pos = orig;
    bfc->width = width;
    bfc->ymax = ymax; bfc->ymin = ymin;
    bfc->xmax = xmax; bfc->xmin = xmin;
    bdf->glyphs[orig] = bfc;
    bfc->sc = bdf->sf->glyphs[orig];
    bfc->vwidth = vwidth!=-1 ? vwidth :
	    rint(bfc->sc->vwidth*bdf->pixelsize / (real) (bdf->sf->ascent+bdf->sf->descent));
    if ( bdf->clut==NULL ) {
	bfc->bytes_per_line = (bfc->xmax-bfc->xmin)/8 +1;
	bfc->depth = 1;
    } else {
	bfc->bytes_per_line = bfc->xmax-bfc->xmin +1;
	bfc->byte_data = true;
	bfc->depth = bdf->clut->clut_len==4 ? 2 : bdf->clut->clut_len==16 ? 4 : 8;
    }
    bfc->bitmap = calloc((bfc->ymax-bfc->ymin+1)*bfc->bytes_per_line,sizeof(uint8));

    memset(&dec,'\0', sizeof(dec)); dec.pos = -1;
    dec.sfd = sfd;
    for ( i=0; i<=bfc->ymax-bfc->ymin; ++i ) {
	uint8 *pt = (uint8 *) (bfc->bitmap + i*bfc->bytes_per_line);
	uint8 *end = (uint8 *) (bfc->bitmap + (i+1)*bfc->bytes_per_line);
	while ( pt<end ) {
	    *pt++ = Dec85(&dec);
	}
    }
    if ( bfc->sc==NULL ) {
	bdf->glyphs[bfc->orig_pos] = NULL;
	BDFCharFree(bfc);
    }
/* This fixes a bug: We didn't set "widthset" on splinechars when reading in */
/*  winfonts. We should set it now on any bitmaps worth outputting to make up*/
/*  for that. Eventually we should have good sfd files and can remove this */
    else if ( bfc->sc->width!=bdf->sf->ascent + bdf->sf->descent )
	bfc->sc->widthset = true;
return( 1 );
}


static void SFDDumpLangName(FILE *sfd, struct ttflangname *ln) {
    int i, end;
    fprintf( sfd, "LangName: %d", ln->lang );
    for ( end = ttf_namemax; end>0 && ln->names[end-1]==NULL; --end );
    for ( i=0; i<end; ++i ) {
        putc(' ',sfd);
        SFDDumpUTF7Str(sfd,ln->names[i]);
    }
    putc('\n',sfd);
}


static int TTFFeatureIndex( uint32 tag, struct table_ordering *ord ) {
    /* This is the order in which features should be executed */
    int cnt = 0;

    if ( ord!=NULL ) {
	for ( cnt=0; ord->ordered_features[cnt]!=0; ++cnt )
	    if ( ord->ordered_features[cnt]==tag )
	break;
return( cnt );
    }

    cnt+=2;

    switch ( tag ) {
/* GSUB ordering */
      case CHR('c','c','m','p'):	/* Must be first? */
return( cnt-2 );
      case CHR('l','o','c','l'):	/* Language dependent letter forms (serbian uses some different glyphs than russian) */
return( cnt-1 );
      case CHR('i','s','o','l'):
return( cnt );
      case CHR('j','a','l','t'):		/* must come after 'isol' */
return( cnt+1 );
      case CHR('f','i','n','a'):
return( cnt+2 );
      case CHR('f','i','n','2'):
      case CHR('f','a','l','t'):		/* must come after 'fina' */
return( cnt+3 );
      case CHR('f','i','n','3'):
return( cnt+4 );
      case CHR('m','e','d','i'):
return( cnt+5 );
      case CHR('m','e','d','2'):
return( cnt+6 );
      case CHR('i','n','i','t'):
return( cnt+7 );

      case CHR('r','t','l','a'):
return( cnt+100 );
      case CHR('s','m','c','p'): case CHR('c','2','s','c'):
return( cnt+200 );

      case CHR('r','l','i','g'):
return( cnt+300 );
      case CHR('c','a','l','t'):
return( cnt+301 );
      case CHR('l','i','g','a'):
return( cnt+302 );
      case CHR('d','l','i','g'): case CHR('h','l','i','g'):
return( cnt+303 );
      case CHR('c','s','w','h'):
return( cnt+304 );
      case CHR('m','s','e','t'):
return( cnt+305 );

      case CHR('f','r','a','c'):
return( cnt+306 );

/* Indic processing */
      case CHR('n','u','k','t'):
      case CHR('p','r','e','f'):
return( cnt+301 );
      case CHR('a','k','h','n'):
return( cnt+302 );
      case CHR('r','p','h','f'):
return( cnt+303 );
      case CHR('b','l','w','f'):
return( cnt+304 );
      case CHR('h','a','l','f'):
      case CHR('a','b','v','f'):
return( cnt+305 );
      case CHR('p','s','t','f'):
return( cnt+306 );
      case CHR('v','a','t','u'):
return( cnt+307 );

      case CHR('p','r','e','s'):
return( cnt+310 );
      case CHR('b','l','w','s'):
return( cnt+311 );
      case CHR('a','b','v','s'):
return( cnt+312 );
      case CHR('p','s','t','s'):
return( cnt+313 );
      case CHR('c','l','i','g'):
return( cnt+314 );

      case CHR('h','a','l','n'):
return( cnt+320 );
/* end indic ordering */

      case CHR('a','f','r','c'):
      case CHR('l','j','m','o'):
      case CHR('v','j','m','o'):
return( cnt+350 );
      case CHR('v','r','t','2'): case CHR('v','e','r','t'):
return( cnt+1010 );		/* Documented to come last */

/* Unknown things come after everything but vert/vrt2 */
      default:
return( cnt+1000 );

    }
}


static int getint(FILE *sfd, int *val) {
    char tokbuf[100]; int ch;
    char *pt=tokbuf, *end = tokbuf+100-2;

    while ( isspace(ch = nlgetc(sfd)));
    if ( ch=='-' || ch=='+' ) {
	*pt++ = ch;
	ch = nlgetc(sfd);
    }
    while ( isdigit(ch)) {
	if ( pt<end ) *pt++ = ch;
	ch = nlgetc(sfd);
    }
    *pt='\0';
    ungetc(ch,sfd);
    *val = strtol(tokbuf,NULL,10);
return( pt!=tokbuf?1:ch==EOF?-1: 0 );
}


static void ttm_pool_mm_shrink_fini(struct ttm_pool_manager *manager)
{
	unregister_shrinker(&manager->mm_shrink);
}


int ttm_pool_populate(struct ttm_tt *ttm, struct ttm_operation_ctx *ctx)
{
	struct ttm_mem_global *mem_glob = ttm->bdev->glob->mem_glob;
	unsigned i;
	int ret;

	if (ttm->state != tt_unpopulated)
		return 0;

	if (ttm_check_under_lowerlimit(mem_glob, ttm->num_pages, ctx))
		return -ENOMEM;

	ret = ttm_get_pages(ttm->pages, ttm->num_pages, ttm->page_flags,
			    ttm->caching_state);
	if (unlikely(ret != 0)) {
		ttm_pool_unpopulate_helper(ttm, 0);
		return ret;
	}

	for (i = 0; i < ttm->num_pages; ++i) {
		ret = ttm_mem_global_alloc_page(mem_glob, ttm->pages[i],
						PAGE_SIZE, ctx);
		if (unlikely(ret != 0)) {
			ttm_pool_unpopulate_helper(ttm, i);
			return -ENOMEM;
		}
	}

	if (unlikely(ttm->page_flags & TTM_PAGE_FLAG_SWAPPED)) {
		ret = ttm_tt_swapin(ttm);
		if (unlikely(ret != 0)) {
			ttm_pool_unpopulate(ttm);
			return ret;
		}
	}

	ttm->state = tt_unbound;
	return 0;
}


static void pass1d(e2fsck_t ctx, char *block_buf)
{
	ext2_filsys fs = ctx->fs;
	struct dup_inode	*p, *t;
	struct dup_cluster	*q;
	ext2_ino_t		*shared, ino;
	int	shared_len;
	int	i;
	int	file_ok;
	int	meta_data = 0;
	struct problem_context pctx;
	dnode_t	*n, *m;
	struct cluster_el	*s;
	struct inode_el *r;

	clear_problem_context(&pctx);

	if (!(ctx->options & E2F_OPT_PREEN))
		fix_problem(ctx, PR_1D_PASS_HEADER, &pctx);
	e2fsck_read_bitmaps(ctx);

	pctx.num = dup_inode_count; /* dict_count(&ino_dict); */
	fix_problem(ctx, PR_1D_NUM_DUP_INODES, &pctx);
	shared = (ext2_ino_t *) e2fsck_allocate_memory(ctx,
				sizeof(ext2_ino_t) * dict_count(&ino_dict),
				"Shared inode list");
	for (n = dict_first(&ino_dict); n; n = dict_next(&ino_dict, n)) {
		p = (struct dup_inode *) dnode_get(n);
		shared_len = 0;
		file_ok = 1;
		ino = (ext2_ino_t)VOIDPTR_TO_INT(dnode_getkey(n));
		if (ino == EXT2_BAD_INO || ino == EXT2_RESIZE_INO)
			continue;

		/*
		 * Find all of the inodes which share blocks with this
		 * one.  First we find all of the duplicate blocks
		 * belonging to this inode, and then search each block
		 * get the list of inodes, and merge them together.
		 */
		for (s = p->cluster_list; s; s = s->next) {
			m = dict_lookup(&clstr_dict,
					INT_TO_VOIDPTR(s->cluster));
			if (!m)
				continue; /* Should never happen... */
			q = (struct dup_cluster *) dnode_get(m);
			if (q->num_bad > 1)
				file_ok = 0;
			if (check_if_fs_cluster(ctx, s->cluster)) {
				file_ok = 0;
				meta_data = 1;
			}

			/*
			 * Add all inodes used by this block to the
			 * shared[] --- which is a unique list, so
			 * if an inode is already in shared[], don't
			 * add it again.
			 */
			for (r = q->inode_list; r; r = r->next) {
				if (r->inode == ino)
					continue;
				for (i = 0; i < shared_len; i++)
					if (shared[i] == r->inode)
						break;
				if (i == shared_len) {
					shared[shared_len++] = r->inode;
				}
			}
		}

		/*
		 * Report the inode that we are working on
		 */
		pctx.inode = EXT2_INODE(&p->inode);
		pctx.ino = ino;
		pctx.dir = p->dir;
		pctx.blkcount = p->num_dupblocks;
		pctx.num = meta_data ? shared_len+1 : shared_len;
		fix_problem(ctx, PR_1D_DUP_FILE, &pctx);
		pctx.blkcount = 0;
		pctx.num = 0;

		if (meta_data)
			fix_problem(ctx, PR_1D_SHARE_METADATA, &pctx);

		for (i = 0; i < shared_len; i++) {
			m = dict_lookup(&ino_dict, INT_TO_VOIDPTR(shared[i]));
			if (!m)
				continue; /* should never happen */
			t = (struct dup_inode *) dnode_get(m);
			/*
			 * Report the inode that we are sharing with
			 */
			pctx.inode = EXT2_INODE(&t->inode);
			pctx.ino = shared[i];
			pctx.dir = t->dir;
			fix_problem(ctx, PR_1D_DUP_FILE_LIST, &pctx);
		}
		/*
		 * Even if the file shares blocks with itself, we still need to
		 * clone the blocks.
		 */
		if (file_ok && (meta_data ? shared_len+1 : shared_len) != 0) {
			fix_problem(ctx, PR_1D_DUP_BLOCKS_DEALT, &pctx);
			continue;
		}
		if ((ctx->options & E2F_OPT_UNSHARE_BLOCKS) ||
                    fix_problem(ctx, PR_1D_CLONE_QUESTION, &pctx)) {
			pctx.errcode = clone_file(ctx, ino, p, block_buf);
			if (pctx.errcode)
				fix_problem(ctx, PR_1D_CLONE_ERROR, &pctx);
			else
				continue;
		}
		/*
		 * Note: When unsharing blocks, we don't prompt to delete
		 * files. If the clone operation fails than the unshare
		 * operation should fail too.
		 */
		if (!(ctx->options & E2F_OPT_UNSHARE_BLOCKS) &&
                    fix_problem(ctx, PR_1D_DELETE_QUESTION, &pctx))
			delete_file(ctx, ino, p, block_buf);
		else
			ext2fs_unmark_valid(fs);
	}
	ext2fs_free_mem(&shared);
}


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

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

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

        bool is_real_collection = true;
        if (type_name && memcmp(type_name, "binary", 6) == 0)
        {
            struct_flags = FileNode::STR;
            data[0] = '\0';
            is_real_collection = false;
        }

        if ( is_real_collection )
        {
            char c = FileNode::isMap(struct_flags) ? '{' : '[';
            data[0] = c;
            data[1] = '\0';
        }

        writeScalar( key, data );
        FStructData current_struct("", struct_flags, parent.indent + 4);

        return current_struct;
    }


    void writeComment(const char* comment, bool eol_comment)
    {
        if( !comment )
            CV_Error( CV_StsNullPtr, "Null comment" );

        int len = static_cast<int>(strlen(comment));
        char* ptr = fs->bufferPtr();
        const char* eol = strchr(comment, '\n');
        bool multiline = eol != 0;

        if( !eol_comment || multiline || fs->bufferEnd() - ptr < len || ptr == fs->bufferStart() )
            ptr = fs->flush();
        else
            *ptr++ = ' ';

        while( comment )
        {
            *ptr++ = '/';
            *ptr++ = '/';
            *ptr++ = ' ';
            if( eol )
            {
                ptr = fs->resizeWriteBuffer( ptr, (int)(eol - comment) + 1 );
                memcpy( ptr, comment, eol - comment + 1 );
                fs->setBufferPtr(ptr + (eol - comment));
                comment = eol + 1;
                eol = strchr( comment, '\n' );
            }
            else
            {
                len = (int)strlen(comment);
                ptr = fs->resizeWriteBuffer( ptr, len );
                memcpy( ptr, comment, len );
                fs->setBufferPtr(ptr + len);
                comment = 0;
            }
            ptr = fs->flush();
        }
    }


    bool parse(char* ptr)
    {
        CV_Assert( fs != 0 );

        std::string key, key2, type_name;
        int tag_type = 0;
        bool ok = false;

        // CV_XML_INSIDE_TAG is used to prohibit leading comments
        ptr = skipSpaces( ptr, CV_XML_INSIDE_TAG );
        if (!ptr)
            CV_PARSE_ERROR_CPP("Invalid input");

        if( memcmp( ptr, "<?xml", 5 ) != 0 )  // FIXIT ptr[1..] - out of bounds read without check
            CV_PARSE_ERROR_CPP( "Valid XML should start with \'<?xml ...?>\'" );

        ptr = parseTag( ptr, key, type_name, tag_type );
        FileNode root_collection(fs->getFS(), 0, 0);

        while( ptr && *ptr != '\0' )
        {
            ptr = skipSpaces( ptr, 0 );
            if (!ptr)
                CV_PARSE_ERROR_CPP("Invalid input");

            if( *ptr != '\0' )
            {
                ptr = parseTag( ptr, key, type_name, tag_type );
                if( tag_type != CV_XML_OPENING_TAG || key != "opencv_storage" )
                    CV_PARSE_ERROR_CPP( "<opencv_storage> tag is missing" );
                FileNode root = fs->addNode(root_collection, std::string(), FileNode::MAP, 0);
                ptr = parseValue( ptr, root );
                ptr = parseTag( ptr, key2, type_name, tag_type );
                if( tag_type != CV_XML_CLOSING_TAG || key != key2 )
                    CV_PARSE_ERROR_CPP( "</opencv_storage> tag is missing" );
                ptr = skipSpaces( ptr, 0 );
                ok = true;
            }
        }
        CV_Assert( fs->eof() );
        return ok;
    }


    bool getBase64Row(char* ptr, int /*indent*/, char* &beg, char* &end)
    {
        beg = end = ptr = skipSpaces(ptr, CV_XML_INSIDE_TAG);
        if( !ptr || !*ptr )
            return false;

        // closing XML tag
        if ( *beg == '<' )
            return false;

        // find end of the row
        while( cv_isprint(*ptr) )
            ++ptr;
        if ( *ptr == '\0' )
            CV_PARSE_ERROR_CPP( "Unexpected end of line" );

        end = ptr;
        return true;
    }


    void write( const char* key, double value )
    {
        char buf[128];
        writeScalar( key, fs::doubleToString( buf, value, true ));
    }


static void init_header(struct ctl_table_header *head,
	struct ctl_table_root *root, struct ctl_table_set *set,
	struct ctl_node *node, struct ctl_table *table)
{
	head->ctl_table = table;
	head->ctl_table_arg = table;
	head->used = 0;
	head->count = 1;
	head->nreg = 1;
	head->unregistering = NULL;
	head->root = root;
	head->set = set;
	head->parent = NULL;
	head->node = node;
	INIT_HLIST_HEAD(&head->inodes);
	if (node) {
		struct ctl_table *entry;
		for (entry = table; entry->procname; entry++, node++)
			node->header = head;
	}
}