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