119304Speter/*-
219304Speter * Copyright (c) 1992, 1993, 1994
319304Speter *	The Regents of the University of California.  All rights reserved.
419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996
519304Speter *	Keith Bostic.  All rights reserved.
619304Speter *
719304Speter * See the LICENSE file for redistribution information.
819304Speter */
919304Speter
1019304Speter#include "config.h"
1119304Speter
1219304Speter#ifndef lint
13254225Speterstatic const char sccsid[] = "$Id: mark.c,v 10.14 2011/07/04 14:42:58 zy Exp $";
1419304Speter#endif /* not lint */
1519304Speter
1619304Speter#include <sys/types.h>
1719304Speter#include <sys/queue.h>
18254225Speter#include <sys/time.h>
1919304Speter
2019304Speter#include <bitstring.h>
2119304Speter#include <errno.h>
2219304Speter#include <limits.h>
2319304Speter#include <stdio.h>
2419304Speter#include <stdlib.h>
2519304Speter#include <string.h>
2619304Speter
2719304Speter#include "common.h"
2819304Speter
2919304Speterstatic LMARK *mark_find __P((SCR *, ARG_CHAR_T));
3019304Speter
3119304Speter/*
32254225Speter * Marks are maintained in a key sorted singly linked list.  We can't
3319304Speter * use arrays because we have no idea how big an index key could be.
3419304Speter * The underlying assumption is that users don't have more than, say,
3519304Speter * 10 marks at any one time, so this will be is fast enough.
3619304Speter *
3719304Speter * Marks are fixed, and modifications to the line don't update the mark's
3819304Speter * position in the line.  This can be hard.  If you add text to the line,
3919304Speter * place a mark in that text, undo the addition and use ` to move to the
4019304Speter * mark, the location will have disappeared.  It's tempting to try to adjust
4119304Speter * the mark with the changes in the line, but this is hard to do, especially
4219304Speter * if we've given the line to v_ntext.c:v_ntext() for editing.  Historic vi
4319304Speter * would move to the first non-blank on the line when the mark location was
4419304Speter * past the end of the line.  This can be complicated by deleting to a mark
4519304Speter * that has disappeared using the ` command.  Historic vi treated this as
4619304Speter * a line-mode motion and deleted the line.  This implementation complains to
4719304Speter * the user.
4819304Speter *
4919304Speter * In historic vi, marks returned if the operation was undone, unless the
5019304Speter * mark had been subsequently reset.  Tricky.  This is hard to start with,
5119304Speter * but in the presence of repeated undo it gets nasty.  When a line is
5219304Speter * deleted, we delete (and log) any marks on that line.  An undo will create
5319304Speter * the mark.  Any mark creations are noted as to whether the user created
5419304Speter * it or if it was created by an undo.  The former cannot be reset by another
5519304Speter * undo, but the latter may.
5619304Speter *
5719304Speter * All of these routines translate ABSMARK2 to ABSMARK1.  Setting either of
5819304Speter * the absolute mark locations sets both, so that "m'" and "m`" work like
5919304Speter * they, ah, for lack of a better word, "should".
6019304Speter */
6119304Speter
6219304Speter/*
6319304Speter * mark_init --
6419304Speter *	Set up the marks.
6519304Speter *
6619304Speter * PUBLIC: int mark_init __P((SCR *, EXF *));
6719304Speter */
6819304Speterint
69254225Spetermark_init(
70254225Speter	SCR *sp,
71254225Speter	EXF *ep)
7219304Speter{
7319304Speter	/*
7419304Speter	 * !!!
7519304Speter	 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
7619304Speter	 *
7719304Speter	 * Set up the marks.
7819304Speter	 */
79254225Speter	SLIST_INIT(ep->marks);
8019304Speter	return (0);
8119304Speter}
8219304Speter
8319304Speter/*
8419304Speter * mark_end --
8519304Speter *	Free up the marks.
8619304Speter *
8719304Speter * PUBLIC: int mark_end __P((SCR *, EXF *));
8819304Speter */
8919304Speterint
90254225Spetermark_end(
91254225Speter	SCR *sp,
92254225Speter	EXF *ep)
9319304Speter{
9419304Speter	LMARK *lmp;
9519304Speter
9619304Speter	/*
9719304Speter	 * !!!
9819304Speter	 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
9919304Speter	 */
100254225Speter	while ((lmp = SLIST_FIRST(ep->marks)) != NULL) {
101254225Speter		SLIST_REMOVE_HEAD(ep->marks, q);
10219304Speter		free(lmp);
10319304Speter	}
10419304Speter	return (0);
10519304Speter}
10619304Speter
10719304Speter/*
10819304Speter * mark_get --
10919304Speter *	Get the location referenced by a mark.
11019304Speter *
11119304Speter * PUBLIC: int mark_get __P((SCR *, ARG_CHAR_T, MARK *, mtype_t));
11219304Speter */
11319304Speterint
114254225Spetermark_get(
115254225Speter	SCR *sp,
116254225Speter	ARG_CHAR_T key,
117254225Speter	MARK *mp,
118254225Speter	mtype_t mtype)
11919304Speter{
12019304Speter	LMARK *lmp;
12119304Speter
12219304Speter	if (key == ABSMARK2)
12319304Speter		key = ABSMARK1;
12419304Speter
12519304Speter	lmp = mark_find(sp, key);
12619304Speter	if (lmp == NULL || lmp->name != key) {
12719304Speter		msgq(sp, mtype, "017|Mark %s: not set", KEY_NAME(sp, key));
12819304Speter                return (1);
12919304Speter	}
13019304Speter	if (F_ISSET(lmp, MARK_DELETED)) {
13119304Speter		msgq(sp, mtype,
13219304Speter		    "018|Mark %s: the line was deleted", KEY_NAME(sp, key));
13319304Speter                return (1);
13419304Speter	}
13519304Speter
13619304Speter	/*
13719304Speter	 * !!!
13819304Speter	 * The absolute mark is initialized to lno 1/cno 0, and historically
13919304Speter	 * you could use it in an empty file.  Make such a mark always work.
14019304Speter	 */
14119304Speter	if ((lmp->lno != 1 || lmp->cno != 0) && !db_exist(sp, lmp->lno)) {
14219304Speter		msgq(sp, mtype,
14319304Speter		    "019|Mark %s: cursor position no longer exists",
14419304Speter		    KEY_NAME(sp, key));
14519304Speter		return (1);
14619304Speter	}
14719304Speter	mp->lno = lmp->lno;
14819304Speter	mp->cno = lmp->cno;
14919304Speter	return (0);
15019304Speter}
15119304Speter
15219304Speter/*
15319304Speter * mark_set --
15419304Speter *	Set the location referenced by a mark.
15519304Speter *
15619304Speter * PUBLIC: int mark_set __P((SCR *, ARG_CHAR_T, MARK *, int));
15719304Speter */
15819304Speterint
159254225Spetermark_set(
160254225Speter	SCR *sp,
161254225Speter	ARG_CHAR_T key,
162254225Speter	MARK *value,
163254225Speter	int userset)
16419304Speter{
16519304Speter	LMARK *lmp, *lmt;
16619304Speter
16719304Speter	if (key == ABSMARK2)
16819304Speter		key = ABSMARK1;
16919304Speter
17019304Speter	/*
17119304Speter	 * The rules are simple.  If the user is setting a mark (if it's a
17219304Speter	 * new mark this is always true), it always happens.  If not, it's
17319304Speter	 * an undo, and we set it if it's not already set or if it was set
17419304Speter	 * by a previous undo.
17519304Speter	 */
17619304Speter	lmp = mark_find(sp, key);
17719304Speter	if (lmp == NULL || lmp->name != key) {
17819304Speter		MALLOC_RET(sp, lmt, LMARK *, sizeof(LMARK));
17919304Speter		if (lmp == NULL) {
180254225Speter			SLIST_INSERT_HEAD(sp->ep->marks, lmt, q);
18119304Speter		} else
182254225Speter			SLIST_INSERT_AFTER(lmp, lmt, q);
18319304Speter		lmp = lmt;
18419304Speter	} else if (!userset &&
18519304Speter	    !F_ISSET(lmp, MARK_DELETED) && F_ISSET(lmp, MARK_USERSET))
18619304Speter		return (0);
18719304Speter
18819304Speter	lmp->lno = value->lno;
18919304Speter	lmp->cno = value->cno;
19019304Speter	lmp->name = key;
19119304Speter	lmp->flags = userset ? MARK_USERSET : 0;
19219304Speter	return (0);
19319304Speter}
19419304Speter
19519304Speter/*
19619304Speter * mark_find --
19719304Speter *	Find the requested mark, or, the slot immediately before
19819304Speter *	where it would go.
19919304Speter */
20019304Speterstatic LMARK *
201254225Spetermark_find(
202254225Speter	SCR *sp,
203254225Speter	ARG_CHAR_T key)
20419304Speter{
205254225Speter	LMARK *lmp, *lastlmp = NULL;
20619304Speter
20719304Speter	/*
20819304Speter	 * Return the requested mark or the slot immediately before
20919304Speter	 * where it should go.
21019304Speter	 */
211254225Speter	SLIST_FOREACH(lmp, sp->ep->marks, q) {
21219304Speter		if (lmp->name >= key)
21319304Speter			return (lmp->name == key ? lmp : lastlmp);
214254225Speter		lastlmp = lmp;
215254225Speter	}
21619304Speter	return (lastlmp);
21719304Speter}
21819304Speter
21919304Speter/*
22019304Speter * mark_insdel --
22119304Speter *	Update the marks based on an insertion or deletion.
22219304Speter *
22319304Speter * PUBLIC: int mark_insdel __P((SCR *, lnop_t, recno_t));
22419304Speter */
22519304Speterint
226254225Spetermark_insdel(
227254225Speter	SCR *sp,
228254225Speter	lnop_t op,
229254225Speter	recno_t lno)
23019304Speter{
23119304Speter	LMARK *lmp;
23219304Speter	recno_t lline;
23319304Speter
23419304Speter	switch (op) {
23519304Speter	case LINE_APPEND:
23619304Speter		/* All insert/append operations are done as inserts. */
23719304Speter		abort();
23819304Speter	case LINE_DELETE:
239254225Speter		SLIST_FOREACH(lmp, sp->ep->marks, q)
24019304Speter			if (lmp->lno >= lno)
24119304Speter				if (lmp->lno == lno) {
24219304Speter					F_SET(lmp, MARK_DELETED);
24319304Speter					(void)log_mark(sp, lmp);
24419304Speter				} else
24519304Speter					--lmp->lno;
24619304Speter		break;
24719304Speter	case LINE_INSERT:
24819304Speter		/*
24919304Speter		 * XXX
25019304Speter		 * Very nasty special case.  If the file was empty, then we're
25119304Speter		 * adding the first line, which is a replacement.  So, we don't
25219304Speter		 * modify the marks.  This is a hack to make:
25319304Speter		 *
25419304Speter		 *	mz:r!echo foo<carriage-return>'z
25519304Speter		 *
25619304Speter		 * work, i.e. historically you could mark the "line" in an empty
25719304Speter		 * file and replace it, and continue to use the mark.  Insane,
25819304Speter		 * well, yes, I know, but someone complained.
25919304Speter		 *
26019304Speter		 * Check for line #2 before going to the end of the file.
26119304Speter		 */
26219304Speter		if (!db_exist(sp, 2)) {
26319304Speter			if (db_last(sp, &lline))
26419304Speter				return (1);
26519304Speter			if (lline == 1)
26619304Speter				return (0);
26719304Speter		}
26819304Speter
269254225Speter		SLIST_FOREACH(lmp, sp->ep->marks, q)
27019304Speter			if (lmp->lno >= lno)
27119304Speter				++lmp->lno;
27219304Speter		break;
27319304Speter	case LINE_RESET:
27419304Speter		break;
27519304Speter	}
27619304Speter	return (0);
27719304Speter}
278