home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Spezial / SPEZIAL2_97.zip / SPEZIAL2_97.iso / ANWEND / EDITOR / NVI179B / NVI179B.ZIP / common / line.c < prev    next >
C/C++ Source or Header  |  1996-09-15  |  12KB  |  577 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[] = "@(#)line.c    10.21 (Berkeley) 9/15/96";
  14. #endif /* not lint */
  15.  
  16. #include <sys/types.h>
  17. #include <sys/queue.h>
  18. #include <sys/time.h>
  19.  
  20. #include <bitstring.h>
  21. #include <errno.h>
  22. #include <limits.h>
  23. #include <stdio.h>
  24. #include <string.h>
  25.  
  26. #include "common.h"
  27. #include "../vi/vi.h"
  28.  
  29. static int scr_update __P((SCR *, recno_t, lnop_t, int));
  30.  
  31. /*
  32.  * db_eget --
  33.  *    Front-end to db_get, special case handling for empty files.
  34.  *
  35.  * PUBLIC: int db_eget __P((SCR *, recno_t, char **, size_t *, int *));
  36.  */
  37. int
  38. db_eget(sp, lno, pp, lenp, isemptyp)
  39.     SCR *sp;
  40.     recno_t lno;                /* Line number. */
  41.     char **pp;                /* Pointer store. */
  42.     size_t *lenp;                /* Length store. */
  43.     int *isemptyp;
  44. {
  45.     recno_t l1;
  46.  
  47.     if (isemptyp != NULL)
  48.         *isemptyp = 0;
  49.  
  50.     /* If the line exists, simply return it. */
  51.     if (!db_get(sp, lno, 0, pp, lenp))
  52.         return (0);
  53.  
  54.     /*
  55.      * If the user asked for line 0 or line 1, i.e. the only possible
  56.      * line in an empty file, find the last line of the file; db_last
  57.      * fails loudly.
  58.      */
  59.     if ((lno == 0 || lno == 1) && db_last(sp, &l1))
  60.         return (1);
  61.  
  62.     /* If the file isn't empty, fail loudly. */
  63.     if (lno != 0 && lno != 1 || l1 != 0) {
  64.         db_err(sp, lno);
  65.         return (1);
  66.     }
  67.  
  68.     if (isemptyp != NULL)
  69.         *isemptyp = 1;
  70.  
  71.     return (1);
  72. }
  73.  
  74. /*
  75.  * db_get --
  76.  *    Look in the text buffers for a line, followed by the cache, followed
  77.  *    by the database.
  78.  *
  79.  * PUBLIC: int db_get __P((SCR *, recno_t, u_int32_t, char **, size_t *));
  80.  */
  81. int
  82. db_get(sp, lno, flags, pp, lenp)
  83.     SCR *sp;
  84.     recno_t lno;                /* Line number. */
  85.     u_int32_t flags;
  86.     char **pp;                /* Pointer store. */
  87.     size_t *lenp;                /* Length store. */
  88. {
  89.     DBT data, key;
  90.     EXF *ep;
  91.     TEXT *tp;
  92.     recno_t l1, l2;
  93.  
  94.     /*
  95.      * The underlying recno stuff handles zero by returning NULL, but
  96.      * have to have an OOB condition for the look-aside into the input
  97.      * buffer anyway.
  98.      */
  99.     if (lno == 0)
  100.         goto err1;
  101.  
  102.     /* Check for no underlying file. */
  103.     if ((ep = sp->ep) == NULL) {
  104.         ex_emsg(sp, NULL, EXM_NOFILEYET);
  105.         goto err3;
  106.     }
  107.  
  108.     if (LF_ISSET(DBG_NOCACHE))
  109.         goto nocache;
  110.  
  111.     /*
  112.      * Look-aside into the TEXT buffers and see if the line we want
  113.      * is there.
  114.      */
  115.     if (F_ISSET(sp, SC_TINPUT)) {
  116.         l1 = ((TEXT *)sp->tiq.cqh_first)->lno;
  117.         l2 = ((TEXT *)sp->tiq.cqh_last)->lno;
  118.         if (l1 <= lno && l2 >= lno) {
  119. #if defined(DEBUG) && 0
  120.     TRACE(sp, "retrieve TEXT buffer line %lu\n", (u_long)lno);
  121. #endif
  122.             for (tp = sp->tiq.cqh_first;
  123.                 tp->lno != lno; tp = tp->q.cqe_next);
  124.             if (lenp != NULL)
  125.                 *lenp = tp->len;
  126.             if (pp != NULL)
  127.                 *pp = tp->lb;
  128.             return (0);
  129.         }
  130.         /*
  131.          * Adjust the line number for the number of lines used
  132.          * by the text input buffers.
  133.          */
  134.         if (lno > l2)
  135.             lno -= l2 - l1;
  136.     }
  137.  
  138.     /* Look-aside into the cache, and see if the line we want is there. */
  139.     if (lno == ep->c_lno) {
  140. #if defined(DEBUG) && 0
  141.     TRACE(sp, "retrieve cached line %lu\n", (u_long)lno);
  142. #endif
  143.         if (lenp != NULL)
  144.             *lenp = ep->c_len;
  145.         if (pp != NULL)
  146.             *pp = ep->c_lp;
  147.         return (0);
  148.     }
  149.     ep->c_lno = OOBLNO;
  150.  
  151. nocache:
  152.     /* Get the line from the underlying database. */
  153.     key.data = &lno;
  154.     key.size = sizeof(lno);
  155.     switch (ep->db->get(ep->db, &key, &data, 0)) {
  156.         case -1:
  157.         goto err2;
  158.     case 1:
  159. err1:        if (LF_ISSET(DBG_FATAL))
  160. err2:            db_err(sp, lno);
  161. err3:        if (lenp != NULL)
  162.             *lenp = 0;
  163.         if (pp != NULL)
  164.             *pp = NULL;
  165.         return (1);
  166.     }
  167.  
  168.     /* Reset the cache. */
  169.     ep->c_lno = lno;
  170.     ep->c_len = data.size;
  171.     ep->c_lp = data.data;
  172.  
  173. #if defined(DEBUG) && 0
  174.     TRACE(sp, "retrieve DB line %lu\n", (u_long)lno);
  175. #endif
  176.     if (lenp != NULL)
  177.         *lenp = data.size;
  178.     if (pp != NULL)
  179.         *pp = ep->c_lp;
  180.     return (0);
  181. }
  182.  
  183. /*
  184.  * db_delete --
  185.  *    Delete a line from the file.
  186.  *
  187.  * PUBLIC: int db_delete __P((SCR *, recno_t));
  188.  */
  189. int
  190. db_delete(sp, lno)
  191.     SCR *sp;
  192.     recno_t lno;
  193. {
  194.     DBT key;
  195.     EXF *ep;
  196.  
  197. #if defined(DEBUG) && 0
  198.     TRACE(sp, "delete line %lu\n", (u_long)lno);
  199. #endif
  200.     /* Check for no underlying file. */
  201.     if ((ep = sp->ep) == NULL) {
  202.         ex_emsg(sp, NULL, EXM_NOFILEYET);
  203.         return (1);
  204.     }
  205.         
  206.     /* Update marks, @ and global commands. */
  207.     if (mark_insdel(sp, LINE_DELETE, lno))
  208.         return (1);
  209.     if (ex_g_insdel(sp, LINE_DELETE, lno))
  210.         return (1);
  211.  
  212.     /* Log change. */
  213.     log_line(sp, lno, LOG_LINE_DELETE);
  214.  
  215.     /* Update file. */
  216.     key.data = &lno;
  217.     key.size = sizeof(lno);
  218.     SIGBLOCK;
  219.     if (ep->db->del(ep->db, &key, 0) == 1) {
  220.         msgq(sp, M_SYSERR,
  221.             "003|unable to delete line %lu", (u_long)lno);
  222.         return (1);
  223.     }
  224.     SIGUNBLOCK;
  225.  
  226.     /* Flush the cache, update line count, before screen update. */
  227.     if (lno <= ep->c_lno)
  228.         ep->c_lno = OOBLNO;
  229.     if (ep->c_nlines != OOBLNO)
  230.         --ep->c_nlines;
  231.  
  232.     /* File now modified. */
  233.     if (F_ISSET(ep, F_FIRSTMODIFY))
  234.         (void)rcv_init(sp);
  235.     F_SET(ep, F_MODIFIED);
  236.  
  237.     /* Update screen. */
  238.     return (scr_update(sp, lno, LINE_DELETE, 1));
  239. }
  240.  
  241. /*
  242.  * db_append --
  243.  *    Append a line into the file.
  244.  *
  245.  * PUBLIC: int db_append __P((SCR *, int, recno_t, char *, size_t));
  246.  */
  247. int
  248. db_append(sp, update, lno, p, len)
  249.     SCR *sp;
  250.     int update;
  251.     recno_t lno;
  252.     char *p;
  253.     size_t len;
  254. {
  255.     DBT data, key;
  256.     EXF *ep;
  257.     int rval;
  258.  
  259. #if defined(DEBUG) && 0
  260.     TRACE(sp, "append to %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
  261. #endif
  262.     /* Check for no underlying file. */
  263.     if ((ep = sp->ep) == NULL) {
  264.         ex_emsg(sp, NULL, EXM_NOFILEYET);
  265.         return (1);
  266.     }
  267.         
  268.     /* Update file. */
  269.     key.data = &lno;
  270.     key.size = sizeof(lno);
  271.     data.data = p;
  272.     data.size = len;
  273.     SIGBLOCK;
  274.     if (ep->db->put(ep->db, &key, &data, R_IAFTER) == -1) {
  275.         msgq(sp, M_SYSERR,
  276.             "004|unable to append to line %lu", (u_long)lno);
  277.         return (1);
  278.     }
  279.     SIGUNBLOCK;
  280.  
  281.     /* Flush the cache, update line count, before screen update. */
  282.     if (lno < ep->c_lno)
  283.         ep->c_lno = OOBLNO;
  284.     if (ep->c_nlines != OOBLNO)
  285.         ++ep->c_nlines;
  286.  
  287.     /* File now dirty. */
  288.     if (F_ISSET(ep, F_FIRSTMODIFY))
  289.         (void)rcv_init(sp);
  290.     F_SET(ep, F_MODIFIED);
  291.  
  292.     /* Log change. */
  293.     log_line(sp, lno + 1, LOG_LINE_APPEND);
  294.  
  295.     /* Update marks, @ and global commands. */
  296.     rval = 0;
  297.     if (mark_insdel(sp, LINE_INSERT, lno + 1))
  298.         rval = 1;
  299.     if (ex_g_insdel(sp, LINE_INSERT, lno + 1))
  300.         rval = 1;
  301.  
  302.     /*
  303.      * Update screen.
  304.      *
  305.      * XXX
  306.      * Nasty hack.  If multiple lines are input by the user, they aren't
  307.      * committed until an <ESC> is entered.  The problem is the screen was
  308.      * updated/scrolled as each line was entered.  So, when this routine
  309.      * is called to copy the new lines from the cut buffer into the file,
  310.      * it has to know not to update the screen again.
  311.      */
  312.     return (scr_update(sp, lno, LINE_APPEND, update) || rval);
  313. }
  314.  
  315. /*
  316.  * db_insert --
  317.  *    Insert a line into the file.
  318.  *
  319.  * PUBLIC: int db_insert __P((SCR *, recno_t, char *, size_t));
  320.  */
  321. int
  322. db_insert(sp, lno, p, len)
  323.     SCR *sp;
  324.     recno_t lno;
  325.     char *p;
  326.     size_t len;
  327. {
  328.     DBT data, key;
  329.     EXF *ep;
  330.     int rval;
  331.  
  332. #if defined(DEBUG) && 0
  333.     TRACE(sp, "insert before %lu: len %lu {%.*s}\n",
  334.         (u_long)lno, (u_long)len, MIN(len, 20), p);
  335. #endif
  336.     /* Check for no underlying file. */
  337.     if ((ep = sp->ep) == NULL) {
  338.         ex_emsg(sp, NULL, EXM_NOFILEYET);
  339.         return (1);
  340.     }
  341.         
  342.     /* Update file. */
  343.     key.data = &lno;
  344.     key.size = sizeof(lno);
  345.     data.data = p;
  346.     data.size = len;
  347.     SIGBLOCK;
  348.     if (ep->db->put(ep->db, &key, &data, R_IBEFORE) == -1) {
  349.         msgq(sp, M_SYSERR,
  350.             "005|unable to insert at line %lu", (u_long)lno);
  351.         return (1);
  352.     }
  353.     SIGUNBLOCK;
  354.  
  355.     /* Flush the cache, update line count, before screen update. */
  356.     if (lno >= ep->c_lno)
  357.         ep->c_lno = OOBLNO;
  358.     if (ep->c_nlines != OOBLNO)
  359.         ++ep->c_nlines;
  360.  
  361.     /* File now dirty. */
  362.     if (F_ISSET(ep, F_FIRSTMODIFY))
  363.         (void)rcv_init(sp);
  364.     F_SET(ep, F_MODIFIED);
  365.  
  366.     /* Log change. */
  367.     log_line(sp, lno, LOG_LINE_INSERT);
  368.  
  369.     /* Update marks, @ and global commands. */
  370.     rval = 0;
  371.     if (mark_insdel(sp, LINE_INSERT, lno))
  372.         rval = 1;
  373.     if (ex_g_insdel(sp, LINE_INSERT, lno))
  374.         rval = 1;
  375.  
  376.     /* Update screen. */
  377.     return (scr_update(sp, lno, LINE_INSERT, 1) || rval);
  378. }
  379.  
  380. /*
  381.  * db_set --
  382.  *    Store a line in the file.
  383.  *
  384.  * PUBLIC: int db_set __P((SCR *, recno_t, char *, size_t));
  385.  */
  386. int
  387. db_set(sp, lno, p, len)
  388.     SCR *sp;
  389.     recno_t lno;
  390.     char *p;
  391.     size_t len;
  392. {
  393.     DBT data, key;
  394.     EXF *ep;
  395.  
  396. #if defined(DEBUG) && 0
  397.     TRACE(sp, "replace line %lu: len %lu {%.*s}\n",
  398.         (u_long)lno, (u_long)len, MIN(len, 20), p);
  399. #endif
  400.  
  401.     /* Check for no underlying file. */
  402.     if ((ep = sp->ep) == NULL) {
  403.         ex_emsg(sp, NULL, EXM_NOFILEYET);
  404.         return (1);
  405.     }
  406.         
  407.     /* Log before change. */
  408.     log_line(sp, lno, LOG_LINE_RESET_B);
  409.  
  410.     /* Update file. */
  411.     key.data = &lno;
  412.     key.size = sizeof(lno);
  413.     data.data = p;
  414.     data.size = len;
  415.     SIGBLOCK;
  416.     if (ep->db->put(ep->db, &key, &data, 0) == -1) {
  417.         msgq(sp, M_SYSERR,
  418.             "006|unable to store line %lu", (u_long)lno);
  419.         return (1);
  420.     }
  421.     SIGUNBLOCK;
  422.  
  423.     /* Flush the cache, before logging or screen update. */
  424.     if (lno == ep->c_lno)
  425.         ep->c_lno = OOBLNO;
  426.  
  427.     /* File now dirty. */
  428.     if (F_ISSET(ep, F_FIRSTMODIFY))
  429.         (void)rcv_init(sp);
  430.     F_SET(ep, F_MODIFIED);
  431.  
  432.     /* Log after change. */
  433.     log_line(sp, lno, LOG_LINE_RESET_F);
  434.  
  435.     /* Update screen. */
  436.     return (scr_update(sp, lno, LINE_RESET, 1));
  437. }
  438.  
  439. /*
  440.  * db_exist --
  441.  *    Return if a line exists.
  442.  *
  443.  * PUBLIC: int db_exist __P((SCR *, recno_t));
  444.  */
  445. int
  446. db_exist(sp, lno)
  447.     SCR *sp;
  448.     recno_t lno;
  449. {
  450.     EXF *ep;
  451.  
  452.     /* Check for no underlying file. */
  453.     if ((ep = sp->ep) == NULL) {
  454.         ex_emsg(sp, NULL, EXM_NOFILEYET);
  455.         return (1);
  456.     }
  457.  
  458.     if (lno == OOBLNO)
  459.         return (0);
  460.         
  461.     /*
  462.      * Check the last-line number cache.  Adjust the cached line
  463.      * number for the lines used by the text input buffers.
  464.      */
  465.     if (ep->c_nlines != OOBLNO)
  466.         return (lno <= (F_ISSET(sp, SC_TINPUT) ?
  467.             ep->c_nlines + (((TEXT *)sp->tiq.cqh_last)->lno -
  468.             ((TEXT *)sp->tiq.cqh_first)->lno) : ep->c_nlines));
  469.  
  470.     /* Go get the line. */
  471.     return (!db_get(sp, lno, 0, NULL, NULL));
  472. }
  473.  
  474. /*
  475.  * db_last --
  476.  *    Return the number of lines in the file.
  477.  *
  478.  * PUBLIC: int db_last __P((SCR *, recno_t *));
  479.  */
  480. int
  481. db_last(sp, lnop)
  482.     SCR *sp;
  483.     recno_t *lnop;
  484. {
  485.     DBT data, key;
  486.     EXF *ep;
  487.     recno_t lno;
  488.  
  489.     /* Check for no underlying file. */
  490.     if ((ep = sp->ep) == NULL) {
  491.         ex_emsg(sp, NULL, EXM_NOFILEYET);
  492.         return (1);
  493.     }
  494.         
  495.     /*
  496.      * Check the last-line number cache.  Adjust the cached line
  497.      * number for the lines used by the text input buffers.
  498.      */
  499.     if (ep->c_nlines != OOBLNO) {
  500.         *lnop = ep->c_nlines;
  501.         if (F_ISSET(sp, SC_TINPUT))
  502.             *lnop += ((TEXT *)sp->tiq.cqh_last)->lno -
  503.                 ((TEXT *)sp->tiq.cqh_first)->lno;
  504.         return (0);
  505.     }
  506.  
  507.     key.data = &lno;
  508.     key.size = sizeof(lno);
  509.  
  510.     switch (ep->db->seq(ep->db, &key, &data, R_LAST)) {
  511.         case -1:
  512.         msgq(sp, M_SYSERR, "007|unable to get last line");
  513.         *lnop = 0;
  514.         return (1);
  515.         case 1:
  516.         *lnop = 0;
  517.         return (0);
  518.     default:
  519.         break;
  520.     }
  521.  
  522.     /* Fill the cache. */
  523.     memcpy(&lno, key.data, sizeof(lno));
  524.     ep->c_nlines = ep->c_lno = lno;
  525.     ep->c_len = data.size;
  526.     ep->c_lp = data.data;
  527.  
  528.     /* Return the value. */
  529.     *lnop = (F_ISSET(sp, SC_TINPUT) &&
  530.         ((TEXT *)sp->tiq.cqh_last)->lno > lno ?
  531.         ((TEXT *)sp->tiq.cqh_last)->lno : lno);
  532.     return (0);
  533. }
  534.  
  535. /*
  536.  * db_err --
  537.  *    Report a line error.
  538.  *
  539.  * PUBLIC: void db_err __P((SCR *, recno_t));
  540.  */
  541. void
  542. db_err(sp, lno)
  543.     SCR *sp;
  544.     recno_t lno;
  545. {
  546.     msgq(sp, M_ERR,
  547.         "008|Error: unable to retrieve line %lu", (u_long)lno);
  548. }
  549.  
  550. /*
  551.  * scr_update --
  552.  *    Update all of the screens that are backed by the file that
  553.  *    just changed.
  554.  */
  555. static int
  556. scr_update(sp, lno, op, current)
  557.     SCR *sp;
  558.     recno_t lno;
  559.     lnop_t op;
  560.     int current;
  561. {
  562.     EXF *ep;
  563.     SCR *tsp;
  564.  
  565.     if (F_ISSET(sp, SC_EX))
  566.         return (0);
  567.  
  568.     ep = sp->ep;
  569.     if (ep->refcnt != 1)
  570.         for (tsp = sp->gp->dq.cqh_first;
  571.             tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next)
  572.             if (sp != tsp && tsp->ep == ep)
  573.                 if (vs_change(tsp, lno, op))
  574.                     return (1);
  575.     return (current ? vs_change(sp, lno, op) : 0);
  576. }
  577.