xref: /openbsd-src/usr.bin/mg/region.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: region.c,v 1.27 2008/09/15 16:11:35 kjell Exp $	*/
2 
3 /* This file is in the public domain. */
4 
5 /*
6  *		Region based commands.
7  * The routines in this file deal with the region, that magic space between
8  * "." and mark.  Some functions are commands.  Some functions are just for
9  * internal use.
10  */
11 
12 #include "def.h"
13 
14 static	int	getregion(struct region *);
15 static	int	setsize(struct region *, RSIZE);
16 
17 /*
18  * Kill the region.  Ask "getregion" to figure out the bounds of the region.
19  * Move "." to the start, and kill the characters. Mark is cleared afterwards.
20  */
21 /* ARGSUSED */
22 int
23 killregion(int f, int n)
24 {
25 	int	s;
26 	struct region	region;
27 
28 	if ((s = getregion(&region)) != TRUE)
29 		return (s);
30 	/* This is a kill-type command, so do magic kill buffer stuff. */
31 	if ((lastflag & CFKILL) == 0)
32 		kdelete();
33 	thisflag |= CFKILL;
34 	curwp->w_dotp = region.r_linep;
35 	curwp->w_doto = region.r_offset;
36 	s = ldelete(region.r_size, KFORW);
37 	clearmark(FFARG, 0);
38 
39 	return (s);
40 }
41 
42 /*
43  * Copy all of the characters in the region to the kill buffer,
44  * clearing the mark afterwards.
45  * This is a bit like a kill region followed by a yank.
46  */
47 /* ARGSUSED */
48 int
49 copyregion(int f, int n)
50 {
51 	struct line	*linep;
52 	struct region	 region;
53 	int	 loffs;
54 	int	 s;
55 
56 	if ((s = getregion(&region)) != TRUE)
57 		return (s);
58 
59 	/* kill type command */
60 	if ((lastflag & CFKILL) == 0)
61 		kdelete();
62 	thisflag |= CFKILL;
63 
64 	/* current line */
65 	linep = region.r_linep;
66 
67 	/* current offset */
68 	loffs = region.r_offset;
69 
70 	while (region.r_size--) {
71 		if (loffs == llength(linep)) {	/* End of line.		 */
72 			if ((s = kinsert('\n', KFORW)) != TRUE)
73 				return (s);
74 			linep = lforw(linep);
75 			loffs = 0;
76 		} else {			/* Middle of line.	 */
77 			if ((s = kinsert(lgetc(linep, loffs), KFORW)) != TRUE)
78 				return (s);
79 			++loffs;
80 		}
81 	}
82 	clearmark(FFARG, 0);
83 
84 	return (TRUE);
85 }
86 
87 /*
88  * Lower case region.  Zap all of the upper case characters in the region to
89  * lower case. Use the region code to set the limits. Scan the buffer, doing
90  * the changes. Call "lchange" to ensure that redisplay is done in all
91  * buffers.
92  */
93 /* ARGSUSED */
94 int
95 lowerregion(int f, int n)
96 {
97 	struct line	*linep;
98 	struct region	 region;
99 	int	 loffs, c, s;
100 
101 	if ((s = checkdirty(curbp)) != TRUE)
102 		return (s);
103 	if (curbp->b_flag & BFREADONLY) {
104 		ewprintf("Buffer is read-only");
105 		return (FALSE);
106 	}
107 
108 	if ((s = getregion(&region)) != TRUE)
109 		return (s);
110 
111 	undo_add_change(region.r_linep, region.r_offset, region.r_size);
112 
113 	lchange(WFFULL);
114 	linep = region.r_linep;
115 	loffs = region.r_offset;
116 	while (region.r_size--) {
117 		if (loffs == llength(linep)) {
118 			linep = lforw(linep);
119 			loffs = 0;
120 		} else {
121 			c = lgetc(linep, loffs);
122 			if (ISUPPER(c) != FALSE)
123 				lputc(linep, loffs, TOLOWER(c));
124 			++loffs;
125 		}
126 	}
127 	return (TRUE);
128 }
129 
130 /*
131  * Upper case region.  Zap all of the lower case characters in the region to
132  * upper case.  Use the region code to set the limits.  Scan the buffer,
133  * doing the changes.  Call "lchange" to ensure that redisplay is done in all
134  * buffers.
135  */
136 /* ARGSUSED */
137 int
138 upperregion(int f, int n)
139 {
140 	struct line	 *linep;
141 	struct region	  region;
142 	int	  loffs, c, s;
143 
144 	if ((s = checkdirty(curbp)) != TRUE)
145 		return (s);
146 	if (curbp->b_flag & BFREADONLY) {
147 		ewprintf("Buffer is read-only");
148 		return (FALSE);
149 	}
150 	if ((s = getregion(&region)) != TRUE)
151 		return (s);
152 
153 	undo_add_change(region.r_linep, region.r_offset, region.r_size);
154 
155 	lchange(WFFULL);
156 	linep = region.r_linep;
157 	loffs = region.r_offset;
158 	while (region.r_size--) {
159 		if (loffs == llength(linep)) {
160 			linep = lforw(linep);
161 			loffs = 0;
162 		} else {
163 			c = lgetc(linep, loffs);
164 			if (ISLOWER(c) != FALSE)
165 				lputc(linep, loffs, TOUPPER(c));
166 			++loffs;
167 		}
168 	}
169 	return (TRUE);
170 }
171 
172 /*
173  * This routine figures out the bound of the region in the current window,
174  * and stores the results into the fields of the REGION structure. Dot and
175  * mark are usually close together, but I don't know the order, so I scan
176  * outward from dot, in both directions, looking for mark. The size is kept
177  * in a long. At the end, after the size is figured out, it is assigned to
178  * the size field of the region structure. If this assignment loses any bits,
179  * then we print an error. This is "type independent" overflow checking. All
180  * of the callers of this routine should be ready to get an ABORT status,
181  * because I might add a "if regions is big, ask before clobbering" flag.
182  */
183 static int
184 getregion(struct region *rp)
185 {
186 	struct line	*flp, *blp;
187 	long	 fsize, bsize;
188 
189 	if (curwp->w_markp == NULL) {
190 		ewprintf("No mark set in this window");
191 		return (FALSE);
192 	}
193 
194 	/* "r_size" always ok */
195 	if (curwp->w_dotp == curwp->w_markp) {
196 		rp->r_linep = curwp->w_dotp;
197 		if (curwp->w_doto < curwp->w_marko) {
198 			rp->r_offset = curwp->w_doto;
199 			rp->r_size = (RSIZE)(curwp->w_marko - curwp->w_doto);
200 		} else {
201 			rp->r_offset = curwp->w_marko;
202 			rp->r_size = (RSIZE)(curwp->w_doto - curwp->w_marko);
203 		}
204 		return (TRUE);
205 	}
206 	/* get region size */
207 	flp = blp = curwp->w_dotp;
208 	bsize = curwp->w_doto;
209 	fsize = llength(flp) - curwp->w_doto + 1;
210 	while (lforw(flp) != curbp->b_headp || lback(blp) != curbp->b_headp) {
211 		if (lforw(flp) != curbp->b_headp) {
212 			flp = lforw(flp);
213 			if (flp == curwp->w_markp) {
214 				rp->r_linep = curwp->w_dotp;
215 				rp->r_offset = curwp->w_doto;
216 				return (setsize(rp,
217 				    (RSIZE)(fsize + curwp->w_marko)));
218 			}
219 			fsize += llength(flp) + 1;
220 		}
221 		if (lback(blp) != curbp->b_headp) {
222 			blp = lback(blp);
223 			bsize += llength(blp) + 1;
224 			if (blp == curwp->w_markp) {
225 				rp->r_linep = blp;
226 				rp->r_offset = curwp->w_marko;
227 				return (setsize(rp,
228 				    (RSIZE)(bsize - curwp->w_marko)));
229 			}
230 		}
231 	}
232 	ewprintf("Bug: lost mark");
233 	return (FALSE);
234 }
235 
236 /*
237  * Set size, and check for overflow.
238  */
239 static int
240 setsize(struct region *rp, RSIZE size)
241 {
242 	rp->r_size = size;
243 	if (rp->r_size != size) {
244 		ewprintf("Region is too large");
245 		return (FALSE);
246 	}
247 	return (TRUE);
248 }
249 
250 #define PREFIXLENGTH 40
251 static char	prefix_string[PREFIXLENGTH] = {'>', '\0'};
252 
253 /*
254  * Prefix the region with whatever is in prefix_string.  Leaves dot at the
255  * beginning of the line after the end of the region.  If an argument is
256  * given, prompts for the line prefix string.
257  */
258 /* ARGSUSED */
259 int
260 prefixregion(int f, int n)
261 {
262 	struct line	*first, *last;
263 	struct region	 region;
264 	char	*prefix = prefix_string;
265 	int	 nline;
266 	int	 s;
267 
268 	if ((s = checkdirty(curbp)) != TRUE)
269 		return (s);
270 	if (curbp->b_flag & BFREADONLY) {
271 		ewprintf("Buffer is read-only");
272 		return (FALSE);
273 	}
274 	if ((f == TRUE) && ((s = setprefix(FFRAND, 1)) != TRUE))
275 		return (s);
276 
277 	/* get # of lines to affect */
278 	if ((s = getregion(&region)) != TRUE)
279 		return (s);
280 	first = region.r_linep;
281 	last = (first == curwp->w_dotp) ? curwp->w_markp : curwp->w_dotp;
282 	for (nline = 1; first != last; nline++)
283 		first = lforw(first);
284 
285 	/* move to beginning of region */
286 	curwp->w_dotp = region.r_linep;
287 	curwp->w_doto = region.r_offset;
288 
289 	/* for each line, go to beginning and insert the prefix string */
290 	while (nline--) {
291 		(void)gotobol(FFRAND, 1);
292 		for (prefix = prefix_string; *prefix; prefix++)
293 			(void)linsert(1, *prefix);
294 		(void)forwline(FFRAND, 1);
295 	}
296 	(void)gotobol(FFRAND, 1);
297 	return (TRUE);
298 }
299 
300 /*
301  * Set line prefix string. Used by prefixregion.
302  */
303 /* ARGSUSED */
304 int
305 setprefix(int f, int n)
306 {
307 	char	buf[PREFIXLENGTH], *rep;
308 	int	retval;
309 
310 	if (prefix_string[0] == '\0')
311 		rep = eread("Prefix string: ", buf, sizeof(buf),
312 		    EFNEW | EFCR);
313 	else
314 		rep = eread("Prefix string (default %s): ", buf, sizeof(buf),
315 		    EFNUL | EFNEW | EFCR, prefix_string);
316 	if (rep == NULL)
317 		return (ABORT);
318 	if (rep[0] != '\0') {
319 		(void)strlcpy(prefix_string, rep, sizeof(prefix_string));
320 		retval = TRUE;
321 	} else if (rep[0] == '\0' && prefix_string[0] != '\0') {
322 		/* CR -- use old one */
323 		retval = TRUE;
324 	} else
325 		retval = FALSE;
326 	return (retval);
327 }
328 
329 int
330 region_get_data(struct region *reg, char *buf, int len)
331 {
332 	int	 i, off;
333 	struct line	*lp;
334 
335 	off = reg->r_offset;
336 	lp = reg->r_linep;
337 	for (i = 0; i < len; i++) {
338 		if (off == llength(lp)) {
339 			lp = lforw(lp);
340 			if (lp == curbp->b_headp)
341 				break;
342 			off = 0;
343 			buf[i] = '\n';
344 		} else {
345 			buf[i] = lgetc(lp, off);
346 			off++;
347 		}
348 	}
349 	buf[i] = '\0';
350 	return (i);
351 }
352 
353 void
354 region_put_data(const char *buf, int len)
355 {
356 	int i;
357 
358 	for (i = 0; buf[i] != '\0' && i < len; i++) {
359 		if (buf[i] == '\n')
360 			lnewline();
361 		else
362 			linsert(1, buf[i]);
363 	}
364 }
365