]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/nvi/common/delete.c
Update nvi to 2.2.0
[FreeBSD/FreeBSD.git] / contrib / nvi / common / delete.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 <stdlib.h>
21 #include <string.h>
22
23 #include "common.h"
24
25 /*
26  * del --
27  *      Delete a range of text.
28  *
29  * PUBLIC: int del(SCR *, MARK *, MARK *, int);
30  */
31 int
32 del(SCR *sp, MARK *fm, MARK *tm, int lmode)
33 {
34         recno_t lno;
35         size_t blen, len, nlen, tlen;
36         CHAR_T *bp, *p;
37         int eof, rval;
38
39         bp = NULL;
40
41         /* Case 1 -- delete in line mode. */
42         if (lmode) {
43                 for (lno = tm->lno; lno >= fm->lno; --lno) {
44                         if (db_delete(sp, lno))
45                                 return (1);
46                         ++sp->rptlines[L_DELETED];
47                         if (lno % INTERRUPT_CHECK == 0 && INTERRUPTED(sp))
48                                 break;
49                 }
50                 goto done;
51         }
52
53         /*
54          * Case 2 -- delete to EOF.  This is a special case because it's
55          * easier to pick it off than try and find it in the other cases.
56          */
57         if (db_last(sp, &lno))
58                 return (1);
59         if (tm->lno >= lno) {
60                 if (tm->lno == lno) {
61                         if (db_get(sp, lno, DBG_FATAL, &p, &len))
62                                 return (1);
63                         eof = tm->cno != ENTIRE_LINE && tm->cno >= len ? 1 : 0;
64                 } else
65                         eof = 1;
66                 if (eof) {
67                         for (lno = tm->lno; lno > fm->lno; --lno) {
68                                 if (db_delete(sp, lno))
69                                         return (1);
70                                 ++sp->rptlines[L_DELETED];
71                                 if (lno %
72                                     INTERRUPT_CHECK == 0 && INTERRUPTED(sp))
73                                         break;
74                         }
75                         if (db_get(sp, fm->lno, DBG_FATAL, &p, &len))
76                                 return (1);
77                         GET_SPACE_RETW(sp, bp, blen, fm->cno);
78                         MEMCPY(bp, p, fm->cno);
79                         if (db_set(sp, fm->lno, bp, fm->cno))
80                                 return (1);
81                         goto done;
82                 }
83         }
84
85         /* Case 3 -- delete within a single line. */
86         if (tm->lno == fm->lno) {
87                 if (db_get(sp, fm->lno, DBG_FATAL, &p, &len))
88                         return (1);
89                 if (len != 0) {
90                         GET_SPACE_RETW(sp, bp, blen, len);
91                         if (fm->cno != 0)
92                                 MEMCPY(bp, p, fm->cno);
93                         MEMCPY(bp + fm->cno, p + (tm->cno + 1),
94                             len - (tm->cno + 1));
95                         if (db_set(sp, fm->lno,
96                             bp, len - ((tm->cno - fm->cno) + 1)))
97                                 goto err;
98                 }
99                 goto done;
100         }
101
102         /*
103          * Case 4 -- delete over multiple lines.
104          *
105          * Copy the start partial line into place.
106          */
107         if ((tlen = fm->cno) != 0) {
108                 if (db_get(sp, fm->lno, DBG_FATAL, &p, NULL))
109                         return (1);
110                 GET_SPACE_RETW(sp, bp, blen, tlen + 256);
111                 MEMCPY(bp, p, tlen);
112         }
113
114         /* Copy the end partial line into place. */
115         if (db_get(sp, tm->lno, DBG_FATAL, &p, &len))
116                 goto err;
117         if (len != 0 && tm->cno != len - 1) {
118                 /*
119                  * XXX
120                  * We can overflow memory here, if the total length is greater
121                  * than SIZE_T_MAX.  The only portable way I've found to test
122                  * is depending on the overflow being less than the value.
123                  */
124                 nlen = (len - (tm->cno + 1)) + tlen;
125                 if (tlen > nlen) {
126                         msgq(sp, M_ERR, "002|Line length overflow");
127                         goto err;
128                 }
129                 if (tlen == 0) {
130                         GET_SPACE_RETW(sp, bp, blen, nlen);
131                 } else
132                         ADD_SPACE_RETW(sp, bp, blen, nlen);
133
134                 MEMCPY(bp + tlen, p + (tm->cno + 1), len - (tm->cno + 1));
135                 tlen += len - (tm->cno + 1);
136         }
137
138         /* Set the current line. */
139         if (db_set(sp, fm->lno, bp, tlen))
140                 goto err;
141
142         /* Delete the last and intermediate lines. */
143         for (lno = tm->lno; lno > fm->lno; --lno) {
144                 if (db_delete(sp, lno))
145                         goto err;
146                 ++sp->rptlines[L_DELETED];
147                 if (lno % INTERRUPT_CHECK == 0 && INTERRUPTED(sp))
148                         break;
149         }
150
151 done:   rval = 0;
152         if (0)
153 err:            rval = 1;
154         if (bp != NULL)
155                 FREE_SPACEW(sp, bp, blen);
156         return (rval);
157 }