home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / EDITOR / NVI179B / NVI179B.ZIP / common / mark.c < prev    next >
C/C++ Source or Header  |  1996-08-10  |  7KB  |  278 lines

  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.