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