This commit was generated by cvs2svn to track changes on a CVS vendor
[unix-history] / usr.bin / vi / line.c
CommitLineData
1e64b3ba
JH
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.20 (Berkeley) 12/29/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 "excmd.h"
45
46static inline int scr_update __P((SCR *, EXF *, recno_t, enum operation, int));
47
48/*
49 * file_gline --
50 * Look in the text buffers for a line; if it's not there
51 * call file_rline to retrieve it from the database.
52 */
53char *
54file_gline(sp, ep, lno, lenp)
55 SCR *sp;
56 EXF *ep;
57 recno_t lno; /* Line number. */
58 size_t *lenp; /* Length store. */
59{
60 TEXT *tp;
61 recno_t l1, l2;
62
63 /*
64 * The underlying recno stuff handles zero by returning NULL, but
65 * have to have an oob condition for the look-aside into the input
66 * buffer anyway.
67 */
68 if (lno == 0)
69 return (NULL);
70
71 /*
72 * Look-aside into the TEXT buffers and see if the line we want
73 * is there.
74 */
75 if (F_ISSET(sp, S_INPUT)) {
76 l1 = ((TEXT *)sp->tiq.cqh_first)->lno;
77 l2 = ((TEXT *)sp->tiq.cqh_last)->lno;
78 if (l1 <= lno && l2 >= lno) {
79 for (tp = sp->tiq.cqh_first;
80 tp->lno != lno; tp = tp->q.cqe_next);
81 if (lenp)
82 *lenp = tp->len;
83 return (tp->lb);
84 }
85 /*
86 * Adjust the line number for the number of lines used
87 * by the text input buffers.
88 */
89 if (lno > l2)
90 lno -= l2 - l1;
91 }
92 return (file_rline(sp, ep, lno, lenp));
93}
94
95/*
96 * file_rline --
97 * Look in the cache for a line; if it's not there retrieve
98 * it from the file.
99 */
100char *
101file_rline(sp, ep, lno, lenp)
102 SCR *sp;
103 EXF *ep;
104 recno_t lno; /* Line number. */
105 size_t *lenp; /* Length store. */
106{
107 DBT data, key;
108
109 /* Check the cache. */
110 if (lno == ep->c_lno) {
111 if (lenp)
112 *lenp = ep->c_len;
113 return (ep->c_lp);
114 }
115 ep->c_lno = OOBLNO;
116
117 /* Get the line from the underlying database. */
118 key.data = &lno;
119 key.size = sizeof(lno);
120 switch (ep->db->get(ep->db, &key, &data, 0)) {
121 case -1:
122 msgq(sp, M_ERR,
123 "Error: %s/%d: unable to get line %u: %s.",
124 tail(__FILE__), __LINE__, lno, strerror(errno));
125 /* FALLTHROUGH */
126 case 1:
127 return (NULL);
128 /* NOTREACHED */
129 }
130 if (lenp)
131 *lenp = data.size;
132
133 /* Fill the cache. */
134 ep->c_lno = lno;
135 ep->c_len = data.size;
136 ep->c_lp = data.data;
137
138 return (data.data);
139}
140
141/*
142 * file_dline --
143 * Delete a line from the file.
144 */
145int
146file_dline(sp, ep, lno)
147 SCR *sp;
148 EXF *ep;
149 recno_t lno;
150{
151 DBT key;
152
153#if defined(DEBUG) && 0
154 TRACE(sp, "delete line %lu\n", lno);
155#endif
156 /*
157 * XXX
158 *
159 * Marks and global commands have to know when lines are
160 * inserted or deleted.
161 */
162 mark_insdel(sp, ep, LINE_DELETE, lno);
163 global_insdel(sp, ep, LINE_DELETE, lno);
164
165 /* Log change. */
166 log_line(sp, ep, lno, LOG_LINE_DELETE);
167
168 /* Update file. */
169 key.data = &lno;
170 key.size = sizeof(lno);
171 if (ep->db->del(ep->db, &key, 0) == 1) {
172 msgq(sp, M_ERR,
173 "Error: %s/%d: unable to delete line %u: %s.",
174 tail(__FILE__), __LINE__, lno, strerror(errno));
175 return (1);
176 }
177
178 /* Flush the cache, update line count, before screen update. */
179 if (lno <= ep->c_lno)
180 ep->c_lno = OOBLNO;
181 if (ep->c_nlines != OOBLNO)
182 --ep->c_nlines;
183
184 /* File now dirty. */
185 if (F_ISSET(ep, F_FIRSTMODIFY))
186 (void)rcv_init(sp, ep);
187 F_SET(ep, F_MODIFIED);
188
189 /* Update screen. */
190 return (scr_update(sp, ep, lno, LINE_DELETE, 1));
191}
192
193/*
194 * file_aline --
195 * Append a line into the file.
196 */
197int
198file_aline(sp, ep, update, lno, p, len)
199 SCR *sp;
200 EXF *ep;
201 int update;
202 recno_t lno;
203 char *p;
204 size_t len;
205{
206 DBT data, key;
207 recno_t lline;
208
209#if defined(DEBUG) && 0
210 TRACE(sp, "append to %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
211#endif
212 /*
213 * Very nasty special case. The historic vi code displays a single
214 * space (or a '$' if the list option is set) for the first line in
215 * an "empty" file. If we "insert" a line, that line gets scrolled
216 * down, not repainted, so it's incorrect when we refresh the the
217 * screen. This is really hard to find and fix in the vi code -- the
218 * text input functions detect it explicitly and don't insert a new
219 * line. The hack here is to repaint the screen if we're appending
220 * to an empty file.
221 */
222 if (lno == 0) {
223 if (file_lline(sp, ep, &lline))
224 return (1);
225 if (lline == 0)
226 F_SET(sp, S_REDRAW);
227 }
228
229 /* Update file. */
230 key.data = &lno;
231 key.size = sizeof(lno);
232 data.data = p;
233 data.size = len;
234 if (ep->db->put(ep->db, &key, &data, R_IAFTER) == -1) {
235 msgq(sp, M_ERR,
236 "Error: %s/%d: unable to append to line %u: %s.",
237 tail(__FILE__), __LINE__, lno, strerror(errno));
238 return (1);
239 }
240
241 /* Flush the cache, update line count, before screen update. */
242 if (lno < ep->c_lno)
243 ep->c_lno = OOBLNO;
244 if (ep->c_nlines != OOBLNO)
245 ++ep->c_nlines;
246
247 /* File now dirty. */
248 if (F_ISSET(ep, F_FIRSTMODIFY))
249 (void)rcv_init(sp, ep);
250 F_SET(ep, F_MODIFIED);
251
252 /* Log change. */
253 log_line(sp, ep, lno + 1, LOG_LINE_APPEND);
254
255 /*
256 * XXX
257 *
258 * Marks and global commands have to know when lines are
259 * inserted or deleted.
260 */
261 mark_insdel(sp, ep, LINE_INSERT, lno + 1);
262 global_insdel(sp, ep, LINE_INSERT, lno + 1);
263
264 /*
265 * Update screen.
266 *
267 * XXX
268 * Nasty hack. If multiple lines are input by the user, they aren't
269 * committed until an <ESC> is entered. The problem is the screen was
270 * updated/scrolled as each line was entered. So, when this routine
271 * is called to copy the new lines from the cut buffer into the file,
272 * it has to know not to update the screen again.
273 */
274 return (scr_update(sp, ep, lno, LINE_APPEND, update));
275}
276
277/*
278 * file_iline --
279 * Insert a line into the file.
280 */
281int
282file_iline(sp, ep, lno, p, len)
283 SCR *sp;
284 EXF *ep;
285 recno_t lno;
286 char *p;
287 size_t len;
288{
289 DBT data, key;
290 recno_t lline;
291
292#if defined(DEBUG) && 0
293 TRACE(sp,
294 "insert before %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
295#endif
296
297 /* Very nasty special case. See comment in file_aline(). */
298 if (lno == 1) {
299 if (file_lline(sp, ep, &lline))
300 return (1);
301 if (lline == 0)
302 F_SET(sp, S_REDRAW);
303 }
304
305 /* Update file. */
306 key.data = &lno;
307 key.size = sizeof(lno);
308 data.data = p;
309 data.size = len;
310 if (ep->db->put(ep->db, &key, &data, R_IBEFORE) == -1) {
311 msgq(sp, M_ERR,
312 "Error: %s/%d: unable to insert at line %u: %s.",
313 tail(__FILE__), __LINE__, lno, strerror(errno));
314 return (1);
315 }
316
317 /* Flush the cache, update line count, before screen update. */
318 if (lno >= ep->c_lno)
319 ep->c_lno = OOBLNO;
320 if (ep->c_nlines != OOBLNO)
321 ++ep->c_nlines;
322
323 /* File now dirty. */
324 if (F_ISSET(ep, F_FIRSTMODIFY))
325 (void)rcv_init(sp, ep);
326 F_SET(ep, F_MODIFIED);
327
328 /* Log change. */
329 log_line(sp, ep, lno, LOG_LINE_INSERT);
330
331 /*
332 * XXX
333 *
334 * Marks and global commands have to know when lines are
335 * inserted or deleted.
336 */
337 mark_insdel(sp, ep, LINE_INSERT, lno);
338 global_insdel(sp, ep, LINE_INSERT, lno);
339
340 /* Update screen. */
341 return (scr_update(sp, ep, lno, LINE_INSERT, 1));
342}
343
344/*
345 * file_sline --
346 * Store a line in the file.
347 */
348int
349file_sline(sp, ep, lno, p, len)
350 SCR *sp;
351 EXF *ep;
352 recno_t lno;
353 char *p;
354 size_t len;
355{
356 DBT data, key;
357
358#if defined(DEBUG) && 0
359 TRACE(sp,
360 "replace line %lu: len %u {%.*s}\n", lno, len, MIN(len, 20), p);
361#endif
362 /* Log before change. */
363 log_line(sp, ep, lno, LOG_LINE_RESET_B);
364
365 /* Update file. */
366 key.data = &lno;
367 key.size = sizeof(lno);
368 data.data = p;
369 data.size = len;
370 if (ep->db->put(ep->db, &key, &data, 0) == -1) {
371 msgq(sp, M_ERR,
372 "Error: %s/%d: unable to store line %u: %s.",
373 tail(__FILE__), __LINE__, lno, strerror(errno));
374 return (1);
375 }
376
377 /* Flush the cache, before logging or screen update. */
378 if (lno == ep->c_lno)
379 ep->c_lno = OOBLNO;
380
381 /* File now dirty. */
382 if (F_ISSET(ep, F_FIRSTMODIFY))
383 (void)rcv_init(sp, ep);
384 F_SET(ep, F_MODIFIED);
385
386 /* Log after change. */
387 log_line(sp, ep, lno, LOG_LINE_RESET_F);
388
389 /* Update screen. */
390 return (scr_update(sp, ep, lno, LINE_RESET, 1));
391}
392
393/*
394 * file_lline --
395 * Return the number of lines in the file.
396 */
397int
398file_lline(sp, ep, lnop)
399 SCR *sp;
400 EXF *ep;
401 recno_t *lnop;
402{
403 DBT data, key;
404 recno_t lno;
405
406 /* Check the cache. */
407 if (ep->c_nlines != OOBLNO) {
408 *lnop = (F_ISSET(sp, S_INPUT) &&
409 ((TEXT *)sp->tiq.cqh_last)->lno > ep->c_nlines ?
410 ((TEXT *)sp->tiq.cqh_last)->lno : ep->c_nlines);
411 return (0);
412 }
413
414 key.data = &lno;
415 key.size = sizeof(lno);
416
417 switch (ep->db->seq(ep->db, &key, &data, R_LAST)) {
418 case -1:
419 msgq(sp, M_ERR,
420 "Error: %s/%d: unable to get last line: %s.",
421 tail(__FILE__), __LINE__, strerror(errno));
422 *lnop = 0;
423 return (1);
424 case 1:
425 lno = 0;
426 break;
427 default:
428 memmove(&lno, key.data, sizeof(lno));
429 break;
430 }
431
432 /* Fill the cache. */
433 ep->c_nlines = ep->c_lno = lno;
434 ep->c_len = data.size;
435 ep->c_lp = data.data;
436
437 *lnop = (F_ISSET(sp, S_INPUT) &&
438 ((TEXT *)sp->tiq.cqh_last)->lno > lno ?
439 ((TEXT *)sp->tiq.cqh_last)->lno : lno);
440 return (0);
441}
442
443/*
444 * scr_update --
445 * Update all of the screens that are backed by the file that
446 * just changed.
447 */
448static inline int
449scr_update(sp, ep, lno, op, current)
450 SCR *sp;
451 EXF *ep;
452 recno_t lno;
453 enum operation op;
454 int current;
455{
456 SCR *tsp;
457
458 if (ep->refcnt != 1)
459 for (tsp = sp->gp->dq.cqh_first;
460 tsp != (void *)&sp->gp->dq; tsp = tsp->q.cqe_next)
461 if (sp != tsp && tsp->ep == ep)
462 (void)sp->s_change(tsp, ep, lno, op);
463 return (current && sp->s_change(sp, ep, lno, op));
464}