Пример #1
0
/*
 * ex_mark -- :mark char
 *	      :k char
 *	Mark lines.
 *
 *
 * PUBLIC: int ex_mark __P((SCR *, EXCMD *));
 */
int
ex_mark(SCR *sp, EXCMD *cmdp)
{
	NEEDFILE(sp, cmdp);

	if (cmdp->argv[0]->len != 1) {
		msgq(sp, M_ERR, "136|Mark names must be a single character");
		return (1);
	}
	return (mark_set(sp, cmdp->argv[0]->bp[0], &cmdp->addr1, 1));
}
Пример #2
0
/*
 * ex_undo -- u
 *	Undo the last change.
 *
 * PUBLIC: int ex_undo __P((SCR *, EXCMD *));
 */
int
ex_undo(SCR *sp, EXCMD *cmdp)
{
	EXF *ep;
	MARK m;

	/*
	 * !!!
	 * Historic undo always set the previous context mark.
	 */
	m.lno = sp->lno;
	m.cno = sp->cno;
	if (mark_set(sp, ABSMARK1, &m, 1))
		return (1);

	/*
	 * !!!
	 * Multiple undo isn't available in ex, as there's no '.' command.
	 * Whether 'u' is undo or redo is toggled each time, unless there
	 * was a change since the last undo, in which case it's an undo.
	 */
	ep = sp->ep;
	if (!F_ISSET(ep, F_UNDO)) {
		F_SET(ep, F_UNDO);
		ep->lundo = FORWARD;
	}
	switch (ep->lundo) {
	case BACKWARD:
		if (log_forward(sp, &m))
			return (1);
		ep->lundo = FORWARD;
		break;
	case FORWARD:
		if (log_backward(sp, &m))
			return (1);
		ep->lundo = BACKWARD;
		break;
	case NOTSET:
		abort();
	}
	sp->lno = m.lno;
	sp->cno = m.cno;
	return (0);
}
Пример #3
0
/*
 * ex_z -- :[line] z [^-.+=] [count] [flags]
 *	Adjust window.
 *
 * PUBLIC: int ex_z __P((SCR *, EXCMD *));
 */
int
ex_z(SCR *sp, EXCMD *cmdp)
{
    MARK abs;
    recno_t cnt, equals, lno;
    int eofcheck;

    NEEDFILE(sp, cmdp);

    /*
     * !!!
     * If no count specified, use either two times the size of the
     * scrolling region, or the size of the window option.  POSIX
     * 1003.2 claims that the latter is correct, but historic ex/vi
     * documentation and practice appear to use the scrolling region.
     * I'm using the window size as it means that the entire screen
     * is used instead of losing a line to roundoff.  Note, we drop
     * a line from the cnt if using the window size to leave room for
     * the next ex prompt.
     */
    if (FL_ISSET(cmdp->iflags, E_C_COUNT))
        cnt = cmdp->count;
    else
#ifdef HISTORICAL_PRACTICE
        cnt = O_VAL(sp, O_SCROLL) * 2;
#else
        cnt = O_VAL(sp, O_WINDOW) - 1;
#endif

    equals = 0;
    eofcheck = 0;
    lno = cmdp->addr1.lno;

    switch (FL_ISSET(cmdp->iflags,
                     E_C_CARAT | E_C_DASH | E_C_DOT | E_C_EQUAL | E_C_PLUS)) {
    case E_C_CARAT:		/* Display cnt * 2 before the line. */
        eofcheck = 1;
        if (lno > cnt * 2)
            cmdp->addr1.lno = (lno - cnt * 2) + 1;
        else
            cmdp->addr1.lno = 1;
        cmdp->addr2.lno = (cmdp->addr1.lno + cnt) - 1;
        break;
    case E_C_DASH:		/* Line goes at the bottom of the screen. */
        cmdp->addr1.lno = lno > cnt ? (lno - cnt) + 1 : 1;
        cmdp->addr2.lno = lno;
        break;
    case E_C_DOT:		/* Line goes in the middle of the screen. */
        /*
         * !!!
         * Historically, the "middleness" of the line overrode the
         * count, so that "3z.19" or "3z.20" would display the first
         * 12 lines of the file, i.e. (N - 1) / 2 lines before and
         * after the specified line.
         */
        eofcheck = 1;
        cnt = (cnt - 1) / 2;
        cmdp->addr1.lno = lno > cnt ? lno - cnt : 1;
        cmdp->addr2.lno = lno + cnt;

        /*
         * !!!
         * Historically, z. set the absolute cursor mark.
         */
        abs.lno = sp->lno;
        abs.cno = sp->cno;
        (void)mark_set(sp, ABSMARK1, &abs, 1);
        break;
    case E_C_EQUAL:		/* Center with hyphens. */
        /*
         * !!!
         * Strangeness.  The '=' flag is like the '.' flag (see the
         * above comment, it applies here as well) but with a special
         * little hack.  Print out lines of hyphens before and after
         * the specified line.  Additionally, the cursor remains set
         * on that line.
         */
        eofcheck = 1;
        cnt = (cnt - 1) / 2;
        cmdp->addr1.lno = lno > cnt ? lno - cnt : 1;
        cmdp->addr2.lno = lno - 1;
        if (ex_pr(sp, cmdp))
            return (1);
        (void)ex_puts(sp, "----------------------------------------\n");
        cmdp->addr2.lno = cmdp->addr1.lno = equals = lno;
        if (ex_pr(sp, cmdp))
            return (1);
        (void)ex_puts(sp, "----------------------------------------\n");
        cmdp->addr1.lno = lno + 1;
        cmdp->addr2.lno = (lno + cnt) - 1;
        break;
    default:
        /* If no line specified, move to the next one. */
        if (F_ISSET(cmdp, E_ADDR_DEF))
            ++lno;
    /* FALLTHROUGH */
    case E_C_PLUS:		/* Line goes at the top of the screen. */
        eofcheck = 1;
        cmdp->addr1.lno = lno;
        cmdp->addr2.lno = (lno + cnt) - 1;
        break;
    }

    if (eofcheck) {
        if (db_last(sp, &lno))
            return (1);
        if (cmdp->addr2.lno > lno)
            cmdp->addr2.lno = lno;
    }

    if (ex_pr(sp, cmdp))
        return (1);
    if (equals)
        sp->lno = equals;
    return (0);
}
Пример #4
0
/*
 * api_setmark --
 *	Set the mark.
 *
 * PUBLIC: int api_setmark __P((SCR *, int, MARK *));
 */
