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