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