int
api_setmark(SCR *sp, int markname, MARK *mp)
{
	return (mark_set(sp, (ARG_CHAR_T)markname, mp, 1));
}
Пример #5
0
Файл: exf.c Проект: fishman/nvi
/*
 * file_cinit --
 *	Set up the initial cursor position.
 */
static void
file_cinit(SCR *sp)
{
    GS *gp;
    MARK m;
    size_t len;
    int nb;
    CHAR_T *wp;
    size_t wlen;

    /* Set some basic defaults. */
    sp->lno = 1;
    sp->cno = 0;

    /*
     * Historically, initial commands (the -c option) weren't executed
     * until a file was loaded, e.g. "vi +10 nofile", followed by an
     * :edit or :tag command, would execute the +10 on the file loaded
     * by the subsequent command, (assuming that it existed).  This
     * applied as well to files loaded using the tag commands, and we
     * follow that historic practice.  Also, all initial commands were
     * ex commands and were always executed on the last line of the file.
     *
     * Otherwise, if no initial command for this file:
     *    If in ex mode, move to the last line, first nonblank character.
     *    If the file has previously been edited, move to the last known
     *	  position, and check it for validity.
     *    Otherwise, move to the first line, first nonblank.
     *
     * This gets called by the file init code, because we may be in a
     * file of ex commands and we want to execute them from the right
     * location in the file.
     */
    nb = 0;
    gp = sp->gp;
    if (gp->c_option != NULL && !F_ISSET(sp->frp, FR_NEWFILE)) {
        if (db_last(sp, &sp->lno))
            return;
        if (sp->lno == 0) {
            sp->lno = 1;
            sp->cno = 0;
        }
        CHAR2INT(sp, gp->c_option, strlen(gp->c_option) + 1,
                 wp, wlen);
        if (ex_run_str(sp, "-c option", wp, wlen - 1, 1, 1))
            return;
        gp->c_option = NULL;
    } else if (F_ISSET(sp, SC_EX)) {
        if (db_last(sp, &sp->lno))
            return;
        if (sp->lno == 0) {
            sp->lno = 1;
            sp->cno = 0;
            return;
        }
        nb = 1;
    } else {
        if (F_ISSET(sp->frp, FR_CURSORSET)) {
            sp->lno = sp->frp->lno;
            sp->cno = sp->frp->cno;

            /* If returning to a file in vi, center the line. */
            F_SET(sp, SC_SCR_CENTER);
        } else {
            if (O_ISSET(sp, O_COMMENT))
                file_comment(sp);
            else
                sp->lno = 1;
            nb = 1;
        }
        if (db_get(sp, sp->lno, 0, NULL, &len)) {
            sp->lno = 1;
            sp->cno = 0;
            return;
        }
        if (!nb && sp->cno > len)
            nb = 1;
    }
    if (nb) {
        sp->cno = 0;
        (void)nonblank(sp, sp->lno, &sp->cno);
    }

    /*
     * !!!
     * The initial column is also the most attractive column.
     */
    sp->rcm = sp->cno;

    /*
     * !!!
     * Historically, vi initialized the absolute mark, but ex did not.
     * Which meant, that if the first command in ex mode was "visual",
     * or if an ex command was executed first (e.g. vi +10 file) vi was
     * entered without the mark being initialized.  For consistency, if
     * the file isn't empty, we initialize it for everyone, believing
     * that it can't hurt, and is generally useful.  Not initializing it
     * if the file is empty is historic practice, although it has always
     * been possible to set (and use) marks in empty vi files.
     */
    m.lno = sp->lno;
    m.cno = sp->cno;
    (void)mark_set(sp, ABSMARK1, &m, 0);
}
Пример #6
0
/*
 * vi --
 * 	Main vi command loop.
 *
 * PUBLIC: int vi __P((SCR **));
 */
