source: trunk/third/nvi/common/cut.c @ 14302

Revision 14302, 8.8 KB checked in by ghudson, 25 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r14301, which included commits to RCS files with non-trunk default branches.
Line 
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#ifndef lint
13static const char sccsid[] = "@(#)cut.c 10.10 (Berkeley) 9/15/96";
14#endif /* not lint */
15
16#include <sys/types.h>
17#include <sys/queue.h>
18
19#include <bitstring.h>
20#include <ctype.h>
21#include <errno.h>
22#include <fcntl.h>
23#include <limits.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27
28#include "common.h"
29
30static void     cb_rotate __P((SCR *));
31
32/*
33 * cut --
34 *      Put a range of lines/columns into a TEXT buffer.
35 *
36 * There are two buffer areas, both found in the global structure.  The first
37 * is the linked list of all the buffers the user has named, the second is the
38 * unnamed buffer storage.  There is a pointer, too, which is the current
39 * default buffer, i.e. it may point to the unnamed buffer or a named buffer
40 * depending on into what buffer the last text was cut.  Logically, in both
41 * delete and yank operations, if the user names a buffer, the text is cut
42 * into it.  If it's a delete of information on more than a single line, the
43 * contents of the numbered buffers are rotated up one, the contents of the
44 * buffer named '9' are discarded, and the text is cut into the buffer named
45 * '1'.  The text is always cut into the unnamed buffer.
46 *
47 * In all cases, upper-case buffer names are the same as lower-case names,
48 * with the exception that they cause the buffer to be appended to instead
49 * of replaced.  Note, however, that if text is appended to a buffer, the
50 * default buffer only contains the appended text, not the entire contents
51 * of the buffer.
52 *
53 * !!!
54 * The contents of the default buffer would disappear after most operations
55 * in historic vi.  It's unclear that this is useful, so we don't bother.
56 *
57 * When users explicitly cut text into the numeric buffers, historic vi became
58 * genuinely strange.  I've never been able to figure out what was supposed to
59 * happen.  It behaved differently if you deleted text than if you yanked text,
60 * and, in the latter case, the text was appended to the buffer instead of
61 * replacing the contents.  Hopefully it's not worth getting right, and here
62 * we just treat the numeric buffers like any other named buffer.
63 *
64 * PUBLIC: int cut __P((SCR *, CHAR_T *, MARK *, MARK *, int));
65 */
66int
67cut(sp, namep, fm, tm, flags)
68        SCR *sp;
69        CHAR_T *namep;
70        MARK *fm, *tm;
71        int flags;
72{
73        CB *cbp;
74        CHAR_T name;
75        recno_t lno;
76        int append, copy_one, copy_def;
77
78        /*
79         * If the user specified a buffer, put it there.  (This may require
80         * a copy into the numeric buffers.  We do the copy so that we don't
81         * have to reference count and so we don't have to deal with things
82         * like appends to buffers that are used multiple times.)
83         *
84         * Otherwise, if it's supposed to be put in a numeric buffer (usually
85         * a delete) put it there.  The rules for putting things in numeric
86         * buffers were historically a little strange.  There were three cases.
87         *
88         *      1: Some motions are always line mode motions, which means
89         *         that the cut always goes into the numeric buffers.
90         *      2: Some motions aren't line mode motions, e.g. d10w, but
91         *         can cross line boundaries.  For these commands, if the
92         *         cut crosses a line boundary, it goes into the numeric
93         *         buffers.  This includes most of the commands.
94         *      3: Some motions aren't line mode motions, e.g. d`<char>,
95         *         but always go into the numeric buffers, regardless.  This
96         *         was the commands: % ` / ? ( ) N n { } -- and nvi adds ^A.
97         *
98         * Otherwise, put it in the unnamed buffer.
99         */
100        append = copy_one = copy_def = 0;
101        if (namep != NULL) {
102                name = *namep;
103                if (LF_ISSET(CUT_NUMREQ) || LF_ISSET(CUT_NUMOPT) &&
104                    (LF_ISSET(CUT_LINEMODE) || fm->lno != tm->lno)) {
105                        copy_one = 1;
106                        cb_rotate(sp);
107                }
108                if ((append = isupper(name)) == 1) {
109                        if (!copy_one)
110                                copy_def = 1;
111                        name = tolower(name);
112                }
113namecb:         CBNAME(sp, cbp, name);
114        } else if (LF_ISSET(CUT_NUMREQ) || LF_ISSET(CUT_NUMOPT) &&
115            (LF_ISSET(CUT_LINEMODE) || fm->lno != tm->lno)) {
116                name = '1';
117                cb_rotate(sp);
118                goto namecb;
119        } else
120                cbp = &sp->gp->dcb_store;
121
122copyloop:
123        /*
124         * If this is a new buffer, create it and add it into the list.
125         * Otherwise, if it's not an append, free its current contents.
126         */
127        if (cbp == NULL) {
128                CALLOC_RET(sp, cbp, CB *, 1, sizeof(CB));
129                cbp->name = name;
130                CIRCLEQ_INIT(&cbp->textq);
131                LIST_INSERT_HEAD(&sp->gp->cutq, cbp, q);
132        } else if (!append) {
133                text_lfree(&cbp->textq);
134                cbp->len = 0;
135                cbp->flags = 0;
136        }
137
138
139#define ENTIRE_LINE     0
140        /* In line mode, it's pretty easy, just cut the lines. */
141        if (LF_ISSET(CUT_LINEMODE)) {
142                cbp->flags |= CB_LMODE;
143                for (lno = fm->lno; lno <= tm->lno; ++lno)
144                        if (cut_line(sp, lno, 0, 0, cbp))
145                                goto cut_line_err;
146        } else {
147                /*
148                 * Get the first line.  A length of 0 causes cut_line
149                 * to cut from the MARK to the end of the line.
150                 */
151                if (cut_line(sp, fm->lno, fm->cno, fm->lno != tm->lno ?
152                    ENTIRE_LINE : (tm->cno - fm->cno) + 1, cbp))
153                        goto cut_line_err;
154
155                /* Get the intermediate lines. */
156                for (lno = fm->lno; ++lno < tm->lno;)
157                        if (cut_line(sp, lno, 0, ENTIRE_LINE, cbp))
158                                goto cut_line_err;
159
160                /* Get the last line. */
161                if (tm->lno != fm->lno &&
162                    cut_line(sp, lno, 0, tm->cno + 1, cbp))
163                        goto cut_line_err;
164        }
165
166        append = 0;             /* Only append to the named buffer. */
167        sp->gp->dcbp = cbp;     /* Repoint the default buffer on each pass. */
168
169        if (copy_one) {         /* Copy into numeric buffer 1. */
170                name = '1';
171                CBNAME(sp, cbp, name);
172                copy_one = 0;
173                goto copyloop;
174        }
175        if (copy_def) {         /* Copy into the default buffer. */
176                cbp = &sp->gp->dcb_store;
177                copy_def = 0;
178                goto copyloop;
179        }
180        return (0);
181
182cut_line_err:   
183        text_lfree(&cbp->textq);
184        cbp->len = 0;
185        cbp->flags = 0;
186        return (1);
187}
188
189/*
190 * cb_rotate --
191 *      Rotate the numbered buffers up one.
192 */
193static void
194cb_rotate(sp)
195        SCR *sp;
196{
197        CB *cbp, *del_cbp;
198
199        del_cbp = NULL;
200        for (cbp = sp->gp->cutq.lh_first; cbp != NULL; cbp = cbp->q.le_next)
201                switch(cbp->name) {
202                case '1':
203                        cbp->name = '2';
204                        break;
205                case '2':
206                        cbp->name = '3';
207                        break;
208                case '3':
209                        cbp->name = '4';
210                        break;
211                case '4':
212                        cbp->name = '5';
213                        break;
214                case '5':
215                        cbp->name = '6';
216                        break;
217                case '6':
218                        cbp->name = '7';
219                        break;
220                case '7':
221                        cbp->name = '8';
222                        break;
223                case '8':
224                        cbp->name = '9';
225                        break;
226                case '9':
227                        del_cbp = cbp;
228                        break;
229                }
230        if (del_cbp != NULL) {
231                LIST_REMOVE(del_cbp, q);
232                text_lfree(&del_cbp->textq);
233                free(del_cbp);
234        }
235}
236
237/*
238 * cut_line --
239 *      Cut a portion of a single line.
240 *
241 * PUBLIC: int cut_line __P((SCR *, recno_t, size_t, size_t, CB *));
242 */
243int
244cut_line(sp, lno, fcno, clen, cbp)
245        SCR *sp;
246        recno_t lno;
247        size_t fcno, clen;
248        CB *cbp;
249{
250        TEXT *tp;
251        size_t len;
252        char *p;
253
254        /* Get the line. */
255        if (db_get(sp, lno, DBG_FATAL, &p, &len))
256                return (1);
257
258        /* Create a TEXT structure that can hold the entire line. */
259        if ((tp = text_init(sp, NULL, 0, len)) == NULL)
260                return (1);
261
262        /*
263         * If the line isn't empty and it's not the entire line,
264         * copy the portion we want, and reset the TEXT length.
265         */
266        if (len != 0) {
267                if (clen == 0)
268                        clen = len - fcno;
269                memcpy(tp->lb, p + fcno, clen);
270                tp->len = clen;
271        }
272
273        /* Append to the end of the cut buffer. */
274        CIRCLEQ_INSERT_TAIL(&cbp->textq, tp, q);
275        cbp->len += tp->len;
276
277        return (0);
278}
279
280/*
281 * cut_close --
282 *      Discard all cut buffers.
283 *
284 * PUBLIC: void cut_close __P((GS *));
285 */
286void
287cut_close(gp)
288        GS *gp;
289{
290        CB *cbp;
291
292        /* Free cut buffer list. */
293        while ((cbp = gp->cutq.lh_first) != NULL) {
294                if (cbp->textq.cqh_first != (void *)&cbp->textq)
295                        text_lfree(&cbp->textq);
296                LIST_REMOVE(cbp, q);
297                free(cbp);
298        }
299
300        /* Free default cut storage. */
301        cbp = &gp->dcb_store;
302        if (cbp->textq.cqh_first != (void *)&cbp->textq)
303                text_lfree(&cbp->textq);
304}
305
306/*
307 * text_init --
308 *      Allocate a new TEXT structure.
309 *
310 * PUBLIC: TEXT *text_init __P((SCR *, const char *, size_t, size_t));
311 */
312TEXT *
313text_init(sp, p, len, total_len)
314        SCR *sp;
315        const char *p;
316        size_t len, total_len;
317{
318        TEXT *tp;
319
320        CALLOC(sp, tp, TEXT *, 1, sizeof(TEXT));
321        if (tp == NULL)
322                return (NULL);
323        /* ANSI C doesn't define a call to malloc(3) for 0 bytes. */
324        if ((tp->lb_len = total_len) != 0) {
325                MALLOC(sp, tp->lb, CHAR_T *, tp->lb_len);
326                if (tp->lb == NULL) {
327                        free(tp);
328                        return (NULL);
329                }
330                if (p != NULL && len != 0)
331                        memcpy(tp->lb, p, len);
332        }
333        tp->len = len;
334        return (tp);
335}
336
337/*
338 * text_lfree --
339 *      Free a chain of text structures.
340 *
341 * PUBLIC: void text_lfree __P((TEXTH *));
342 */
343void
344text_lfree(headp)
345        TEXTH *headp;
346{
347        TEXT *tp;
348
349        while ((tp = headp->cqh_first) != (void *)headp) {
350                CIRCLEQ_REMOVE(headp, tp, q);
351                text_free(tp);
352        }
353}
354
355/*
356 * text_free --
357 *      Free a text structure.
358 *
359 * PUBLIC: void text_free __P((TEXT *));
360 */
361void
362text_free(tp)
363        TEXT *tp;
364{
365        if (tp->lb != NULL)
366                free(tp->lb);
367        free(tp);
368}
Note: See TracBrowser for help on using the repository browser.