use pathnames.h, it now includes enough
[nvi.git] / common / db.c
blobf55623a0c424c393d7e8bb339c660263437ea0c8
1 /*-
2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
5 * %sccs.include.redist.c%
6 */
8 #ifndef lint
9 static char sccsid[] = "$Id: db.c,v 8.20 1993/12/29 16:11:01 bostic Exp $ (Berkeley) $Date: 1993/12/29 16:11:01 $";
10 #endif /* not lint */
12 #include <sys/types.h>
14 #include <errno.h>
15 #include <string.h>
17 #include "vi.h"
18 #include "excmd.h"
20 static inline int scr_update __P((SCR *, EXF *, recno_t, enum operation, int));
23 * file_gline --
24 * Look in the text buffers for a line; if it's not there
25 * call file_rline to retrieve it from the database.
27 char *
28 file_gline(sp, ep, lno, lenp)
29 SCR *sp;
30 EXF *ep;
31 recno_t lno; /* Line number. */
32 size_t *lenp; /* Length store. */
34 TEXT *tp;
35 recno_t l1, l2;
38 * The underlying recno stuff handles zero by returning NULL, but
39 * have to have an oob condition for the look-aside into the input
40 * buffer anyway.
42 if (lno == 0)
43 return (NULL);
46 * Look-aside into the TEXT buffers and see if the line we want
47 * is there.
49 if (F_ISSET(sp, S_INPUT)) {
50 l1 = ((TEXT *)sp->tiq.cqh_first)->lno;
51 l2 = ((TEXT *)sp->tiq.cqh_last)->lno;
52 if (l1 <= lno && l2 >= lno) {
53 for (tp = sp->tiq.cqh_first;
54 tp->lno != lno; tp = tp->q.cqe_next);
55 if (lenp)
56 *lenp = tp->len;
57 return (tp->lb);
60 * Adjust the line number for the number of lines used
61 * by the text input buffers.
63 if (lno > l2)
64 lno -= l2 - l1;
66 return (file_rline(sp, ep, lno, lenp));
70 * file_rline --
71 * Look in the cache for a line; if it's not there retrieve
72 * it from the file.
74 char *
75 file_rline(sp, ep, lno, lenp)
76 SCR *sp;
77 EXF *ep;
78 recno_t lno; /* Line number. */
79 size_t *lenp; /* Length store. */
81 DBT data, key;
83 /* Check the cache. */
84 if (lno == ep->c_lno) {
85 if (lenp)
86 *lenp = ep->c_len;
87 return (ep->c_lp);
89 ep->c_lno = OOBLNO;
91 /* Get the line from the underlying database. */
92 key.data = &lno;
93 key.size = sizeof(lno);
94 switch (ep->db->get(ep->db, &key, &data, 0)) {
95 case -1:
96 msgq(sp, M_ERR,
97 "Error: %s/%d: unable to get line %u: %s.",
98 tail(__FILE__), __LINE__, lno, strerror(errno));
99 /* FALLTHROUGH */
100 case 1:
101 return (NULL);
102 /* NOTREACHED */
104 if (lenp)
105 *lenp = data.size;
107 /* Fill the cache. */
108 ep->c_lno = lno;
109 ep->c_len = data.size;
110 ep->c_lp = data.data;
112 return (data.data);
116 * file_dline --
117 * Delete a line from the file.
120 file_dline(sp, ep, lno)
121 SCR *sp;
122 EXF *ep;
123 recno_t lno;
125 DBT key;
127 #if defined(DEBUG) && 0
128 TRACE(sp, "delete line %lu\n", lno);
129 #endif
131 * XXX
133 * Marks and global commands have to know when lines are
134 * inserted or deleted.
136 mark_insdel(sp, ep, LINE_DELETE, lno);
137 global_insdel(sp, ep, LINE_DELETE, lno);
139 /* Log change. */
140 log_line(sp, ep, lno, LOG_LINE_DELETE);
142 /* Update file. */
143 key.data = &lno;
144 key.size = sizeof(lno);
145 if (ep->db->del(ep->db, &key, 0) == 1) {
146 msgq(sp, M_ERR,
147 "Error: %s/%d: unable to delete line %u: %s.",
148 tail(__FILE__), __LINE__, lno, strerror(errno));
149 return (1);
152 /* Flush the cache, update line count, before screen update. */
153 if (lno <= ep->c_lno)
154 ep->c_lno = OOBLNO;
155 if (ep->c_nlines != OOBLNO)
156 --ep->c_nlines;
158 /* File now dirty. */
159 if (F_ISSET(ep, F_FIRSTMODIFY))
160 (void)rcv_init(sp, ep);
161 F_SET(ep, F_MODIFIED);
163 /* Update screen. */
164 return (scr_update(sp, ep, lno, LINE_DELETE, 1));
168 * file_aline --
169 * Append a line into the file.
172 file_aline(sp, ep, update, lno, p, len)
173 SCR *sp;
174 EXF *ep;
175 int update;
176 recno_t lno;
177 char *p;
178 size_t len;
180 DBT data, key;
181 recno_t lline;
183 #if defined(DEBUG) && 0
184 TRACE(sp, "append to %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
185 #endif
187 * Very nasty special case. The historic vi code displays a single
188 * space (or a '$' if the list option is set) for the first line in
189 * an "empty" file. If we "insert" a line, that line gets scrolled
190 * down, not repainted, so it's incorrect when we refresh the the
191 * screen. This is really hard to find and fix in the vi code -- the
192 * text input functions detect it explicitly and don't insert a new
193 * line. The hack here is to repaint the screen if we're appending
194 * to an empty file.
196 if (lno == 0) {
197 if (file_lline(sp, ep, &lline))
198 return (1);
199 if (lline == 0)
200 F_SET(sp, S_REDRAW);
203 /* Update file. */
204 key.data = &lno;
205 key.size = sizeof(lno);
206 data.data = p;
207 data.size = len;
208 if (ep->db->put(ep->db, &key, &data, R_IAFTER) == -1) {
209 msgq(sp, M_ERR,
210 "Error: %s/%d: unable to append to line %u: %s.",
211 tail(__FILE__), __LINE__, lno, strerror(errno));
212 return (1);
215 /* Flush the cache, update line count, before screen update. */
216 if (lno < ep->c_lno)
217 ep->c_lno = OOBLNO;
218 if (ep->c_nlines != OOBLNO)
219 ++ep->c_nlines;
221 /* File now dirty. */
222 if (F_ISSET(ep, F_FIRSTMODIFY))
223 (void)rcv_init(sp, ep);
224 F_SET(ep, F_MODIFIED);
226 /* Log change. */
227 log_line(sp, ep, lno + 1, LOG_LINE_APPEND);
230 * XXX
232 * Marks and global commands have to know when lines are
233 * inserted or deleted.
235 mark_insdel(sp, ep, LINE_INSERT, lno + 1);
236 global_insdel(sp, ep, LINE_INSERT, lno + 1);
239 * Update screen.
241 * XXX
242 * Nasty hack. If multiple lines are input by the user, they aren't
243 * committed until an <ESC> is entered. The problem is the screen was
244 * updated/scrolled as each line was entered. So, when this routine
245 * is called to copy the new lines from the cut buffer into the file,
246 * it has to know not to update the screen again.
248 return (scr_update(sp, ep, lno, LINE_APPEND, update));
252 * file_iline --
253 * Insert a line into the file.
256 file_iline(sp, ep, lno, p, len)
257 SCR *sp;
258 EXF *ep;
259 recno_t lno;
260 char *p;
261 size_t len;
263 DBT data, key;
264 recno_t lline;
266 #if defined(DEBUG) && 0
267 TRACE(sp,
268 "insert before %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
269 #endif
271 /* Very nasty special case. See comment in file_aline(). */
272 if (lno == 1) {
273 if (file_lline(sp, ep, &lline))
274 return (1);
275 if (lline == 0)
276 F_SET(sp, S_REDRAW);
279 /* Update file. */
280 key.data = &lno;
281 key.size = sizeof(lno);
282 data.data = p;
283 data.size = len;
284 if (ep->db->put(ep->db, &key, &data, R_IBEFORE) == -1) {
285 msgq(sp, M_ERR,
286 "Error: %s/%d: unable to insert at line %u: %s.",
287 tail(__FILE__), __LINE__, lno, strerror(errno));
288 return (1);
291 /* Flush the cache, update line count, before screen update. */
292 if (lno >= ep->c_lno)
293 ep->c_lno = OOBLNO;
294 if (ep->c_nlines != OOBLNO)
295 ++ep->c_nlines;
297 /* File now dirty. */
298 if (F_ISSET(ep, F_FIRSTMODIFY))
299 (void)rcv_init(sp, ep);
300 F_SET(ep, F_MODIFIED);
302 /* Log change. */
303 log_line(sp, ep, lno, LOG_LINE_INSERT);
306 * XXX
308 * Marks and global commands have to know when lines are
309 * inserted or deleted.
311 mark_insdel(sp, ep, LINE_INSERT, lno);
312 global_insdel(sp, ep, LINE_INSERT, lno);
314 /* Update screen. */
315 return (scr_update(sp, ep, lno, LINE_INSERT, 1));
319 * file_sline --
320 * Store a line in the file.
323 file_sline(sp, ep, lno, p, len)
324 SCR *sp;
325 EXF *ep;
326 recno_t lno;
327 char *p;
328 size_t len;
330 DBT data, key;
332 #if defined(DEBUG) && 0
333 TRACE(sp,
334 "replace line %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
335 #endif
336 /* Log before change. */
337 log_line(sp, ep, lno, LOG_LINE_RESET_B);
339 /* Update file. */
340 key.data = &lno;
341 key.size = sizeof(lno);
342 data.data = p;
343 data.size = len;
344 if (ep->db->put(ep->db, &key, &data, 0) == -1) {
345 msgq(sp, M_ERR,
346 "Error: %s/%d: unable to store line %u: %s.",
347 tail(__FILE__), __LINE__, lno, strerror(errno));
348 return (1);
351 /* Flush the cache, before logging or screen update. */
352 if (lno == ep->c_lno)
353 ep->c_lno = OOBLNO;
355 /* File now dirty. */
356 if (F_ISSET(ep, F_FIRSTMODIFY))
357 (void)rcv_init(sp, ep);
358 F_SET(ep, F_MODIFIED);
360 /* Log after change. */
361 log_line(sp, ep, lno, LOG_LINE_RESET_F);
363 /* Update screen. */
364 return (scr_update(sp, ep, lno, LINE_RESET, 1));
368 * file_lline --
369 * Return the number of lines in the file.
372 file_lline(sp, ep, lnop)
373 SCR *sp;
374 EXF *ep;
375 recno_t *lnop;
377 DBT data, key;
378 recno_t lno;
380 /* Check the cache. */
381 if (ep->c_nlines != OOBLNO) {
382 *lnop = (F_ISSET(sp, S_INPUT) &&
383 ((TEXT *)sp->tiq.cqh_last)->lno > ep->c_nlines ?
384 ((TEXT *)sp->tiq.cqh_last)->lno : ep->c_nlines);
385 return (0);
388 key.data = &lno;
389 key.size = sizeof(lno);
391 switch (ep->db->seq(ep->db, &key, &data, R_LAST)) {
392 case -1:
393 msgq(sp, M_ERR,
394 "Error: %s/%d: unable to get last line: %s.",
395 tail(__FILE__), __LINE__, strerror(errno));
396 *lnop = 0;
397 return (1);
398 case 1:
399 lno = 0;
400 break;
401 default:
402 memmove(&lno, key.data, sizeof(lno));
403 break;
406 /* Fill the cache. */
407 ep->c_nlines = ep->c_lno = lno;
408 ep->c_len = data.size;
409 ep->c_lp = data.data;
411 *lnop = (F_ISSET(sp, S_INPUT) &&
412 ((TEXT *)sp->tiq.cqh_last)->lno > lno ?
413 ((TEXT *)sp->tiq.cqh_last)->lno : lno);
414 return (0);
418 * scr_update --
419 * Update all of the screens that are backed by the file that
420 * just changed.
422 static inline int
423 scr_update(sp, ep, lno, op, current)
424 SCR *sp;
425 EXF *ep;
426 recno_t lno;
427 enum operation op;
428 int current;
430 SCR *tsp;
432 if (ep->refcnt != 1)
433 for (tsp = sp->gp->dq.cqh_first;
434 tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next)
435 if (sp != tsp && tsp->ep == ep)
436 (void)sp->s_change(tsp, ep, lno, op);
437 return (current && sp->s_change(sp, ep, lno, op));