cmdline_browse_history(
	int	c,
	int	firstc,
	char_u	**curcmdstr,
	int	histype,
	int	*hiscnt_p,
	expand_T *xp)
{
    int		i;
    int		j;
    char_u	*lookfor = *curcmdstr;
    int		hiscnt = *hiscnt_p;
    int		res;

    if (get_hislen() == 0 || firstc == NUL)	// no history
	return CMDLINE_NOT_CHANGED;

    i = hiscnt;

    // save current command string so it can be restored later
    if (lookfor == NULL)
    {
	if ((lookfor = vim_strsave(ccline.cmdbuff)) == NULL)
	    return CMDLINE_NOT_CHANGED;
	lookfor[ccline.cmdpos] = NUL;
    }

    j = (int)STRLEN(lookfor);
    for (;;)
    {
	// one step backwards
	if (c == K_UP|| c == K_S_UP || c == Ctrl_P
		|| c == K_PAGEUP || c == K_KPAGEUP)
	{
	    if (hiscnt == get_hislen())	// first time
		hiscnt = *get_hisidx(histype);
	    else if (hiscnt == 0 && *get_hisidx(histype)
		    != get_hislen() - 1)
		hiscnt = get_hislen() - 1;
	    else if (hiscnt != *get_hisidx(histype) + 1)
		--hiscnt;
	    else			// at top of list
	    {
		hiscnt = i;
		break;
	    }
	}
	else    // one step forwards
	{
	    // on last entry, clear the line
	    if (hiscnt == *get_hisidx(histype))
	    {
		hiscnt = get_hislen();
		break;
	    }

	    // not on a history line, nothing to do
	    if (hiscnt == get_hislen())
		break;
	    if (hiscnt == get_hislen() - 1)   // wrap around
		hiscnt = 0;
	    else
		++hiscnt;
	}
	if (hiscnt < 0 || get_histentry(histype)[hiscnt].hisstr
		== NULL)
	{
	    hiscnt = i;
	    break;
	}
	if ((c != K_UP && c != K_DOWN)
		|| hiscnt == i
		|| STRNCMP(get_histentry(histype)[hiscnt].hisstr,
		    lookfor, (size_t)j) == 0)
	    break;
    }

    if (hiscnt != i)	// jumped to other entry
    {
	char_u	*p;
	int		len;
	int		old_firstc;

	VIM_CLEAR(ccline.cmdbuff);
	xp->xp_context = EXPAND_NOTHING;
	if (hiscnt == get_hislen())
	    p = lookfor;	// back to the old one
	else
	    p = get_histentry(histype)[hiscnt].hisstr;

	if (histype == HIST_SEARCH
		&& p != lookfor
		&& (old_firstc = p[STRLEN(p) + 1]) != firstc)
	{
	    // Correct for the separator character used when
	    // adding the history entry vs the one used now.
	    // First loop: count length.
	    // Second loop: copy the characters.
	    for (i = 0; i <= 1; ++i)
	    {
		len = 0;
		for (j = 0; p[j] != NUL; ++j)
		{
		    // Replace old sep with new sep, unless it is
		    // escaped.
		    if (p[j] == old_firstc
			    && (j == 0 || p[j - 1] != '\\'))
		    {
			if (i > 0)
			    ccline.cmdbuff[len] = firstc;
		    }
		    else
		    {
			// Escape new sep, unless it is already
			// escaped.
			if (p[j] == firstc
				&& (j == 0 || p[j - 1] != '\\'))
			{
			    if (i > 0)
				ccline.cmdbuff[len] = '\\';
			    ++len;
			}
			if (i > 0)
			    ccline.cmdbuff[len] = p[j];
		    }
		    ++len;
		}
		if (i == 0)
		{
		    alloc_cmdbuff(len);
		    if (ccline.cmdbuff == NULL)
		    {
			res = GOTO_NORMAL_MODE;
			goto done;
		    }
		}
	    }
	    ccline.cmdbuff[len] = NUL;
	}
	else
	{
	    alloc_cmdbuff((int)STRLEN(p));
	    if (ccline.cmdbuff == NULL)
	    {
		res = GOTO_NORMAL_MODE;
		goto done;
	    }
	    STRCPY(ccline.cmdbuff, p);
	}

	ccline.cmdpos = ccline.cmdlen = (int)STRLEN(ccline.cmdbuff);
	redrawcmd();
	res = CMDLINE_CHANGED;
	goto done;
    }
    beep_flush();
    res = CMDLINE_NOT_CHANGED;

done:
    *curcmdstr = lookfor;
    *hiscnt_p = hiscnt;
    return res;
}