/*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint /* from: static char sccsid[] = "@(#)mark.c 8.12 (Berkeley) 12/27/93"; */ static char *rcsid = "$Id: mark.c,v 1.2 1994/01/24 06:39:00 cgd Exp $"; #endif /* not lint */ #include #include #include #include #include "vi.h" static MARK *mark_find __P((SCR *, EXF *, ARG_CHAR_T)); /* * Marks are maintained in a key sorted doubly linked list. We can't * use arrays because we have no idea how big an index key could be. * The underlying assumption is that users don't have more than, say, * 10 marks at any one time, so this will be is fast enough. * * Marks are fixed, and modifications to the line don't update the mark's * position in the line. This can be hard. If you add text to the line, * place a mark in that text, undo the addition and use ` to move to the * mark, the location will have disappeared. It's tempting to try to adjust * the mark with the changes in the line, but this is hard to do, especially * if we've given the line to v_ntext.c:v_ntext() for editing. Historic vi * would move to the first non-blank on the line when the mark location was * past the end of the line. This can be complicated by deleting to a mark * that has disappeared using the ` command. Historic vi vi treated this as * a line-mode motion and deleted the line. This implementation complains to * the user. * * In historic vi, marks returned if the operation was undone, unless the * mark had been subsequently reset. Tricky. This is hard to start with, * but in the presence of repeated undo it gets nasty. When a line is * deleted, we delete (and log) any marks on that line. An undo will create * the mark. Any mark creations are noted as to whether the user created * it or if it was created by an undo. The former cannot be reset by another * undo, but the latter may. * * All of these routines translate ABSMARK2 to ABSMARK1. Setting either of * the absolute mark locations sets both, so that "m'" and "m`" work like * they, ah, for lack of a better word, "should". */ /* * mark_init -- * Set up the marks. */ int mark_init(sp, ep) SCR *sp; EXF *ep; { MARK *mp; /* * Make sure the marks have been set up. If they * haven't, do so, and create the absolute mark. */ MALLOC_RET(sp, mp, MARK *, sizeof(MARK)); mp->lno = 1; mp->cno = 0; mp->name = ABSMARK1; mp->flags = 0; LIST_INSERT_HEAD(&ep->marks, mp, q); return (0); } /* * mark_end -- * Free up the marks. */ int mark_end(sp, ep) SCR *sp; EXF *ep; { MARK *mp; while ((mp = ep->marks.lh_first) != NULL) { LIST_REMOVE(mp, q); FREE(mp, sizeof(MARK)); } return (0); } /* * mark_get -- * Get the location referenced by a mark. */ MARK * mark_get(sp, ep, key) SCR *sp; EXF *ep; ARG_CHAR_T key; { MARK *mp; size_t len; char *p; if (key == ABSMARK2) key = ABSMARK1; mp = mark_find(sp, ep, key); if (mp == NULL || mp->name != key) { msgq(sp, M_BERR, "Mark %s: not set.", charname(sp, key)); return (NULL); } if (F_ISSET(mp, MARK_DELETED)) { msgq(sp, M_BERR, "Mark %s: the line was deleted.", charname(sp, key)); return (NULL); } if ((p = file_gline(sp, ep, mp->lno, &len)) == NULL || mp->cno > len || mp->cno == len && len != 0) { msgq(sp, M_BERR, "Mark %s: cursor position no longer exists.", charname(sp, key)); return (NULL); } return (mp); } /* * mark_set -- * Set the location referenced by a mark. */ int mark_set(sp, ep, key, value, userset) SCR *sp; EXF *ep; ARG_CHAR_T key; MARK *value; int userset; { MARK *mp, *mt; if (key == ABSMARK2) key = ABSMARK1; /* * The rules are simple. If the user is setting a mark (if it's a * new mark this is always true), it always happens. If not, it's * an undo, and we set it if it's not already set or if it was set * by a previous undo. */ mp = mark_find(sp, ep, key); if (mp == NULL || mp->name != key) { MALLOC_RET(sp, mt, MARK *, sizeof(MARK)); if (mp == NULL) { LIST_INSERT_HEAD(&ep->marks, mt, q); } else LIST_INSERT_AFTER(mp, mt, q); mp = mt; } else if (!userset && !F_ISSET(mp, MARK_DELETED) && F_ISSET(mp, MARK_USERSET)) return (0); mp->lno = value->lno; mp->cno = value->cno; mp->name = key; mp->flags = userset ? MARK_USERSET : 0; return (0); } /* * mark_find -- * Find the requested mark, or, the slot immediately before * where it would go. */ static MARK * mark_find(sp, ep, key) SCR *sp; EXF *ep; ARG_CHAR_T key; { MARK *mp, *lastmp; /* * Return the requested mark or the slot immediately before * where it should go. */ for (lastmp = NULL, mp = ep->marks.lh_first; mp != NULL; lastmp = mp, mp = mp->q.le_next) if (mp->name >= key) return (mp->name == key ? mp : lastmp); return (lastmp); } /* * mark_insdel -- * Update the marks based on an insertion or deletion. */ void mark_insdel(sp, ep, op, lno) SCR *sp; EXF *ep; enum operation op; recno_t lno; { MARK *mp; switch (op) { case LINE_APPEND: return; case LINE_DELETE: for (mp = ep->marks.lh_first; mp != NULL; mp = mp->q.le_next) if (mp->lno >= lno) if (mp->lno == lno) { F_SET(mp, MARK_DELETED); (void)log_mark(sp, ep, mp); } else --mp->lno; return; case LINE_INSERT: for (mp = ep->marks.lh_first; mp != NULL; mp = mp->q.le_next) if (mp->lno >= lno) ++mp->lno; return; case LINE_RESET: return; } /* NOTREACHED */ }