119304Speter/*- 219304Speter * Copyright (c) 1992, 1993, 1994 319304Speter * The Regents of the University of California. All rights reserved. 419304Speter * Copyright (c) 1992, 1993, 1994, 1995, 1996 519304Speter * Keith Bostic. All rights reserved. 619304Speter * 719304Speter * See the LICENSE file for redistribution information. 819304Speter */ 919304Speter 1019304Speter#include "config.h" 1119304Speter 1219304Speter#ifndef lint 13254225Speterstatic const char sccsid[] = "$Id: mark.c,v 10.14 2011/07/04 14:42:58 zy Exp $"; 1419304Speter#endif /* not lint */ 1519304Speter 1619304Speter#include <sys/types.h> 1719304Speter#include <sys/queue.h> 18254225Speter#include <sys/time.h> 1919304Speter 2019304Speter#include <bitstring.h> 2119304Speter#include <errno.h> 2219304Speter#include <limits.h> 2319304Speter#include <stdio.h> 2419304Speter#include <stdlib.h> 2519304Speter#include <string.h> 2619304Speter 2719304Speter#include "common.h" 2819304Speter 2919304Speterstatic LMARK *mark_find __P((SCR *, ARG_CHAR_T)); 3019304Speter 3119304Speter/* 32254225Speter * Marks are maintained in a key sorted singly linked list. We can't 3319304Speter * use arrays because we have no idea how big an index key could be. 3419304Speter * The underlying assumption is that users don't have more than, say, 3519304Speter * 10 marks at any one time, so this will be is fast enough. 3619304Speter * 3719304Speter * Marks are fixed, and modifications to the line don't update the mark's 3819304Speter * position in the line. This can be hard. If you add text to the line, 3919304Speter * place a mark in that text, undo the addition and use ` to move to the 4019304Speter * mark, the location will have disappeared. It's tempting to try to adjust 4119304Speter * the mark with the changes in the line, but this is hard to do, especially 4219304Speter * if we've given the line to v_ntext.c:v_ntext() for editing. Historic vi 4319304Speter * would move to the first non-blank on the line when the mark location was 4419304Speter * past the end of the line. This can be complicated by deleting to a mark 4519304Speter * that has disappeared using the ` command. Historic vi treated this as 4619304Speter * a line-mode motion and deleted the line. This implementation complains to 4719304Speter * the user. 4819304Speter * 4919304Speter * In historic vi, marks returned if the operation was undone, unless the 5019304Speter * mark had been subsequently reset. Tricky. This is hard to start with, 5119304Speter * but in the presence of repeated undo it gets nasty. When a line is 5219304Speter * deleted, we delete (and log) any marks on that line. An undo will create 5319304Speter * the mark. Any mark creations are noted as to whether the user created 5419304Speter * it or if it was created by an undo. The former cannot be reset by another 5519304Speter * undo, but the latter may. 5619304Speter * 5719304Speter * All of these routines translate ABSMARK2 to ABSMARK1. Setting either of 5819304Speter * the absolute mark locations sets both, so that "m'" and "m`" work like 5919304Speter * they, ah, for lack of a better word, "should". 6019304Speter */ 6119304Speter 6219304Speter/* 6319304Speter * mark_init -- 6419304Speter * Set up the marks. 6519304Speter * 6619304Speter * PUBLIC: int mark_init __P((SCR *, EXF *)); 6719304Speter */ 6819304Speterint 69254225Spetermark_init( 70254225Speter SCR *sp, 71254225Speter EXF *ep) 7219304Speter{ 7319304Speter /* 7419304Speter * !!! 7519304Speter * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER. 7619304Speter * 7719304Speter * Set up the marks. 7819304Speter */ 79254225Speter SLIST_INIT(ep->marks); 8019304Speter return (0); 8119304Speter} 8219304Speter 8319304Speter/* 8419304Speter * mark_end -- 8519304Speter * Free up the marks. 8619304Speter * 8719304Speter * PUBLIC: int mark_end __P((SCR *, EXF *)); 8819304Speter */ 8919304Speterint 90254225Spetermark_end( 91254225Speter SCR *sp, 92254225Speter EXF *ep) 9319304Speter{ 9419304Speter LMARK *lmp; 9519304Speter 9619304Speter /* 9719304Speter * !!! 9819304Speter * ep MAY NOT BE THE SAME AS sp->ep, DON'T USE THE LATTER. 9919304Speter */ 100254225Speter while ((lmp = SLIST_FIRST(ep->marks)) != NULL) { 101254225Speter SLIST_REMOVE_HEAD(ep->marks, q); 10219304Speter free(lmp); 10319304Speter } 10419304Speter return (0); 10519304Speter} 10619304Speter 10719304Speter/* 10819304Speter * mark_get -- 10919304Speter * Get the location referenced by a mark. 11019304Speter * 11119304Speter * PUBLIC: int mark_get __P((SCR *, ARG_CHAR_T, MARK *, mtype_t)); 11219304Speter */ 11319304Speterint 114254225Spetermark_get( 115254225Speter SCR *sp, 116254225Speter ARG_CHAR_T key, 117254225Speter MARK *mp, 118254225Speter mtype_t mtype) 11919304Speter{ 12019304Speter LMARK *lmp; 12119304Speter 12219304Speter if (key == ABSMARK2) 12319304Speter key = ABSMARK1; 12419304Speter 12519304Speter lmp = mark_find(sp, key); 12619304Speter if (lmp == NULL || lmp->name != key) { 12719304Speter msgq(sp, mtype, "017|Mark %s: not set", KEY_NAME(sp, key)); 12819304Speter return (1); 12919304Speter } 13019304Speter if (F_ISSET(lmp, MARK_DELETED)) { 13119304Speter msgq(sp, mtype, 13219304Speter "018|Mark %s: the line was deleted", KEY_NAME(sp, key)); 13319304Speter return (1); 13419304Speter } 13519304Speter 13619304Speter /* 13719304Speter * !!! 13819304Speter * The absolute mark is initialized to lno 1/cno 0, and historically 13919304Speter * you could use it in an empty file. Make such a mark always work. 14019304Speter */ 14119304Speter if ((lmp->lno != 1 || lmp->cno != 0) && !db_exist(sp, lmp->lno)) { 14219304Speter msgq(sp, mtype, 14319304Speter "019|Mark %s: cursor position no longer exists", 14419304Speter KEY_NAME(sp, key)); 14519304Speter return (1); 14619304Speter } 14719304Speter mp->lno = lmp->lno; 14819304Speter mp->cno = lmp->cno; 14919304Speter return (0); 15019304Speter} 15119304Speter 15219304Speter/* 15319304Speter * mark_set -- 15419304Speter * Set the location referenced by a mark. 15519304Speter * 15619304Speter * PUBLIC: int mark_set __P((SCR *, ARG_CHAR_T, MARK *, int)); 15719304Speter */ 15819304Speterint 159254225Spetermark_set( 160254225Speter SCR *sp, 161254225Speter ARG_CHAR_T key, 162254225Speter MARK *value, 163254225Speter int userset) 16419304Speter{ 16519304Speter LMARK *lmp, *lmt; 16619304Speter 16719304Speter if (key == ABSMARK2) 16819304Speter key = ABSMARK1; 16919304Speter 17019304Speter /* 17119304Speter * The rules are simple. If the user is setting a mark (if it's a 17219304Speter * new mark this is always true), it always happens. If not, it's 17319304Speter * an undo, and we set it if it's not already set or if it was set 17419304Speter * by a previous undo. 17519304Speter */ 17619304Speter lmp = mark_find(sp, key); 17719304Speter if (lmp == NULL || lmp->name != key) { 17819304Speter MALLOC_RET(sp, lmt, LMARK *, sizeof(LMARK)); 17919304Speter if (lmp == NULL) { 180254225Speter SLIST_INSERT_HEAD(sp->ep->marks, lmt, q); 18119304Speter } else 182254225Speter SLIST_INSERT_AFTER(lmp, lmt, q); 18319304Speter lmp = lmt; 18419304Speter } else if (!userset && 18519304Speter !F_ISSET(lmp, MARK_DELETED) && F_ISSET(lmp, MARK_USERSET)) 18619304Speter return (0); 18719304Speter 18819304Speter lmp->lno = value->lno; 18919304Speter lmp->cno = value->cno; 19019304Speter lmp->name = key; 19119304Speter lmp->flags = userset ? MARK_USERSET : 0; 19219304Speter return (0); 19319304Speter} 19419304Speter 19519304Speter/* 19619304Speter * mark_find -- 19719304Speter * Find the requested mark, or, the slot immediately before 19819304Speter * where it would go. 19919304Speter */ 20019304Speterstatic LMARK * 201254225Spetermark_find( 202254225Speter SCR *sp, 203254225Speter ARG_CHAR_T key) 20419304Speter{ 205254225Speter LMARK *lmp, *lastlmp = NULL; 20619304Speter 20719304Speter /* 20819304Speter * Return the requested mark or the slot immediately before 20919304Speter * where it should go. 21019304Speter */ 211254225Speter SLIST_FOREACH(lmp, sp->ep->marks, q) { 21219304Speter if (lmp->name >= key) 21319304Speter return (lmp->name == key ? lmp : lastlmp); 214254225Speter lastlmp = lmp; 215254225Speter } 21619304Speter return (lastlmp); 21719304Speter} 21819304Speter 21919304Speter/* 22019304Speter * mark_insdel -- 22119304Speter * Update the marks based on an insertion or deletion. 22219304Speter * 22319304Speter * PUBLIC: int mark_insdel __P((SCR *, lnop_t, recno_t)); 22419304Speter */ 22519304Speterint 226254225Spetermark_insdel( 227254225Speter SCR *sp, 228254225Speter lnop_t op, 229254225Speter recno_t lno) 23019304Speter{ 23119304Speter LMARK *lmp; 23219304Speter recno_t lline; 23319304Speter 23419304Speter switch (op) { 23519304Speter case LINE_APPEND: 23619304Speter /* All insert/append operations are done as inserts. */ 23719304Speter abort(); 23819304Speter case LINE_DELETE: 239254225Speter SLIST_FOREACH(lmp, sp->ep->marks, q) 24019304Speter if (lmp->lno >= lno) 24119304Speter if (lmp->lno == lno) { 24219304Speter F_SET(lmp, MARK_DELETED); 24319304Speter (void)log_mark(sp, lmp); 24419304Speter } else 24519304Speter --lmp->lno; 24619304Speter break; 24719304Speter case LINE_INSERT: 24819304Speter /* 24919304Speter * XXX 25019304Speter * Very nasty special case. If the file was empty, then we're 25119304Speter * adding the first line, which is a replacement. So, we don't 25219304Speter * modify the marks. This is a hack to make: 25319304Speter * 25419304Speter * mz:r!echo foo<carriage-return>'z 25519304Speter * 25619304Speter * work, i.e. historically you could mark the "line" in an empty 25719304Speter * file and replace it, and continue to use the mark. Insane, 25819304Speter * well, yes, I know, but someone complained. 25919304Speter * 26019304Speter * Check for line #2 before going to the end of the file. 26119304Speter */ 26219304Speter if (!db_exist(sp, 2)) { 26319304Speter if (db_last(sp, &lline)) 26419304Speter return (1); 26519304Speter if (lline == 1) 26619304Speter return (0); 26719304Speter } 26819304Speter 269254225Speter SLIST_FOREACH(lmp, sp->ep->marks, q) 27019304Speter if (lmp->lno >= lno) 27119304Speter ++lmp->lno; 27219304Speter break; 27319304Speter case LINE_RESET: 27419304Speter break; 27519304Speter } 27619304Speter return (0); 27719304Speter} 278