1 /*- 2 * Copyright (c) 1992, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * Copyright (c) 1992, 1993, 1994, 1995, 1996 5 * Keith Bostic. All rights reserved. 6 * 7 * See the LICENSE file for redistribution information. 8 */ 9 10 #include "config.h" 11 12 #ifndef lint 13 static const char sccsid[] = "$Id: mark.c,v 10.14 2011/07/04 14:42:58 zy Exp $"; 14 #endif /* not lint */ 15 16 #include <sys/types.h> 17 #include <sys/queue.h> 18 #include <sys/time.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 singly 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( 70 SCR *sp, 71 EXF *ep) 72 { 73 /* 74 * !!! 75 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER. 76 * 77 * Set up the marks. 78 */ 79 SLIST_INIT(ep->marks); 80 return (0); 81 } 82 83 /* 84 * mark_end -- 85 * Free up the marks. 86 * 87 * PUBLIC: int mark_end __P((SCR *, EXF *)); 88 */ 89 int 90 mark_end( 91 SCR *sp, 92 EXF *ep) 93 { 94 LMARK *lmp; 95 96 /* 97 * !!! 98 * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER. 99 */ 100 while ((lmp = SLIST_FIRST(ep->marks)) != NULL) { 101 SLIST_REMOVE_HEAD(ep->marks, q); 102 free(lmp); 103 } 104 return (0); 105 } 106 107 /* 108 * mark_get -- 109 * Get the location referenced by a mark. 110 * 111 * PUBLIC: int mark_get __P((SCR *, ARG_CHAR_T, MARK *, mtype_t)); 112 */ 113 int 114 mark_get( 115 SCR *sp, 116 ARG_CHAR_T key, 117 MARK *mp, 118 mtype_t mtype) 119 { 120 LMARK *lmp; 121 122 if (key == ABSMARK2) 123 key = ABSMARK1; 124 125 lmp = mark_find(sp, key); 126 if (lmp == NULL || lmp->name != key) { 127 msgq(sp, mtype, "017|Mark %s: not set", KEY_NAME(sp, key)); 128 return (1); 129 } 130 if (F_ISSET(lmp, MARK_DELETED)) { 131 msgq(sp, mtype, 132 "018|Mark %s: the line was deleted", KEY_NAME(sp, key)); 133 return (1); 134 } 135 136 /* 137 * !!! 138 * The absolute mark is initialized to lno 1/cno 0, and historically 139 * you could use it in an empty file. Make such a mark always work. 140 */ 141 if ((lmp->lno != 1 || lmp->cno != 0) && !db_exist(sp, lmp->lno)) { 142 msgq(sp, mtype, 143 "019|Mark %s: cursor position no longer exists", 144 KEY_NAME(sp, key)); 145 return (1); 146 } 147 mp->lno = lmp->lno; 148 mp->cno = lmp->cno; 149 return (0); 150 } 151 152 /* 153 * mark_set -- 154 * Set the location referenced by a mark. 155 * 156 * PUBLIC: int mark_set __P((SCR *, ARG_CHAR_T, MARK *, int)); 157 */ 158 int 159 mark_set( 160 SCR *sp, 161 ARG_CHAR_T key, 162 MARK *value, 163 int userset) 164 { 165 LMARK *lmp, *lmt; 166 167 if (key == ABSMARK2) 168 key = ABSMARK1; 169 170 /* 171 * The rules are simple. If the user is setting a mark (if it's a 172 * new mark this is always true), it always happens. If not, it's 173 * an undo, and we set it if it's not already set or if it was set 174 * by a previous undo. 175 */ 176 lmp = mark_find(sp, key); 177 if (lmp == NULL || lmp->name != key) { 178 MALLOC_RET(sp, lmt, LMARK *, sizeof(LMARK)); 179 if (lmp == NULL) { 180 SLIST_INSERT_HEAD(sp->ep->marks, lmt, q); 181 } else 182 SLIST_INSERT_AFTER(lmp, lmt, q); 183 lmp = lmt; 184 } else if (!userset && 185 !F_ISSET(lmp, MARK_DELETED) && F_ISSET(lmp, MARK_USERSET)) 186 return (0); 187 188 lmp->lno = value->lno; 189 lmp->cno = value->cno; 190 lmp->name = key; 191 lmp->flags = userset ? MARK_USERSET : 0; 192 return (0); 193 } 194 195 /* 196 * mark_find -- 197 * Find the requested mark, or, the slot immediately before 198 * where it would go. 199 */ 200 static LMARK * 201 mark_find( 202 SCR *sp, 203 ARG_CHAR_T key) 204 { 205 LMARK *lmp, *lastlmp = NULL; 206 207 /* 208 * Return the requested mark or the slot immediately before 209 * where it should go. 210 */ 211 SLIST_FOREACH(lmp, sp->ep->marks, q) { 212 if (lmp->name >= key) 213 return (lmp->name == key ? lmp : lastlmp); 214 lastlmp = lmp; 215 } 216 return (lastlmp); 217 } 218 219 /* 220 * mark_insdel -- 221 * Update the marks based on an insertion or deletion. 222 * 223 * PUBLIC: int mark_insdel __P((SCR *, lnop_t, recno_t)); 224 */ 225 int 226 mark_insdel( 227 SCR *sp, 228 lnop_t op, 229 recno_t lno) 230 { 231 LMARK *lmp; 232 recno_t lline; 233 234 switch (op) { 235 case LINE_APPEND: 236 /* All insert/append operations are done as inserts. */ 237 abort(); 238 case LINE_DELETE: 239 SLIST_FOREACH(lmp, sp->ep->marks, q) 240 if (lmp->lno >= lno) 241 if (lmp->lno == lno) { 242 F_SET(lmp, MARK_DELETED); 243 (void)log_mark(sp, lmp); 244 } else 245 --lmp->lno; 246 break; 247 case LINE_INSERT: 248 /* 249 * XXX 250 * Very nasty special case. If the file was empty, then we're 251 * adding the first line, which is a replacement. So, we don't 252 * modify the marks. This is a hack to make: 253 * 254 * mz:r!echo foo<carriage-return>'z 255 * 256 * work, i.e. historically you could mark the "line" in an empty 257 * file and replace it, and continue to use the mark. Insane, 258 * well, yes, I know, but someone complained. 259 * 260 * Check for line #2 before going to the end of the file. 261 */ 262 if (!db_exist(sp, 2)) { 263 if (db_last(sp, &lline)) 264 return (1); 265 if (lline == 1) 266 return (0); 267 } 268 269 SLIST_FOREACH(lmp, sp->ep->marks, q) 270 if (lmp->lno >= lno) 271 ++lmp->lno; 272 break; 273 case LINE_RESET: 274 break; 275 } 276 return (0); 277 } 278