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