Home | History | Annotate | Line # | Download | only in common
      1  1.4  christos /*	$NetBSD: mark.c,v 1.4 2014/01/26 21:43:45 christos Exp $ */
      2  1.1  christos /*-
      3  1.1  christos  * Copyright (c) 1992, 1993, 1994
      4  1.1  christos  *	The Regents of the University of California.  All rights reserved.
      5  1.1  christos  * Copyright (c) 1992, 1993, 1994, 1995, 1996
      6  1.1  christos  *	Keith Bostic.  All rights reserved.
      7  1.1  christos  *
      8  1.1  christos  * See the LICENSE file for redistribution information.
      9  1.1  christos  */
     10  1.1  christos 
     11  1.1  christos #include "config.h"
     12  1.1  christos 
     13  1.4  christos #include <sys/cdefs.h>
     14  1.4  christos #if 0
     15  1.1  christos #ifndef lint
     16  1.1  christos static const char sccsid[] = "Id: mark.c,v 10.15 2001/06/25 15:19:11 skimo Exp  (Berkeley) Date: 2001/06/25 15:19:11 ";
     17  1.1  christos #endif /* not lint */
     18  1.4  christos #else
     19  1.4  christos __RCSID("$NetBSD: mark.c,v 1.4 2014/01/26 21:43:45 christos Exp $");
     20  1.4  christos #endif
     21  1.1  christos 
     22  1.1  christos #include <sys/types.h>
     23  1.1  christos #include <sys/queue.h>
     24  1.1  christos 
     25  1.1  christos #include <bitstring.h>
     26  1.1  christos #include <errno.h>
     27  1.1  christos #include <limits.h>
     28  1.1  christos #include <stdio.h>
     29  1.1  christos #include <stdlib.h>
     30  1.1  christos #include <string.h>
     31  1.1  christos 
     32  1.1  christos #include "common.h"
     33  1.1  christos 
     34  1.1  christos static LMARK *mark_find __P((SCR *, ARG_CHAR_T));
     35  1.1  christos 
     36  1.1  christos /*
     37  1.1  christos  * Marks are maintained in a key sorted doubly linked list.  We can't
     38  1.1  christos  * use arrays because we have no idea how big an index key could be.
     39  1.1  christos  * The underlying assumption is that users don't have more than, say,
     40  1.1  christos  * 10 marks at any one time, so this will be is fast enough.
     41  1.1  christos  *
     42  1.1  christos  * Marks are fixed, and modifications to the line don't update the mark's
     43  1.1  christos  * position in the line.  This can be hard.  If you add text to the line,
     44  1.1  christos  * place a mark in that text, undo the addition and use ` to move to the
     45  1.1  christos  * mark, the location will have disappeared.  It's tempting to try to adjust
     46  1.1  christos  * the mark with the changes in the line, but this is hard to do, especially
     47  1.1  christos  * if we've given the line to v_ntext.c:v_ntext() for editing.  Historic vi
     48  1.1  christos  * would move to the first non-blank on the line when the mark location was
     49  1.1  christos  * past the end of the line.  This can be complicated by deleting to a mark
     50  1.1  christos  * that has disappeared using the ` command.  Historic vi treated this as
     51  1.1  christos  * a line-mode motion and deleted the line.  This implementation complains to
     52  1.1  christos  * the user.
     53  1.1  christos  *
     54  1.1  christos  * In historic vi, marks returned if the operation was undone, unless the
     55  1.1  christos  * mark had been subsequently reset.  Tricky.  This is hard to start with,
     56  1.1  christos  * but in the presence of repeated undo it gets nasty.  When a line is
     57  1.1  christos  * deleted, we delete (and log) any marks on that line.  An undo will create
     58  1.1  christos  * the mark.  Any mark creations are noted as to whether the user created
     59  1.1  christos  * it or if it was created by an undo.  The former cannot be reset by another
     60  1.1  christos  * undo, but the latter may.
     61  1.1  christos  *
     62  1.1  christos  * All of these routines translate ABSMARK2 to ABSMARK1.  Setting either of
     63  1.1  christos  * the absolute mark locations sets both, so that "m'" and "m`" work like
     64  1.1  christos  * they, ah, for lack of a better word, "should".
     65  1.1  christos  */
     66  1.1  christos 
     67  1.1  christos /*
     68  1.1  christos  * mark_init --
     69  1.1  christos  *	Set up the marks.
     70  1.1  christos  *
     71  1.1  christos  * PUBLIC: int mark_init __P((SCR *, EXF *));
     72  1.1  christos  */
     73  1.1  christos int
     74  1.1  christos mark_init(SCR *sp, EXF *ep)
     75  1.1  christos {
     76  1.1  christos 	/*
     77  1.1  christos 	 * !!!
     78  1.1  christos 	 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
     79  1.1  christos 	 *
     80  1.1  christos 	 * Set up the marks.
     81  1.1  christos 	 */
     82  1.1  christos 	LIST_INIT(&ep->marks);
     83  1.1  christos 	return (0);
     84  1.1  christos }
     85  1.1  christos 
     86  1.1  christos /*
     87  1.1  christos  * mark_end --
     88  1.1  christos  *	Free up the marks.
     89  1.1  christos  *
     90  1.1  christos  * PUBLIC: int mark_end __P((SCR *, EXF *));
     91  1.1  christos  */
     92  1.1  christos int
     93  1.1  christos mark_end(SCR *sp, EXF *ep)
     94  1.1  christos {
     95  1.1  christos 	LMARK *lmp;
     96  1.1  christos 
     97  1.1  christos 	/*
     98  1.1  christos 	 * !!!
     99  1.1  christos 	 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER.
    100  1.1  christos 	 */
    101  1.3  christos 	while ((lmp = LIST_FIRST(&ep->marks)) != NULL) {
    102  1.1  christos 		LIST_REMOVE(lmp, q);
    103  1.1  christos 		free(lmp);
    104  1.1  christos 	}
    105  1.1  christos 	return (0);
    106  1.1  christos }
    107  1.1  christos 
    108  1.1  christos /*
    109  1.1  christos  * mark_get --
    110  1.1  christos  *	Get the location referenced by a mark.
    111  1.1  christos  *
    112  1.1  christos  * PUBLIC: int mark_get __P((SCR *, ARG_CHAR_T, MARK *, mtype_t));
    113  1.1  christos  */
    114  1.1  christos int
    115  1.1  christos mark_get(SCR *sp, ARG_CHAR_T key, MARK *mp, mtype_t mtype)
    116  1.1  christos {
    117  1.1  christos 	LMARK *lmp;
    118  1.1  christos 
    119  1.1  christos 	if (key == ABSMARK2)
    120  1.1  christos 		key = ABSMARK1;
    121  1.1  christos 
    122  1.1  christos 	lmp = mark_find(sp, key);
    123  1.2  christos 	if (lmp == NULL || (ARG_CHAR_T)lmp->name != key) {
    124  1.1  christos 		msgq(sp, mtype, "017|Mark %s: not set", KEY_NAME(sp, key));
    125  1.1  christos                 return (1);
    126  1.1  christos 	}
    127  1.1  christos 	if (F_ISSET(lmp, MARK_DELETED)) {
    128  1.1  christos 		msgq(sp, mtype,
    129  1.1  christos 		    "018|Mark %s: the line was deleted", KEY_NAME(sp, key));
    130  1.1  christos                 return (1);
    131  1.1  christos 	}
    132  1.1  christos 
    133  1.1  christos 	/*
    134  1.1  christos 	 * !!!
    135  1.1  christos 	 * The absolute mark is initialized to lno 1/cno 0, and historically
    136  1.1  christos 	 * you could use it in an empty file.  Make such a mark always work.
    137  1.1  christos 	 */
    138  1.1  christos 	if ((lmp->lno != 1 || lmp->cno != 0) && !db_exist(sp, lmp->lno)) {
    139  1.1  christos 		msgq(sp, mtype,
    140  1.1  christos 		    "019|Mark %s: cursor position no longer exists",
    141  1.1  christos 		    KEY_NAME(sp, key));
    142  1.1  christos 		return (1);
    143  1.1  christos 	}
    144  1.1  christos 	mp->lno = lmp->lno;
    145  1.1  christos 	mp->cno = lmp->cno;
    146  1.1  christos 	return (0);
    147  1.1  christos }
    148  1.1  christos 
    149  1.1  christos /*
    150  1.1  christos  * mark_set --
    151  1.1  christos  *	Set the location referenced by a mark.
    152  1.1  christos  *
    153  1.1  christos  * PUBLIC: int mark_set __P((SCR *, ARG_CHAR_T, MARK *, int));
    154  1.1  christos  */
    155  1.1  christos int
    156  1.1  christos mark_set(SCR *sp, ARG_CHAR_T key, MARK *value, int userset)
    157  1.1  christos {
    158  1.1  christos 	LMARK *lmp, *lmt;
    159  1.1  christos 
    160  1.1  christos 	if (key == ABSMARK2)
    161  1.1  christos 		key = ABSMARK1;
    162  1.1  christos 
    163  1.1  christos 	/*
    164  1.1  christos 	 * The rules are simple.  If the user is setting a mark (if it's a
    165  1.1  christos 	 * new mark this is always true), it always happens.  If not, it's
    166  1.1  christos 	 * an undo, and we set it if it's not already set or if it was set
    167  1.1  christos 	 * by a previous undo.
    168  1.1  christos 	 */
    169  1.1  christos 	lmp = mark_find(sp, key);
    170  1.2  christos 	if (lmp == NULL || (ARG_CHAR_T)lmp->name != key) {
    171  1.1  christos 		MALLOC_RET(sp, lmt, LMARK *, sizeof(LMARK));
    172  1.1  christos 		if (lmp == NULL) {
    173  1.1  christos 			LIST_INSERT_HEAD(&sp->ep->marks, lmt, q);
    174  1.1  christos 		} else
    175  1.1  christos 			LIST_INSERT_AFTER(lmp, lmt, q);
    176  1.1  christos 		lmp = lmt;
    177  1.1  christos 	} else if (!userset &&
    178  1.1  christos 	    !F_ISSET(lmp, MARK_DELETED) && F_ISSET(lmp, MARK_USERSET))
    179  1.1  christos 		return (0);
    180  1.1  christos 
    181  1.1  christos 	lmp->lno = value->lno;
    182  1.1  christos 	lmp->cno = value->cno;
    183  1.1  christos 	lmp->name = key;
    184  1.1  christos 	lmp->flags = userset ? MARK_USERSET : 0;
    185  1.1  christos 	return (0);
    186  1.1  christos }
    187  1.1  christos 
    188  1.1  christos /*
    189  1.1  christos  * mark_find --
    190  1.1  christos  *	Find the requested mark, or, the slot immediately before
    191  1.1  christos  *	where it would go.
    192  1.1  christos  */
    193  1.1  christos static LMARK *
    194  1.1  christos mark_find(SCR *sp, ARG_CHAR_T key)
    195  1.1  christos {
    196  1.1  christos 	LMARK *lmp, *lastlmp;
    197  1.1  christos 
    198  1.1  christos 	/*
    199  1.1  christos 	 * Return the requested mark or the slot immediately before
    200  1.1  christos 	 * where it should go.
    201  1.1  christos 	 */
    202  1.3  christos 	for (lastlmp = NULL, lmp = LIST_FIRST(&sp->ep->marks);
    203  1.3  christos 	    lmp != NULL; lastlmp = lmp, lmp = LIST_NEXT(lmp, q))
    204  1.2  christos 		if ((ARG_CHAR_T)lmp->name >= key)
    205  1.2  christos 			return ((ARG_CHAR_T)lmp->name == key ? lmp : lastlmp);
    206  1.1  christos 	return (lastlmp);
    207  1.1  christos }
    208  1.1  christos 
    209  1.1  christos /*
    210  1.1  christos  * mark_insdel --
    211  1.1  christos  *	Update the marks based on an insertion or deletion.
    212  1.1  christos  *
    213  1.1  christos  * PUBLIC: int mark_insdel __P((SCR *, lnop_t, db_recno_t));
    214  1.1  christos  */
    215  1.1  christos int
    216  1.1  christos mark_insdel(SCR *sp, lnop_t op, db_recno_t lno)
    217  1.1  christos {
    218  1.1  christos 	LMARK *lmp;
    219  1.1  christos 	db_recno_t lline;
    220  1.1  christos 
    221  1.1  christos 	switch (op) {
    222  1.1  christos 	case LINE_APPEND:
    223  1.1  christos 		/* All insert/append operations are done as inserts. */
    224  1.1  christos 		abort();
    225  1.1  christos 	case LINE_DELETE:
    226  1.3  christos 		LIST_FOREACH(lmp, &sp->ep->marks, q)
    227  1.2  christos 			if (lmp->lno >= lno) {
    228  1.1  christos 				if (lmp->lno == lno) {
    229  1.1  christos 					F_SET(lmp, MARK_DELETED);
    230  1.1  christos 					(void)log_mark(sp, lmp);
    231  1.1  christos 				} else
    232  1.1  christos 					--lmp->lno;
    233  1.2  christos 			}
    234  1.1  christos 		break;
    235  1.1  christos 	case LINE_INSERT:
    236  1.1  christos 		/*
    237  1.1  christos 		 * XXX
    238  1.1  christos 		 * Very nasty special case.  If the file was empty, then we're
    239  1.1  christos 		 * adding the first line, which is a replacement.  So, we don't
    240  1.1  christos 		 * modify the marks.  This is a hack to make:
    241  1.1  christos 		 *
    242  1.1  christos 		 *	mz:r!echo foo<carriage-return>'z
    243  1.1  christos 		 *
    244  1.1  christos 		 * work, i.e. historically you could mark the "line" in an empty
    245  1.1  christos 		 * file and replace it, and continue to use the mark.  Insane,
    246  1.1  christos 		 * well, yes, I know, but someone complained.
    247  1.1  christos 		 *
    248  1.1  christos 		 * Check for line #2 before going to the end of the file.
    249  1.1  christos 		 */
    250  1.1  christos 		if (!db_exist(sp, 2)) {
    251  1.1  christos 			if (db_last(sp, &lline))
    252  1.1  christos 				return (1);
    253  1.1  christos 			if (lline == 1)
    254  1.1  christos 				return (0);
    255  1.1  christos 		}
    256  1.1  christos 
    257  1.3  christos 		LIST_FOREACH(lmp, &sp->ep->marks, q)
    258  1.1  christos 			if (lmp->lno >= lno)
    259  1.1  christos 				++lmp->lno;
    260  1.1  christos 		break;
    261  1.1  christos 	case LINE_RESET:
    262  1.1  christos 		break;
    263  1.1  christos 	}
    264  1.1  christos 	return (0);
    265  1.1  christos }
    266