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