source: trunk/third/nvi/vi/v_paragraph.c @ 14302

Revision 14302, 8.0 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[] = "@(#)v_paragraph.c 10.7 (Berkeley) 3/6/96";
14#endif /* not lint */
15
16#include <sys/types.h>
17#include <sys/queue.h>
18#include <sys/time.h>
19
20#include <bitstring.h>
21#include <errno.h>
22#include <limits.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26
27#include "../common/common.h"
28#include "vi.h"
29
30#define INTEXT_CHECK {                                                  \
31        if (len == 0 || v_isempty(p, len)) {                            \
32                if (!--cnt)                                             \
33                        goto found;                                     \
34                pstate = P_INBLANK;                                     \
35        }                                                               \
36        /*                                                              \
37         * !!!                                                          \
38         * Historic documentation (USD:15-11, 4.2) said that formfeed   \
39         * characters (^L) in the first column delimited paragraphs.    \
40         * The historic vi code mentions formfeed characters, but never \
41         * implements them.  It seems reasonable, do it.                \
42         */                                                             \
43        if (p[0] == '\014') {                                           \
44                if (!--cnt)                                             \
45                        goto found;                                     \
46                continue;                                               \
47        }                                                               \
48        if (p[0] != '.' || len < 2)                                     \
49                continue;                                               \
50        for (lp = VIP(sp)->ps; *lp != '\0'; lp += 2)                    \
51                if (lp[0] == p[1] &&                                    \
52                    (lp[1] == ' ' && len == 2 || lp[1] == p[2]) &&      \
53                    !--cnt)                                             \
54                        goto found;                                     \
55}
56
57/*
58 * v_paragraphf -- [count]}
59 *      Move forward count paragraphs.
60 *
61 * Paragraphs are empty lines after text, formfeed characters, or values
62 * from the paragraph or section options.
63 *
64 * PUBLIC: int v_paragraphf __P((SCR *, VICMD *));
65 */
66int
67v_paragraphf(sp, vp)
68        SCR *sp;
69        VICMD *vp;
70{
71        enum { P_INTEXT, P_INBLANK } pstate;
72        size_t lastlen, len;
73        recno_t cnt, lastlno, lno;
74        int isempty;
75        char *p, *lp;
76
77        /*
78         * !!!
79         * If the starting cursor position is at or before any non-blank
80         * characters in the line, i.e. the movement is cutting all of the
81         * line's text, the buffer is in line mode.  It's a lot easier to
82         * check here, because we know that the end is going to be the start
83         * or end of a line.
84         *
85         * This was historical practice in vi, with a single exception.  If
86         * the paragraph movement was from the start of the last line to EOF,
87         * then all the characters were deleted from the last line, but the
88         * line itself remained.  If somebody complains, don't pause, don't
89         * hesitate, just hit them.
90         */
91        if (ISMOTION(vp))
92                if (vp->m_start.cno == 0)
93                        F_SET(vp, VM_LMODE);
94                else {
95                        vp->m_stop = vp->m_start;
96                        vp->m_stop.cno = 0;
97                        if (nonblank(sp, vp->m_stop.lno, &vp->m_stop.cno))
98                                return (1);
99                        if (vp->m_start.cno <= vp->m_stop.cno)
100                                F_SET(vp, VM_LMODE);
101                }
102
103        /* Figure out what state we're currently in. */
104        lno = vp->m_start.lno;
105        if (db_get(sp, lno, 0, &p, &len))
106                goto eof;
107
108        /*
109         * If we start in text, we want to switch states
110         * (2 * N - 1) times, in non-text, (2 * N) times.
111         */
112        cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
113        cnt *= 2;
114        if (len == 0 || v_isempty(p, len))
115                pstate = P_INBLANK;
116        else {
117                --cnt;
118                pstate = P_INTEXT;
119        }
120
121        for (;;) {
122                lastlno = lno;
123                lastlen = len;
124                if (db_get(sp, ++lno, 0, &p, &len))
125                        goto eof;
126                switch (pstate) {
127                case P_INTEXT:
128                        INTEXT_CHECK;
129                        break;
130                case P_INBLANK:
131                        if (len == 0 || v_isempty(p, len))
132                                break;
133                        if (--cnt) {
134                                pstate = P_INTEXT;
135                                break;
136                        }
137                        /*
138                         * !!!
139                         * Non-motion commands move to the end of the range,
140                         * delete and yank stay at the start.  Ignore others.
141                         * Adjust the end of the range for motion commands;
142                         * historically, a motion component was to the end of
143                         * the previous line, whereas the movement command was
144                         * to the start of the new "paragraph".
145                         */
146found:                  if (ISMOTION(vp)) {
147                                vp->m_stop.lno = lastlno;
148                                vp->m_stop.cno = lastlen ? lastlen - 1 : 0;
149                                vp->m_final = vp->m_start;
150                        } else {
151                                vp->m_stop.lno = lno;
152                                vp->m_stop.cno = 0;
153                                vp->m_final = vp->m_stop;
154                        }
155                        return (0);
156                default:
157                        abort();
158                }
159        }
160
161        /*
162         * !!!
163         * Adjust end of the range for motion commands; EOF is a movement
164         * sink.  The } command historically moved to the end of the last
165         * line, not the beginning, from any position before the end of the
166         * last line.  It also historically worked on empty files, so we
167         * have to make it okay.
168         */
169eof:    if (vp->m_start.lno == lno || vp->m_start.lno == lno - 1) {
170                if (db_eget(sp, vp->m_start.lno, &p, &len, &isempty)) {
171                        if (!isempty)
172                                return (1);
173                        vp->m_start.cno = 0;
174                        return (0);
175                }
176                if (vp->m_start.cno == (len ? len - 1 : 0)) {
177                        v_eof(sp, NULL);
178                        return (1);
179                }
180        }
181        /*
182         * !!!
183         * Non-motion commands move to the end of the range, delete
184         * and yank stay at the start.  Ignore others.
185         *
186         * If deleting the line (which happens if deleting to EOF), then
187         * cursor movement is to the first nonblank.
188         */
189        if (ISMOTION(vp) && ISCMD(vp->rkp, 'd')) {
190                F_CLR(vp, VM_RCM_MASK);
191                F_SET(vp, VM_RCM_SETFNB);
192        }
193        vp->m_stop.lno = lno - 1;
194        vp->m_stop.cno = len ? len - 1 : 0;
195        vp->m_final = ISMOTION(vp) ? vp->m_start : vp->m_stop;
196        return (0);
197}
198
199/*
200 * v_paragraphb -- [count]{
201 *      Move backward count paragraphs.
202 *
203 * PUBLIC: int v_paragraphb __P((SCR *, VICMD *));
204 */
205int
206v_paragraphb(sp, vp)
207        SCR *sp;
208        VICMD *vp;
209{
210        enum { P_INTEXT, P_INBLANK } pstate;
211        size_t len;
212        recno_t cnt, lno;
213        char *p, *lp;
214
215        /*
216         * !!!
217         * Check for SOF.  The historic vi didn't complain if users hit SOF
218         * repeatedly, unless it was part of a motion command.  There is no
219         * question but that Emerson's editor of choice was vi.
220         *
221         * The { command historically moved to the beginning of the first
222         * line if invoked on the first line.
223         *
224         * !!!
225         * If the starting cursor position is in the first column (backward
226         * paragraph movements did NOT historically pay attention to non-blank
227         * characters) i.e. the movement is cutting the entire line, the buffer
228         * is in line mode.  Cuts from the beginning of the line also did not
229         * cut the current line, but started at the previous EOL.
230         *
231         * Correct for a left motion component while we're thinking about it.
232         */
233        lno = vp->m_start.lno;
234
235        if (ISMOTION(vp))
236                if (vp->m_start.cno == 0) {
237                        if (vp->m_start.lno == 1) {
238                                v_sof(sp, &vp->m_start);
239                                return (1);
240                        } else
241                                --vp->m_start.lno;
242                        F_SET(vp, VM_LMODE);
243                } else
244                        --vp->m_start.cno;
245
246        if (vp->m_start.lno <= 1)
247                goto sof;
248
249        /* Figure out what state we're currently in. */
250        if (db_get(sp, lno, 0, &p, &len))
251                goto sof;
252
253        /*
254         * If we start in text, we want to switch states
255         * (2 * N - 1) times, in non-text, (2 * N) times.
256         */
257        cnt = F_ISSET(vp, VC_C1SET) ? vp->count : 1;
258        cnt *= 2;
259        if (len == 0 || v_isempty(p, len))
260                pstate = P_INBLANK;
261        else {
262                --cnt;
263                pstate = P_INTEXT;
264
265                /*
266                 * !!!
267                 * If the starting cursor is past the first column,
268                 * the current line is checked for a paragraph.
269                 */
270                if (vp->m_start.cno > 0)
271                        ++lno;
272        }
273
274        for (;;) {
275                if (db_get(sp, --lno, 0, &p, &len))
276                        goto sof;
277                switch (pstate) {
278                case P_INTEXT:
279                        INTEXT_CHECK;
280                        break;
281                case P_INBLANK:
282                        if (len != 0 && !v_isempty(p, len)) {
283                                if (!--cnt)
284                                        goto found;
285                                pstate = P_INTEXT;
286                        }
287                        break;
288                default:
289                        abort();
290                }
291        }
292
293        /* SOF is a movement sink. */
294sof:    lno = 1;
295
296found:  vp->m_stop.lno = lno;
297        vp->m_stop.cno = 0;
298
299        /*
300         * All commands move to the end of the range.  (We already
301         * adjusted the start of the range for motion commands).
302         */
303        vp->m_final = vp->m_stop;
304        return (0);
305}
306
307/*
308 * v_buildps --
309 *      Build the paragraph command search pattern.
310 *
311 * PUBLIC: int v_buildps __P((SCR *, char *, char *));
312 */
313int
314v_buildps(sp, p_p, s_p)
315        SCR *sp;
316        char *p_p, *s_p;
317{
318        VI_PRIVATE *vip;
319        size_t p_len, s_len;
320        char *p;
321
322        /*
323         * The vi paragraph command searches for either a paragraph or
324         * section option macro.
325         */
326        p_len = p_p == NULL ? 0 : strlen(p_p);
327        s_len = s_p == NULL ? 0 : strlen(s_p);
328
329        if (p_len == 0 && s_len == 0)
330                return (0);
331
332        MALLOC_RET(sp, p, char *, p_len + s_len + 1);
333
334        vip = VIP(sp);
335        if (vip->ps != NULL)
336                free(vip->ps);
337
338        if (p_p != NULL)
339                memmove(p, p_p, p_len + 1);
340        if (s_p != NULL)
341                memmove(p + p_len, s_p, s_len + 1);
342        vip->ps = p;
343        return (0);
344}
Note: See TracBrowser for help on using the repository browser.