int
vi(SCR **spp)
{
	GS *gp;
	MARK abs;
	SCR *next, *sp;
	VICMD cmd = { 0 }, *vp;
	VI_PRIVATE *vip;
	int comcount, mapped, rval;

	/* Get the first screen. */
	sp = *spp;
	gp = sp->gp;

	/* Point to the command structure. */
	vp = &cmd;

	/* Reset strange attraction. */
	F_SET(vp, VM_RCM_SET);

	/* Initialize the vi screen. */
	if (v_init(sp))
		return (1);

	/* Set the focus. */
	(void)sp->gp->scr_rename(sp, sp->frp->name, 1);

	for (vip = VIP(sp), rval = 0;;) {
		/* Resolve messages. */
		if (!MAPPED_KEYS_WAITING(sp) && vs_resolve(sp, NULL, 0))
			goto ret;

		/*
		 * If not skipping a refresh, return to command mode and
		 * refresh the screen.
		 */
		if (F_ISSET(vip, VIP_S_REFRESH))
			F_CLR(vip, VIP_S_REFRESH);
		else {
			sp->showmode = SM_COMMAND;
			if (vs_refresh(sp, 0))
				goto ret;
		}

		/* Set the new favorite position. */
		if (F_ISSET(vp, VM_RCM_SET | VM_RCM_SETFNB | VM_RCM_SETNNB)) {
			F_CLR(vip, VIP_RCM_LAST);
			(void)vs_column(sp, &sp->rcm);
		}

		/*
		 * If not currently in a map, log the cursor position,
		 * and set a flag so that this command can become the
		 * DOT command.
		 */
		if (MAPPED_KEYS_WAITING(sp))
			mapped = 1;
		else {
			if (log_cursor(sp))
				goto err;
			mapped = 0;
		}

		/*
		 * There may be an ex command waiting, and we returned here
		 * only because we exited a screen or file.  In this case,
		 * we simply go back into the ex parser.
		 */
		if (EXCMD_RUNNING(gp)) {
			vp->kp = &vikeys[':'];
			goto ex_continue;
		}

		/* Refresh the command structure. */
		memset(vp, 0, sizeof(VICMD));

		/*
		 * We get a command, which may or may not have an associated
		 * motion.  If it does, we get it too, calling its underlying
		 * function to get the resulting mark.  We then call the
		 * command setting the cursor to the resulting mark.
		 *
		 * !!!
		 * Vi historically flushed mapped characters on error, but
		 * entering extra <escape> characters at the beginning of
		 * a map wasn't considered an error -- in fact, users would
		 * put leading <escape> characters in maps to clean up vi
		 * state before the map was interpreted.  Beauty!
		 */
		switch (v_cmd(sp, DOT, vp, NULL, &comcount, &mapped)) {
		case GC_ERR:
			goto err;
		case GC_ERR_NOFLUSH:
			goto gc_err_noflush;
		case GC_EVENT:
			goto gc_event;
		case GC_FATAL:
			goto ret;
		case GC_INTERRUPT:
			goto intr;
		case GC_OK:
			break;
		}

		/* Check for security setting. */
		if (F_ISSET(vp->kp, V_SECURE) && O_ISSET(sp, O_SECURE)) {
			ex_emsg(sp, KEY_NAME(sp, vp->key), EXM_SECURE);
			goto err;
		}

		/*
		 * Historical practice: if a dot command gets a new count,
		 * any motion component goes away, i.e. "d3w2." deletes a
		 * total of 5 words.
		 */
		if (F_ISSET(vp, VC_ISDOT) && comcount)
			DOTMOTION->count = 1;

		/* Copy the key flags into the local structure. */
		F_SET(vp, vp->kp->flags);

		/* Prepare to set the previous context. */
		if (F_ISSET(vp, V_ABS | V_ABS_C | V_ABS_L)) {
			abs.lno = sp->lno;
			abs.cno = sp->cno;
		}

		/*
		 * Set the three cursor locations to the current cursor.  The
		 * underlying routines don't bother if the cursor doesn't move.
		 * This also handles line commands (e.g. Y) defaulting to the
		 * current line.
		 */
		vp->m_start.lno = vp->m_stop.lno = vp->m_final.lno = sp->lno;
		vp->m_start.cno = vp->m_stop.cno = vp->m_final.cno = sp->cno;

		/*
		 * Do any required motion; v_motion sets the from MARK and the
		 * line mode flag, as well as the VM_RCM flags.
		 */
		if (F_ISSET(vp, V_MOTION) &&
		    v_motion(sp, DOTMOTION, vp, &mapped)) {
			if (INTERRUPTED(sp))
				goto intr;
			goto err;
		}

		/*
		 * If a count is set and the command is line oriented, set the
		 * to MARK here relative to the cursor/from MARK.  This is for
		 * commands that take both counts and motions, i.e. "4yy" and
		 * "y%".  As there's no way the command can know which the user
		 * did, we have to do it here.  (There are commands that are
		 * line oriented and that take counts ("#G", "#H"), for which
		 * this calculation is either completely meaningless or wrong.
		 * Each command must validate the value for itself.
		 */
		if (F_ISSET(vp, VC_C1SET) && F_ISSET(vp, VM_LMODE))
			vp->m_stop.lno += vp->count - 1;

		/* Increment the command count. */
		++sp->ccnt;

#if defined(DEBUG) && defined(COMLOG)
		v_comlog(sp, vp);
#endif
		/* Call the function. */
ex_continue:	if (vp->kp->func(sp, vp))
			goto err;
gc_event:
#ifdef DEBUG
		/* Make sure no function left the temporary space locked. */
		if (F_ISSET(gp, G_TMP_INUSE)) {
			F_CLR(gp, G_TMP_INUSE);
			msgq(sp, M_ERR,
			    "232|vi: temporary buffer not released");
		}
#endif
		/*
		 * If we're exiting this screen, move to the next one, or, if
		 * there aren't any more, return to the main editor loop.  The
		 * ordering is careful, don't discard the contents of sp until
		 * the end.
		 */
		if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE)) {
			if (file_end(sp, NULL, F_ISSET(sp, SC_EXIT_FORCE)))
				goto ret;
			if (vs_discard(sp, &next))
				goto ret;
			if (next == NULL && vs_swap(sp, &next, NULL))
				goto ret;
			*spp = next;
			if (screen_end(sp))
				goto ret;
			if (next == NULL)
				break;

			/* Switch screens, change focus. */
			sp = next;
			vip = VIP(sp);
			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);

			/* Don't trust the cursor. */
			F_SET(vip, VIP_CUR_INVALID);

			continue;
		}

		/*
		 * Set the dot command structure.
		 *
		 * !!!
		 * Historically, commands which used mapped keys did not
		 * set the dot command, with the exception of the text
		 * input commands.
		 */
		if (F_ISSET(vp, V_DOT) && !mapped) {
			*DOT = cmd;
			F_SET(DOT, VC_ISDOT);

			/*
			 * If a count was supplied for both the command and
			 * its motion, the count was used only for the motion.
			 * Turn the count back on for the dot structure.
			 */
			if (F_ISSET(vp, VC_C1RESET))
				F_SET(DOT, VC_C1SET);

			/* VM flags aren't retained. */
			F_CLR(DOT, VM_COMMASK | VM_RCM_MASK);
		}

		/*
		 * Some vi row movements are "attracted" to the last position
		 * set, i.e. the VM_RCM commands are moths to the VM_RCM_SET
		 * commands' candle.  If the movement is to the EOL the vi
		 * command handles it.  If it's to the beginning, we handle it
		 * here.
		 *
		 * Note, some commands (e.g. _, ^) don't set the VM_RCM_SETFNB
		 * flag, but do the work themselves.  The reason is that they
		 * have to modify the column in case they're being used as a
		 * motion component.  Other similar commands (e.g. +, -) don't
		 * have to modify the column because they are always line mode
		 * operations when used as motions, so the column number isn't
		 * of any interest.
		 *
		 * Does this totally violate the screen and editor layering?
		 * You betcha.  As they say, if you think you understand it,
		 * you don't.
		 */
		switch (F_ISSET(vp, VM_RCM_MASK)) {
		case 0:
		case VM_RCM_SET:
			break;
		case VM_RCM:
			vp->m_final.cno = vs_rcm(sp,
			    vp->m_final.lno, F_ISSET(vip, VIP_RCM_LAST));
			break;
		case VM_RCM_SETLAST:
			F_SET(vip, VIP_RCM_LAST);
			break;
		case VM_RCM_SETFNB:
			vp->m_final.cno = 0;
			/* FALLTHROUGH */
		case VM_RCM_SETNNB:
			if (nonblank(sp, vp->m_final.lno, &vp->m_final.cno))
				goto err;
			break;
		default:
			abort();
		}

		/* Update the cursor. */
		sp->lno = vp->m_final.lno;
		sp->cno = vp->m_final.cno;

		/*
		 * Set the absolute mark -- set even if a tags or similar
		 * command, since the tag may be moving to the same file.
		 */
		if ((F_ISSET(vp, V_ABS) ||
		    (F_ISSET(vp, V_ABS_L) && sp->lno != abs.lno) ||
		    (F_ISSET(vp, V_ABS_C) &&
		    (sp->lno != abs.lno || sp->cno != abs.cno))) &&
		    mark_set(sp, ABSMARK1, &abs, 1))
			goto err;

		if (0) {
err:			if (v_event_flush(sp, CH_MAPPED))
				msgq(sp, M_BERR,
			    "110|Vi command failed: mapped keys discarded");
		}

		/*
		 * Check and clear interrupts.  There's an obvious race, but
		 * it's not worth fixing.
		 */
