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