makeopens(
    FILE	*fd,
    char_u	*dirnow)	/* Current directory name */
{
    buf_T	*buf;
    int		only_save_windows = TRUE;
    int		nr;
    int		restore_size = TRUE;
    win_T	*wp;
    char_u	*sname;
    win_T	*edited_win = NULL;
    int		tabnr;
    int		restore_stal = FALSE;
    win_T	*tab_firstwin;
    frame_T	*tab_topframe;
    int		cur_arg_idx = 0;
    int		next_arg_idx = 0;

    if (ssop_flags & SSOP_BUFFERS)
	only_save_windows = FALSE;		/* Save ALL buffers */

    /*
     * Begin by setting the this_session variable, and then other
     * sessionable variables.
     */
#ifdef FEAT_EVAL
    if (put_line(fd, "let v:this_session=expand(\"<sfile>:p\")") == FAIL)
	return FAIL;
    if (ssop_flags & SSOP_GLOBALS)
	if (store_session_globals(fd) == FAIL)
	    return FAIL;
#endif

    /*
     * Close all windows and tabs but one.
     */
    if (put_line(fd, "silent only") == FAIL)
	return FAIL;
    if ((ssop_flags & SSOP_TABPAGES)
	    && put_line(fd, "silent tabonly") == FAIL)
	return FAIL;

    /*
     * Now a :cd command to the session directory or the current directory
     */
    if (ssop_flags & SSOP_SESDIR)
    {
	if (put_line(fd, "exe \"cd \" . escape(expand(\"<sfile>:p:h\"), ' ')")
								      == FAIL)
	    return FAIL;
    }
    else if (ssop_flags & SSOP_CURDIR)
    {
	sname = home_replace_save(NULL, globaldir != NULL ? globaldir : dirnow);
	if (sname == NULL
		|| fputs("cd ", fd) < 0
		|| ses_put_fname(fd, sname, &ssop_flags) == FAIL
		|| put_eol(fd) == FAIL)
	{
	    vim_free(sname);
	    return FAIL;
	}
	vim_free(sname);
    }

    /*
     * If there is an empty, unnamed buffer we will wipe it out later.
     * Remember the buffer number.
     */
    if (put_line(fd, "if expand('%') == '' && !&modified && line('$') <= 1 && getline(1) == ''") == FAIL)
	return FAIL;
    if (put_line(fd, "  let s:wipebuf = bufnr('%')") == FAIL)
	return FAIL;
    if (put_line(fd, "endif") == FAIL)
	return FAIL;

    /*
     * Now save the current files, current buffer first.
     */
    if (put_line(fd, "set shortmess=aoO") == FAIL)
	return FAIL;

    /* the global argument list */
    if (ses_arglist(fd, "argglobal", &global_alist.al_ga,
			    !(ssop_flags & SSOP_CURDIR), &ssop_flags) == FAIL)
	return FAIL;

    if (ssop_flags & SSOP_RESIZE)
    {
	/* Note: after the restore we still check it worked!*/
	if (fprintf(fd, "set lines=%ld columns=%ld" , Rows, Columns) < 0
		|| put_eol(fd) == FAIL)
	    return FAIL;
    }

#ifdef FEAT_GUI
    if (gui.in_use && (ssop_flags & SSOP_WINPOS))
    {
	int	x, y;

	if (gui_mch_get_winpos(&x, &y) == OK)
	{
	    /* Note: after the restore we still check it worked!*/
	    if (fprintf(fd, "winpos %d %d", x, y) < 0 || put_eol(fd) == FAIL)
		return FAIL;
	}
    }
#endif

    /*
     * When there are two or more tabpages and 'showtabline' is 1 the tabline
     * will be displayed when creating the next tab.  That resizes the windows
     * in the first tab, which may cause problems.  Set 'showtabline' to 2
     * temporarily to avoid that.
     */
    if (p_stal == 1 && first_tabpage->tp_next != NULL)
    {
	if (put_line(fd, "set stal=2") == FAIL)
	    return FAIL;
	restore_stal = TRUE;
    }

    /*
     * May repeat putting Windows for each tab, when "tabpages" is in
     * 'sessionoptions'.
     * Don't use goto_tabpage(), it may change directory and trigger
     * autocommands.
     */
    tab_firstwin = firstwin;	/* first window in tab page "tabnr" */
    tab_topframe = topframe;
    if ((ssop_flags & SSOP_TABPAGES))
    {
	tabpage_T *tp;

	// Similar to ses_win_rec() below, populate the tab pages first so
	// later local options won't be copied to the new tabs.
	FOR_ALL_TABPAGES(tp)
	    if (tp->tp_next != NULL && put_line(fd, "tabnew") == FAIL)
		return FAIL;
	if (first_tabpage->tp_next != NULL && put_line(fd, "tabrewind") == FAIL)
	    return FAIL;
    }
    for (tabnr = 1; ; ++tabnr)
    {
	int	need_tabnext = FALSE;
	int	cnr = 1;

	if ((ssop_flags & SSOP_TABPAGES))
	{
	    tabpage_T *tp = find_tabpage(tabnr);

	    if (tp == NULL)
		break;		/* done all tab pages */
	    if (tp == curtab)
	    {
		tab_firstwin = firstwin;
		tab_topframe = topframe;
	    }
	    else
	    {
		tab_firstwin = tp->tp_firstwin;
		tab_topframe = tp->tp_topframe;
	    }
	    if (tabnr > 1)
		need_tabnext = TRUE;
	}

	/*
	 * Before creating the window layout, try loading one file.  If this
	 * is aborted we don't end up with a number of useless windows.
	 * This may have side effects! (e.g., compressed or network file).
	 */
	for (wp = tab_firstwin; wp != NULL; wp = wp->w_next)
	{
	    if (ses_do_win(wp)
		    && wp->w_buffer->b_ffname != NULL
		    && !bt_help(wp->w_buffer)
#ifdef FEAT_QUICKFIX
		    && !bt_nofile(wp->w_buffer)
#endif
		    )
	    {
		if (need_tabnext && put_line(fd, "tabnext") == FAIL)
		    return FAIL;
		need_tabnext = FALSE;

		if (fputs("edit ", fd) < 0
			      || ses_fname(fd, wp->w_buffer, &ssop_flags, TRUE)
								       == FAIL)
		    return FAIL;
		if (!wp->w_arg_idx_invalid)
		    edited_win = wp;
		break;
	    }
	}

	/* If no file got edited create an empty tab page. */
	if (need_tabnext && put_line(fd, "tabnext") == FAIL)
	    return FAIL;

	/*
	 * Save current window layout.
	 */
	if (put_line(fd, "set splitbelow splitright") == FAIL)
	    return FAIL;
	if (ses_win_rec(fd, tab_topframe) == FAIL)
	    return FAIL;
	if (!p_sb && put_line(fd, "set nosplitbelow") == FAIL)
	    return FAIL;
	if (!p_spr && put_line(fd, "set nosplitright") == FAIL)
	    return FAIL;

	/*
	 * Check if window sizes can be restored (no windows omitted).
	 * Remember the window number of the current window after restoring.
	 */
	nr = 0;
	for (wp = tab_firstwin; wp != NULL; wp = W_NEXT(wp))
	{
	    if (ses_do_win(wp))
		++nr;
	    else
		restore_size = FALSE;
	    if (curwin == wp)
		cnr = nr;
	}

	/* Go to the first window. */
	if (put_line(fd, "wincmd t") == FAIL)
	    return FAIL;

	/*
	 * If more than one window, see if sizes can be restored.
	 * First set 'winheight' and 'winwidth' to 1 to avoid the windows being
	 * resized when moving between windows.
	 * Do this before restoring the view, so that the topline and the
	 * cursor can be set.  This is done again below.
	 * winminheight and winminwidth need to be set to avoid an error if the
	 * user has set winheight or winwidth.
	 */
	if (put_line(fd, "set winminheight=0") == FAIL
		|| put_line(fd, "set winheight=1") == FAIL
		|| put_line(fd, "set winminwidth=0") == FAIL
		|| put_line(fd, "set winwidth=1") == FAIL)
	    return FAIL;
	if (nr > 1 && ses_winsizes(fd, restore_size, tab_firstwin) == FAIL)
	    return FAIL;

	/*
	 * Restore the view of the window (options, file, cursor, etc.).
	 */
	for (wp = tab_firstwin; wp != NULL; wp = wp->w_next)
	{
	    if (!ses_do_win(wp))
		continue;
	    if (put_view(fd, wp, wp != edited_win, &ssop_flags,
							 cur_arg_idx) == FAIL)
		return FAIL;
	    if (nr > 1 && put_line(fd, "wincmd w") == FAIL)
		return FAIL;
	    next_arg_idx = wp->w_arg_idx;
	}

	/* The argument index in the first tab page is zero, need to set it in
	 * each window.  For further tab pages it's the window where we do
	 * "tabedit". */
	cur_arg_idx = next_arg_idx;

	/*
	 * Restore cursor to the current window if it's not the first one.
	 */
	if (cnr > 1 && (fprintf(fd, "%dwincmd w", cnr) < 0
						      || put_eol(fd) == FAIL))
	    return FAIL;

	/*
	 * Restore window sizes again after jumping around in windows, because
	 * the current window has a minimum size while others may not.
	 */
	if (nr > 1 && ses_winsizes(fd, restore_size, tab_firstwin) == FAIL)
	    return FAIL;

	/* Don't continue in another tab page when doing only the current one
	 * or when at the last tab page. */
	if (!(ssop_flags & SSOP_TABPAGES))
	    break;
    }

    if (ssop_flags & SSOP_TABPAGES)
    {
	if (fprintf(fd, "tabnext %d", tabpage_index(curtab)) < 0
		|| put_eol(fd) == FAIL)
	    return FAIL;
    }
    if (restore_stal && put_line(fd, "set stal=1") == FAIL)
	return FAIL;

    // Now put the remaining buffers into the buffer list.
    // This is near the end, so that when 'hidden' is set we don't create extra
    // buffers.  If the buffer was already created with another command the
    // ":badd" will have no effect.
    FOR_ALL_BUFFERS(buf)
    {
	if (!(only_save_windows && buf->b_nwindows == 0)
		&& !(buf->b_help && !(ssop_flags & SSOP_HELP))
#ifdef FEAT_TERMINAL
		// Skip terminal buffers: finished ones are not useful, others
		// will be resurrected and result in a new buffer.
		&& !bt_terminal(buf)
#endif
		&& buf->b_fname != NULL
		&& buf->b_p_bl)
	{
	    if (fprintf(fd, "badd +%ld ", buf->b_wininfo == NULL ? 1L
					   : buf->b_wininfo->wi_fpos.lnum) < 0
		    || ses_fname(fd, buf, &ssop_flags, TRUE) == FAIL)
		return FAIL;
	}
    }

    /*
     * Wipe out an empty unnamed buffer we started in.
     */
    if (put_line(fd, "if exists('s:wipebuf') && len(win_findbuf(s:wipebuf)) == 0")
								       == FAIL)
	return FAIL;
    if (put_line(fd, "  silent exe 'bwipe ' . s:wipebuf") == FAIL)
	return FAIL;
    if (put_line(fd, "endif") == FAIL)
	return FAIL;
    if (put_line(fd, "unlet! s:wipebuf") == FAIL)
	return FAIL;

    /* Re-apply 'winheight', 'winwidth' and 'shortmess'. */
    if (fprintf(fd, "set winheight=%ld winwidth=%ld shortmess=%s",
			       p_wh, p_wiw, p_shm) < 0 || put_eol(fd) == FAIL)
	return FAIL;
    /* Re-apply 'winminheight' and 'winminwidth'. */
    if (fprintf(fd, "set winminheight=%ld winminwidth=%ld",
				      p_wmh, p_wmw) < 0 || put_eol(fd) == FAIL)
	return FAIL;

    /*
     * Lastly, execute the x.vim file if it exists.
     */
    if (put_line(fd, "let s:sx = expand(\"<sfile>:p:r\").\"x.vim\"") == FAIL
	    || put_line(fd, "if file_readable(s:sx)") == FAIL
	    || put_line(fd, "  exe \"source \" . fnameescape(s:sx)") == FAIL
	    || put_line(fd, "endif") == FAIL)
	return FAIL;

    return OK;
}