convert screen queue and parent/child queue to <sys/queue.h> queues
[nvi.git] / common / db.c
blobdc352a918a07b88daa9fd83d357c8f693b71d299
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.16 1993/11/18 13:50:22 bostic Exp $ (Berkeley) $Date: 1993/11/18 13:50:22 $";
10 #endif /* not lint */
12 #include <sys/types.h>
14 #include <errno.h>
15 #include <string.h>
17 #include "vi.h"
19 static inline int scr_update __P((SCR *, EXF *, recno_t, enum operation, int));
22 * file_gline --
23 * Look in the text buffers for a line; if it's not there
24 * call file_rline to retrieve it from the database.
26 char *
27 file_gline(sp, ep, lno, lenp)
28 SCR *sp;
29 EXF *ep;
30 recno_t lno; /* Line number. */
31 size_t *lenp; /* Length store. */
33 TEXT *tp;
36 * The underlying recno stuff handles zero by returning NULL, but
37 * have to have an oob condition for the look-aside into the input
38 * buffer anyway.
40 if (lno == 0)
41 return (NULL);
44 * Look-aside into the TEXT buffers and see if the line we want
45 * is there.
47 if (F_ISSET(sp, S_INPUT) &&
48 ((TEXT *)sp->tiq.cqh_first)->lno <= lno &&
49 ((TEXT *)sp->tiq.cqh_last)->lno >= lno) {
50 for (tp = sp->tiq.cqh_first;
51 tp->lno != lno; tp = tp->q.cqe_next);
52 if (lenp)
53 *lenp = tp->len;
54 return (tp->lb);
56 return (file_rline(sp, ep, lno, lenp));
60 * file_rline --
61 * Look in the cache for a line; if it's not there retrieve
62 * it from the file.
64 char *
65 file_rline(sp, ep, lno, lenp)
66 SCR *sp;
67 EXF *ep;
68 recno_t lno; /* Line number. */
69 size_t *lenp; /* Length store. */
71 DBT data, key;
73 /* Check the cache. */
74 if (lno == ep->c_lno) {
75 if (lenp)
76 *lenp = ep->c_len;
77 return (ep->c_lp);
79 ep->c_lno = OOBLNO;
81 /* Get the line from the underlying database. */
82 key.data = &lno;
83 key.size = sizeof(lno);
84 switch (ep->db->get(ep->db, &key, &data, 0)) {
85 case -1:
86 msgq(sp, M_ERR,
87 "Error: %s/%d: unable to get line %u: %s.",
88 tail(__FILE__), __LINE__, lno, strerror(errno));
89 /* FALLTHROUGH */
90 case 1:
91 return (NULL);
92 /* NOTREACHED */
94 if (lenp)
95 *lenp = data.size;
97 /* Fill the cache. */
98 ep->c_lno = lno;
99 ep->c_len = data.size;
100 ep->c_lp = data.data;
102 return (data.data);
106 * file_dline --
107 * Delete a line from the file.
110 file_dline(sp, ep, lno)
111 SCR *sp;
112 EXF *ep;
113 recno_t lno;
115 DBT key;
117 #if defined(DEBUG) && 0
118 TRACE(sp, "delete line %lu\n", lno);
119 #endif
120 /* Delete any marks in the line. */
121 mark_delete(sp, ep, lno);
123 /* Log change. */
124 log_line(sp, ep, lno, LOG_LINE_DELETE);
126 /* Update file. */
127 key.data = &lno;
128 key.size = sizeof(lno);
129 if (ep->db->del(ep->db, &key, 0) == 1) {
130 msgq(sp, M_ERR,
131 "Error: %s/%d: unable to delete line %u: %s.",
132 tail(__FILE__), __LINE__, lno, strerror(errno));
133 return (1);
136 /* Flush the cache, update line count, before screen update. */
137 if (lno <= ep->c_lno)
138 ep->c_lno = OOBLNO;
139 if (ep->c_nlines != OOBLNO)
140 --ep->c_nlines;
142 /* File now dirty. */
143 if (F_ISSET(ep, F_FIRSTMODIFY))
144 (void)rcv_init(sp, ep);
145 F_SET(ep, F_MODIFIED);
147 /* Update screen. */
148 return (scr_update(sp, ep, lno, LINE_DELETE, 1));
152 * file_aline --
153 * Append a line into the file.
156 file_aline(sp, ep, update, lno, p, len)
157 SCR *sp;
158 EXF *ep;
159 int update;
160 recno_t lno;
161 char *p;
162 size_t len;
164 DBT data, key;
165 recno_t lline;
167 #if defined(DEBUG) && 0
168 TRACE(sp, "append to %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
169 #endif
171 * Very nasty special case. The historic vi code displays a single
172 * space (or a '$' if the list option is set) for the first line in
173 * an "empty" file. If we "insert" a line, that line gets scrolled
174 * down, not repainted, so it's incorrect when we refresh the the
175 * screen. This is really hard to find and fix in the vi code -- the
176 * text input functions detect it explicitly and don't insert a new
177 * line. The hack here is to repaint the screen if we're appending
178 * to an empty file.
180 if (lno == 0) {
181 if (file_lline(sp, ep, &lline))
182 return (1);
183 if (lline == 0)
184 F_SET(sp, S_REDRAW);
187 /* Update file. */
188 key.data = &lno;
189 key.size = sizeof(lno);
190 data.data = p;
191 data.size = len;
192 if (ep->db->put(ep->db, &key, &data, R_IAFTER) == -1) {
193 msgq(sp, M_ERR,
194 "Error: %s/%d: unable to append to line %u: %s.",
195 tail(__FILE__), __LINE__, lno, strerror(errno));
196 return (1);
199 /* Flush the cache, update line count, before screen update. */
200 if (lno < ep->c_lno)
201 ep->c_lno = OOBLNO;
202 if (ep->c_nlines != OOBLNO)
203 ++ep->c_nlines;
205 /* File now dirty. */
206 if (F_ISSET(ep, F_FIRSTMODIFY))
207 (void)rcv_init(sp, ep);
208 F_SET(ep, F_MODIFIED);
210 /* Log change. */
211 log_line(sp, ep, lno + 1, LOG_LINE_APPEND);
213 /* Push any marks above the line. */
214 mark_insert(sp, ep, lno + 1);
217 * Update screen.
219 * XXX
220 * Nasty hack. If multiple lines are input by the user, they aren't
221 * committed until an <ESC> is entered. The problem is the screen was
222 * updated/scrolled as each line was entered. So, when this routine
223 * is called to copy the new lines from the cut buffer into the file,
224 * it has to know not to update the screen again.
226 return (scr_update(sp, ep, lno, LINE_APPEND, update));
230 * file_iline --
231 * Insert a line into the file.
234 file_iline(sp, ep, lno, p, len)
235 SCR *sp;
236 EXF *ep;
237 recno_t lno;
238 char *p;
239 size_t len;
241 DBT data, key;
242 recno_t lline;
244 #if defined(DEBUG) && 0
245 TRACE(sp,
246 "insert before %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
247 #endif
249 /* Very nasty special case. See comment in file_aline(). */
250 if (lno == 1) {
251 if (file_lline(sp, ep, &lline))
252 return (1);
253 if (lline == 0)
254 F_SET(sp, S_REDRAW);
257 /* Update file. */
258 key.data = &lno;
259 key.size = sizeof(lno);
260 data.data = p;
261 data.size = len;
262 if (ep->db->put(ep->db, &key, &data, R_IBEFORE) == -1) {
263 msgq(sp, M_ERR,
264 "Error: %s/%d: unable to insert at line %u: %s.",
265 tail(__FILE__), __LINE__, lno, strerror(errno));
266 return (1);
269 /* Flush the cache, update line count, before screen update. */
270 if (lno >= ep->c_lno)
271 ep->c_lno = OOBLNO;
272 if (ep->c_nlines != OOBLNO)
273 ++ep->c_nlines;
275 /* File now dirty. */
276 if (F_ISSET(ep, F_FIRSTMODIFY))
277 (void)rcv_init(sp, ep);
278 F_SET(ep, F_MODIFIED);
280 /* Log change. */
281 log_line(sp, ep, lno, LOG_LINE_INSERT);
283 /* Push any marks above the line. */
284 mark_insert(sp, ep, lno);
286 /* Update screen. */
287 return (scr_update(sp, ep, lno, LINE_INSERT, 1));
291 * file_sline --
292 * Store a line in the file.
295 file_sline(sp, ep, lno, p, len)
296 SCR *sp;
297 EXF *ep;
298 recno_t lno;
299 char *p;
300 size_t len;
302 DBT data, key;
304 #if defined(DEBUG) && 0
305 TRACE(sp,
306 "replace line %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
307 #endif
308 /* Log before change. */
309 log_line(sp, ep, lno, LOG_LINE_RESET_B);
311 /* Update file. */
312 key.data = &lno;
313 key.size = sizeof(lno);
314 data.data = p;
315 data.size = len;
316 if (ep->db->put(ep->db, &key, &data, 0) == -1) {
317 msgq(sp, M_ERR,
318 "Error: %s/%d: unable to store line %u: %s.",
319 tail(__FILE__), __LINE__, lno, strerror(errno));
320 return (1);
323 /* Flush the cache, before logging or screen update. */
324 if (lno == ep->c_lno)
325 ep->c_lno = OOBLNO;
327 /* File now dirty. */
328 if (F_ISSET(ep, F_FIRSTMODIFY))
329 (void)rcv_init(sp, ep);
330 F_SET(ep, F_MODIFIED);
332 /* Log after change. */
333 log_line(sp, ep, lno, LOG_LINE_RESET_F);
335 /* Update screen. */
336 return (scr_update(sp, ep, lno, LINE_RESET, 1));
340 * file_lline --
341 * Return the number of lines in the file.
344 file_lline(sp, ep, lnop)
345 SCR *sp;
346 EXF *ep;
347 recno_t *lnop;
349 DBT data, key;
350 recno_t lno;
352 /* Check the cache. */
353 if (ep->c_nlines != OOBLNO) {
354 *lnop = (F_ISSET(sp, S_INPUT) &&
355 ((TEXT *)sp->tiq.cqh_last)->lno > ep->c_nlines ?
356 ((TEXT *)sp->tiq.cqh_last)->lno : ep->c_nlines);
357 return (0);
360 key.data = &lno;
361 key.size = sizeof(lno);
363 switch (ep->db->seq(ep->db, &key, &data, R_LAST)) {
364 case -1:
365 msgq(sp, M_ERR,
366 "Error: %s/%d: unable to get last line: %s.",
367 tail(__FILE__), __LINE__, strerror(errno));
368 *lnop = 0;
369 return (1);
370 case 1:
371 lno = 0;
372 break;
373 default:
374 memmove(&lno, key.data, sizeof(lno));
375 break;
378 /* Fill the cache. */
379 ep->c_nlines = ep->c_lno = lno;
380 ep->c_len = data.size;
381 ep->c_lp = data.data;
383 *lnop = (F_ISSET(sp, S_INPUT) &&
384 ((TEXT *)sp->tiq.cqh_last)->lno > lno ?
385 ((TEXT *)sp->tiq.cqh_last)->lno : lno);
386 return (0);
390 * scr_update --
391 * Update all of the screens that are backed by the file that
392 * just changed.
394 static inline int
395 scr_update(sp, ep, lno, op, current)
396 SCR *sp;
397 EXF *ep;
398 recno_t lno;
399 enum operation op;
400 int current;
402 SCR *tsp;
404 if (ep->refcnt != 1) {
405 for (tsp = sp;;) {
406 if ((tsp = tsp->q.cqe_prev) == (void *)&sp->gp->dq)
407 break;
408 if (tsp->ep == ep)
409 (void)sp->s_change(tsp, ep, lno, op);
411 for (tsp = sp;;) {
412 if ((tsp = tsp->q.cqe_next) == (void *)&sp->gp->dq)
413 break;
414 if (tsp->ep == ep)
415 (void)sp->s_change(tsp, ep, lno, op);
418 return (current && sp->s_change(sp, ep, lno, op));