BSD 4_4 release
[unix-history] / usr / src / contrib / nvi / nvi / line.c
CommitLineData
ad787160
C
1/*-
2 * Copyright (c) 1992, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static char sccsid[] = "@(#)line.c 8.1 (Berkeley) 6/9/93";
36#endif /* not lint */
37
38#include <sys/types.h>
39
40#include <errno.h>
41#include <string.h>
42
43#include "vi.h"
44#include "recover.h"
45
46/*
47 * UPDATE_SCREENS --
48 * Macro to walk the screens and update all of them that are backed
49 * by the file that just changed.
50 */
51#define UPDATE_SCREENS(op) { \
52 if (ep->refcnt == 1) { \
53 if (sp->s_change != NULL) \
54 sp->s_change(sp, ep, lno, op); \
55 } else { \
56 SCR *__tsp; \
57 for (__tsp = sp->gp->scrhdr.next; \
58 __tsp != (SCR *)&sp->gp->scrhdr; \
59 __tsp = __tsp->next) \
60 if (__tsp->ep == ep && \
61 __tsp->s_change != NULL) { \
62 sp->s_change(__tsp, ep, lno, op); \
63 sp->s_refresh(__tsp, ep); \
64 } \
65 } \
66}
67
68/*
69 * file_gline --
70 * Look in the text buffers for a line; if it's not there
71 * call file_rline to retrieve it from the database.
72 */
73char *
74file_gline(sp, ep, lno, lenp)
75 SCR *sp;
76 EXF *ep;
77 recno_t lno; /* Line number. */
78 size_t *lenp; /* Length store. */
79{
80 TEXT *tp;
81
82 /*
83 * The underlying recno stuff handles zero by returning NULL, but
84 * have to have an oob condition for the look-aside into the input
85 * buffer anyway.
86 */
87 if (lno == 0)
88 return (NULL);
89
90 /*
91 * Look-aside into the TEXT buffers and see if the line we want
92 * is there.
93 */
94 if (F_ISSET(&sp->txthdr, HDR_INUSE) &&
95 ((TEXT *)sp->txthdr.next)->lno <= lno &&
96 ((TEXT *)sp->txthdr.prev)->lno >= lno) {
97 for (tp = sp->txthdr.next; tp->lno != lno; tp = tp->next);
98 if (lenp)
99 *lenp = tp->len;
100 return (tp->lb);
101 }
102 if (F_ISSET(&sp->bhdr, HDR_INUSE) &&
103 ((TEXT *)sp->bhdr.next)->lno <= lno &&
104 ((TEXT *)sp->bhdr.prev)->lno >= lno) {
105 for (tp = sp->bhdr.next; tp->lno != lno; tp = tp->next);
106 if (lenp)
107 *lenp = tp->len;
108 return (tp->lb);
109 }
110 return (file_rline(sp, ep, lno, lenp));
111}
112
113/*
114 * file_rline --
115 * Look in the cache for a line; if it's not there retrieve
116 * it from the file.
117 */
118char *
119file_rline(sp, ep, lno, lenp)
120 SCR *sp;
121 EXF *ep;
122 recno_t lno; /* Line number. */
123 size_t *lenp; /* Length store. */
124{
125 DBT data, key;
126
127 /* Check the cache. */
128 if (lno == ep->c_lno) {
129 if (lenp)
130 *lenp = ep->c_len;
131 return (ep->c_lp);
132 }
133 ep->c_lno = OOBLNO;
134
135 /* Get the line from the underlying database. */
136 key.data = &lno;
137 key.size = sizeof(lno);
138 switch (ep->db->get(ep->db, &key, &data, 0)) {
139 case -1:
140 msgq(sp, M_ERR,
141 "Error: %s/%d: unable to get line %u: %s.",
142 tail(__FILE__), __LINE__, lno, strerror(errno));
143 /* FALLTHROUGH */
144 case 1:
145 return (NULL);
146 /* NOTREACHED */
147 }
148 if (lenp)
149 *lenp = data.size;
150
151 /* Fill the cache. */
152 ep->c_lno = lno;
153 ep->c_len = data.size;
154 ep->c_lp = data.data;
155
156 return (data.data);
157}
158
159/*
160 * file_dline --
161 * Delete a line from the file.
162 */
163int
164file_dline(sp, ep, lno)
165 SCR *sp;
166 EXF *ep;
167 recno_t lno;
168{
169 DBT key;
170
171#if DEBUG && 0
172 TRACE(sp, "delete line %lu\n", lno);
173#endif
174 /* Log change. */
175 log_line(sp, ep, lno, LOG_LINE_DELETE);
176
177 /* Update file. */
178 key.data = &lno;
179 key.size = sizeof(lno);
180 if (ep->db->del(ep->db, &key, 0) == 1) {
181 msgq(sp, M_ERR,
182 "Error: %s/%d: unable to delete line %u: %s.",
183 tail(__FILE__), __LINE__, lno, strerror(errno));
184 return (1);
185 }
186
187 /* Flush the cache, update line count, before screen update. */
188 if (lno <= ep->c_lno)
189 ep->c_lno = OOBLNO;
190 if (ep->c_nlines != OOBLNO)
191 --ep->c_nlines;
192
193 /* File now dirty. */
194 if (F_ISSET(ep, F_FIRSTMODIFY))
195 (void)rcv_init(sp, ep);
196 F_SET(ep, F_MODIFIED);
197
198 /* Update screen. */
199 UPDATE_SCREENS(LINE_DELETE);
200 return (0);
201}
202
203/*
204 * file_aline --
205 * Append a line into the file.
206 */
207int
208file_aline(sp, ep, update, lno, p, len)
209 SCR *sp;
210 EXF *ep;
211 int update;
212 recno_t lno;
213 char *p;
214 size_t len;
215{
216 DBT data, key;
217
218#if DEBUG && 0
219 TRACE(sp, "append to %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
220#endif
221 /* Update file. */
222 key.data = &lno;
223 key.size = sizeof(lno);
224 data.data = p;
225 data.size = len;
226 if (ep->db->put(ep->db, &key, &data, R_IAFTER) == -1) {
227 msgq(sp, M_ERR,
228 "Error: %s/%d: unable to append to line %u: %s.",
229 tail(__FILE__), __LINE__, lno, strerror(errno));
230 return (1);
231 }
232
233 /* Flush the cache, update line count, before screen update. */
234 if (lno < ep->c_lno)
235 ep->c_lno = OOBLNO;
236 if (ep->c_nlines != OOBLNO)
237 ++ep->c_nlines;
238
239 /* File now dirty. */
240 if (F_ISSET(ep, F_FIRSTMODIFY))
241 (void)rcv_init(sp, ep);
242 F_SET(ep, F_MODIFIED);
243
244 /* Log change. */
245 log_line(sp, ep, lno + 1, LOG_LINE_APPEND);
246
247 /*
248 * Update screen.
249 *
250 * XXX
251 * Nasty hack. If multiple lines are input by the user, they aren't
252 * committed until an <ESC> is entered. The problem is the screen was
253 * updated/scrolled as each line was entered. So, when this routine
254 * is called to copy the new lines from the cut buffer into the file,
255 * it has to know not to update the screen again.
256 */
257 if (update)
258 UPDATE_SCREENS(LINE_APPEND);
259 return (0);
260}
261
262/*
263 * file_iline --
264 * Insert a line into the file.
265 */
266int
267file_iline(sp, ep, lno, p, len)
268 SCR *sp;
269 EXF *ep;
270 recno_t lno;
271 char *p;
272 size_t len;
273{
274 DBT data, key;
275
276#if DEBUG && 0
277 TRACE(sp,
278 "insert before %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
279#endif
280 /* Update file. */
281 key.data = &lno;
282 key.size = sizeof(lno);
283 data.data = p;
284 data.size = len;
285 if (ep->db->put(ep->db, &key, &data, R_IBEFORE) == -1) {
286 msgq(sp, M_ERR,
287 "Error: %s/%d: unable to insert at line %u: %s.",
288 tail(__FILE__), __LINE__, lno, strerror(errno));
289 return (1);
290 }
291
292 /* Flush the cache, update line count, before screen update. */
293 if (lno >= ep->c_lno)
294 ep->c_lno = OOBLNO;
295 if (ep->c_nlines != OOBLNO)
296 ++ep->c_nlines;
297
298 /* File now dirty. */
299 if (F_ISSET(ep, F_FIRSTMODIFY))
300 (void)rcv_init(sp, ep);
301 F_SET(ep, F_MODIFIED);
302
303 /* Log change. */
304 log_line(sp, ep, lno, LOG_LINE_INSERT);
305
306 /* Update screen. */
307 UPDATE_SCREENS(LINE_INSERT);
308 return (0);
309}
310
311/*
312 * file_sline --
313 * Store a line in the file.
314 */
315int
316file_sline(sp, ep, lno, p, len)
317 SCR *sp;
318 EXF *ep;
319 recno_t lno;
320 char *p;
321 size_t len;
322{
323 DBT data, key;
324
325#if DEBUG && 0
326 TRACE(sp,
327 "replace line %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
328#endif
329 /* Log before change. */
330 log_line(sp, ep, lno, LOG_LINE_RESET_B);
331
332 /* Update file. */
333 key.data = &lno;
334 key.size = sizeof(lno);
335 data.data = p;
336 data.size = len;
337 if (ep->db->put(ep->db, &key, &data, 0) == -1) {
338 msgq(sp, M_ERR,
339 "Error: %s/%d: unable to store line %u: %s.",
340 tail(__FILE__), __LINE__, lno, strerror(errno));
341 return (1);
342 }
343
344 /* Flush the cache, before logging or screen update. */
345 if (lno == ep->c_lno)
346 ep->c_lno = OOBLNO;
347
348 /* File now dirty. */
349 if (F_ISSET(ep, F_FIRSTMODIFY))
350 (void)rcv_init(sp, ep);
351 F_SET(ep, F_MODIFIED);
352
353 /* Log after change. */
354 log_line(sp, ep, lno, LOG_LINE_RESET_F);
355
356 /* Update screen. */
357 UPDATE_SCREENS(LINE_RESET);
358 return (0);
359}
360
361/*
362 * file_lline --
363 * Return the number of lines in the file.
364 */
365int
366file_lline(sp, ep, lnop)
367 SCR *sp;
368 EXF *ep;
369 recno_t *lnop;
370{
371 DBT data, key;
372 recno_t lno;
373
374 /* Check the cache. */
375 if (ep->c_nlines != OOBLNO) {
376 *lnop = (F_ISSET(sp, S_INPUT) &&
377 ((TEXT *)sp->txthdr.prev)->lno > ep->c_nlines ?
378 ((TEXT *)sp->txthdr.prev)->lno : ep->c_nlines);
379 return (0);
380 }
381
382 key.data = &lno;
383 key.size = sizeof(lno);
384
385 switch (ep->db->seq(ep->db, &key, &data, R_LAST)) {
386 case -1:
387 msgq(sp, M_ERR,
388 "Error: %s/%d: unable to get last line: %s.",
389 tail(__FILE__), __LINE__, strerror(errno));
390 *lnop = 0;
391 return (1);
392 case 1:
393 lno = 0;
394 break;
395 default:
396 memmove(&lno, key.data, sizeof(lno));
397 break;
398 }
399
400 /* Fill the cache. */
401 ep->c_nlines = ep->c_lno = lno;
402 ep->c_len = data.size;
403 ep->c_lp = data.data;
404
405 *lnop = (F_ISSET(sp, S_INPUT) &&
406 ((TEXT *)sp->txthdr.prev)->lno > lno ?
407 ((TEXT *)sp->txthdr.prev)->lno : lno);
408 return (0);
409}