term_start(
	typval_T    *argvar,
	char	    **argv,
	jobopt_T    *opt,
	int	    flags)
{
    exarg_T	split_ea;
    win_T	*old_curwin = curwin;
    term_T	*term;
    buf_T	*old_curbuf = NULL;
    int		res;
    buf_T	*newbuf;
    int		vertical = opt->jo_vertical || (cmdmod.split & WSP_VERT);
    jobopt_T	orig_opt;  // only partly filled

    if (check_restricted() || check_secure())
	return NULL;

    if ((opt->jo_set & (JO_IN_IO + JO_OUT_IO + JO_ERR_IO))
					 == (JO_IN_IO + JO_OUT_IO + JO_ERR_IO)
	|| (!(opt->jo_set & JO_OUT_IO) && (opt->jo_set & JO_OUT_BUF))
	|| (!(opt->jo_set & JO_ERR_IO) && (opt->jo_set & JO_ERR_BUF)))
    {
	EMSG(_(e_invarg));
	return NULL;
    }

    term = (term_T *)alloc_clear(sizeof(term_T));
    if (term == NULL)
	return NULL;
    term->tl_dirty_row_end = MAX_ROW;
    term->tl_cursor_visible = TRUE;
    term->tl_cursor_shape = VTERM_PROP_CURSORSHAPE_BLOCK;
    term->tl_finish = opt->jo_term_finish;
#ifdef FEAT_GUI
    term->tl_system = (flags & TERM_START_SYSTEM);
#endif
    ga_init2(&term->tl_scrollback, sizeof(sb_line_T), 300);

    vim_memset(&split_ea, 0, sizeof(split_ea));
    if (opt->jo_curwin)
    {
	/* Create a new buffer in the current window. */
	if (!can_abandon(curbuf, flags & TERM_START_FORCEIT))
	{
	    no_write_message();
	    vim_free(term);
	    return NULL;
	}
	if (do_ecmd(0, NULL, NULL, &split_ea, ECMD_ONE,
		     ECMD_HIDE
			   + ((flags & TERM_START_FORCEIT) ? ECMD_FORCEIT : 0),
		     curwin) == FAIL)
	{
	    vim_free(term);
	    return NULL;
	}
    }
    else if (opt->jo_hidden || (flags & TERM_START_SYSTEM))
    {
	buf_T *buf;

	/* Create a new buffer without a window. Make it the current buffer for
	 * a moment to be able to do the initialisations. */
	buf = buflist_new((char_u *)"", NULL, (linenr_T)0,
							 BLN_NEW | BLN_LISTED);
	if (buf == NULL || ml_open(buf) == FAIL)
	{
	    vim_free(term);
	    return NULL;
	}
	old_curbuf = curbuf;
	--curbuf->b_nwindows;
	curbuf = buf;
	curwin->w_buffer = buf;
	++curbuf->b_nwindows;
    }
    else
    {
	/* Open a new window or tab. */
	split_ea.cmdidx = CMD_new;
	split_ea.cmd = (char_u *)"new";
	split_ea.arg = (char_u *)"";
	if (opt->jo_term_rows > 0 && !vertical)
	{
	    split_ea.line2 = opt->jo_term_rows;
	    split_ea.addr_count = 1;
	}
	if (opt->jo_term_cols > 0 && vertical)
	{
	    split_ea.line2 = opt->jo_term_cols;
	    split_ea.addr_count = 1;
	}

	if (vertical)
	    cmdmod.split |= WSP_VERT;
	ex_splitview(&split_ea);
	if (curwin == old_curwin)
	{
	    /* split failed */
	    vim_free(term);
	    return NULL;
	}
    }
    term->tl_buffer = curbuf;
    curbuf->b_term = term;

    if (!opt->jo_hidden)
    {
	/* Only one size was taken care of with :new, do the other one.  With
	 * "curwin" both need to be done. */
	if (opt->jo_term_rows > 0 && (opt->jo_curwin || vertical))
	    win_setheight(opt->jo_term_rows);
	if (opt->jo_term_cols > 0 && (opt->jo_curwin || !vertical))
	    win_setwidth(opt->jo_term_cols);
    }

    /* Link the new terminal in the list of active terminals. */
    term->tl_next = first_term;
    first_term = term;

    if (opt->jo_term_name != NULL)
	curbuf->b_ffname = vim_strsave(opt->jo_term_name);
    else if (argv != NULL)
	curbuf->b_ffname = vim_strsave((char_u *)"!system");
    else
    {
	int	i;
	size_t	len;
	char_u	*cmd, *p;

	if (argvar->v_type == VAR_STRING)
	{
	    cmd = argvar->vval.v_string;
	    if (cmd == NULL)
		cmd = (char_u *)"";
	    else if (STRCMP(cmd, "NONE") == 0)
		cmd = (char_u *)"pty";
	}
	else if (argvar->v_type != VAR_LIST
		|| argvar->vval.v_list == NULL
		|| argvar->vval.v_list->lv_len < 1
		|| (cmd = tv_get_string_chk(
			       &argvar->vval.v_list->lv_first->li_tv)) == NULL)
	    cmd = (char_u*)"";

	len = STRLEN(cmd) + 10;
	p = alloc((int)len);

	for (i = 0; p != NULL; ++i)
	{
	    /* Prepend a ! to the command name to avoid the buffer name equals
	     * the executable, otherwise ":w!" would overwrite it. */
	    if (i == 0)
		vim_snprintf((char *)p, len, "!%s", cmd);
	    else
		vim_snprintf((char *)p, len, "!%s (%d)", cmd, i);
	    if (buflist_findname(p) == NULL)
	    {
		vim_free(curbuf->b_ffname);
		curbuf->b_ffname = p;
		break;
	    }
	}
    }
    curbuf->b_fname = curbuf->b_ffname;

    if (opt->jo_term_opencmd != NULL)
	term->tl_opencmd = vim_strsave(opt->jo_term_opencmd);

    if (opt->jo_eof_chars != NULL)
	term->tl_eof_chars = vim_strsave(opt->jo_eof_chars);

    set_string_option_direct((char_u *)"buftype", -1,
				  (char_u *)"terminal", OPT_FREE|OPT_LOCAL, 0);
    // Avoid that 'buftype' is reset when this buffer is entered.
    curbuf->b_p_initialized = TRUE;

    /* Mark the buffer as not modifiable. It can only be made modifiable after
     * the job finished. */
    curbuf->b_p_ma = FALSE;

    set_term_and_win_size(term);
#ifdef WIN3264
    mch_memmove(orig_opt.jo_io, opt->jo_io, sizeof(orig_opt.jo_io));
#endif
    setup_job_options(opt, term->tl_rows, term->tl_cols);

    if (flags & TERM_START_NOJOB)
	return curbuf;

#if defined(FEAT_SESSION)
    /* Remember the command for the session file. */
    if (opt->jo_term_norestore || argv != NULL)
    {
	term->tl_command = vim_strsave((char_u *)"NONE");
    }
    else if (argvar->v_type == VAR_STRING)
    {
	char_u	*cmd = argvar->vval.v_string;

	if (cmd != NULL && STRCMP(cmd, p_sh) != 0)
	    term->tl_command = vim_strsave(cmd);
    }
    else if (argvar->v_type == VAR_LIST
	    && argvar->vval.v_list != NULL
	    && argvar->vval.v_list->lv_len > 0)
    {
	garray_T	ga;
	listitem_T	*item;

	ga_init2(&ga, 1, 100);
	for (item = argvar->vval.v_list->lv_first;
					item != NULL; item = item->li_next)
	{
	    char_u *s = tv_get_string_chk(&item->li_tv);
	    char_u *p;

	    if (s == NULL)
		break;
	    p = vim_strsave_fnameescape(s, FALSE);
	    if (p == NULL)
		break;
	    ga_concat(&ga, p);
	    vim_free(p);
	    ga_append(&ga, ' ');
	}
	if (item == NULL)
	{
	    ga_append(&ga, NUL);
	    term->tl_command = ga.ga_data;
	}
	else
	    ga_clear(&ga);
    }
#endif

    if (opt->jo_term_kill != NULL)
    {
	char_u *p = skiptowhite(opt->jo_term_kill);

	term->tl_kill = vim_strnsave(opt->jo_term_kill, p - opt->jo_term_kill);
    }

    /* System dependent: setup the vterm and maybe start the job in it. */
    if (argv == NULL
	    && argvar->v_type == VAR_STRING
	    && argvar->vval.v_string != NULL
	    && STRCMP(argvar->vval.v_string, "NONE") == 0)
	res = create_pty_only(term, opt);
    else
	res = term_and_job_init(term, argvar, argv, opt, &orig_opt);

    newbuf = curbuf;
    if (res == OK)
    {
	/* Get and remember the size we ended up with.  Update the pty. */
	vterm_get_size(term->tl_vterm, &term->tl_rows, &term->tl_cols);
	term_report_winsize(term, term->tl_rows, term->tl_cols);
#ifdef FEAT_GUI
	if (term->tl_system)
	{
	    /* display first line below typed command */
	    term->tl_toprow = msg_row + 1;
	    term->tl_dirty_row_end = 0;
	}
#endif

	/* Make sure we don't get stuck on sending keys to the job, it leads to
	 * a deadlock if the job is waiting for Vim to read. */
	channel_set_nonblock(term->tl_job->jv_channel, PART_IN);

	if (old_curbuf != NULL)
	{
	    --curbuf->b_nwindows;
	    curbuf = old_curbuf;
	    curwin->w_buffer = curbuf;
	    ++curbuf->b_nwindows;
	}
    }
    else
    {
	term_close_buffer(curbuf, old_curbuf);
	return NULL;
    }

    apply_autocmds(EVENT_TERMINALOPEN, NULL, NULL, FALSE, newbuf);
    return newbuf;
}