source: trunk/third/nvi/common/line.c @ 14302

Revision 14302, 11.5 KB checked in by ghudson, 25 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r14301, which included commits to RCS files with non-trunk default branches.
Line 
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
13static 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
29static 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 */
37int
38db_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 */
81int
82db_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
151nocache:
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:
159err1:           if (LF_ISSET(DBG_FATAL))
160err2:                   db_err(sp, lno);
161err3:           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 */
189int
190db_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 */
247int
248db_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 */
321int
322db_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 */
386int
387db_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 */
445int
446db_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 */
480int
481db_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 */
541void
542db_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 */
555static int
556scr_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}
Note: See TracBrowser for help on using the repository browser.