gc_err_noflush:	if (INTERRUPTED(sp)) {
intr:			CLR_INTERRUPT(sp);
			if (v_event_flush(sp, CH_MAPPED))
				msgq(sp, M_ERR,
				    "231|Interrupted: mapped keys discarded");
			else
				msgq(sp, M_ERR, "236|Interrupted");
		}

		/* If the last command switched screens, update. */
		if (F_ISSET(sp, SC_SSWITCH)) {
			F_CLR(sp, SC_SSWITCH);

			/*
			 * If the current screen is still displayed, it will
			 * need a new status line.
			 */
			F_SET(sp, SC_STATUS);

			/* Switch screens, change focus. */
			sp = sp->nextdisp;
			vip = VIP(sp);
			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);

			/* Don't trust the cursor. */
			F_SET(vip, VIP_CUR_INVALID);

			/* Refresh so we can display messages. */
			if (vs_refresh(sp, 1))
				return (1);
		}

		/* If the last command switched files, change focus. */
		if (F_ISSET(sp, SC_FSWITCH)) {
			F_CLR(sp, SC_FSWITCH);
			(void)sp->gp->scr_rename(sp, sp->frp->name, 1);
		}

		/* If leaving vi, return to the main editor loop. */
		if (F_ISSET(gp, G_SRESTART) || F_ISSET(sp, SC_EX)) {
			*spp = sp;
			v_dtoh(sp);
			gp->scr_discard(sp, NULL);
			break;
		}
	}
	if (0)
ret:		rval = 1;
	return (rval);
}
Пример #7
0
/*
 * v_mark -- m[a-z]
 *	Set a mark.
 *
 * PUBLIC: int v_mark __P((SCR *, VICMD *));
 */
int
v_mark(SCR *sp, VICMD *vp)
{
	return (mark_set(sp, vp->character, &vp->m_start, 1));
}
Пример #8
0
static void mark_cur(int c)
{
	mark_set(c, gui_y(), gui_x());
}