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