1 /*
2  * The routines in this file
3  * deal with the region, that magic space
4  * between "." and mark. Some functions are
5  * commands. Some functions are just for
6  * internal use.
7  */
8 #include	<stdio.h>
9 #include	"estruct.h"
10 #include	"eproto.h"
11 #include	"edef.h"
12 #include	"elang.h"
13 
14 /*	reglines:	how many lines in the current region
15 			used by the trim/entab/detab-region commands
16 */
17 
reglines()18 int PASCAL NEAR reglines()
19 
20 {
21 	register LINE *linep;	/* position while scanning */
22 	register int n; 	/* number of lines in this current region */
23 	REGION region;
24 
25 	/* check for a valid region first */
26 	if (getregion(&region) != TRUE)
27 		return(0);
28 
29 	/* start at the top of the region.... */
30 	linep = region.r_linep;
31 	region.r_size += region.r_offset;
32 	n = 0;
33 
34 	/* scan the region... counting lines */
35 	while (region.r_size > 0L) {
36 		region.r_size -= lused(linep) + 1;
37 		linep = lforw(linep);
38 		n++;
39 	}
40 
41 	/* place us at the beginning of the region */
42 	curwp->w_dotp = region.r_linep;
43 	curwp->w_doto = region.r_offset;
44 
45 	return(n);
46 }
47 
48 /*
49  * Kill the region. Ask "getregion"
50  * to figure out the bounds of the region.
51  * Move "." to the start, and kill the characters.
52  * Bound to "C-W".
53  */
killregion(f,n)54 PASCAL NEAR killregion(f, n)
55 
56 int f,n;	/* prefix flag and argument */
57 
58 {
59 	register int	s;
60 	REGION		region;
61 
62 	/* Don't do this command in read-only mode */
63 	if (curbp->b_mode&MDVIEW)
64 		return(rdonly());
65 
66 	/* get the boundries of the region to kill */
67 	if ((s=getregion(&region)) != TRUE)
68 		return(s);
69 
70 	/* flag this command as a kill */
71 	if ((lastflag&CFKILL) == 0)
72 		next_kill();
73 	thisflag |= CFKILL;
74 
75 	/* make sure the cursor gets back to the right place on an undo */
76 	undo_insert(OP_CPOS, 0L, obj);
77 
78 	/* delete the region */
79 	curwp->w_dotp = region.r_linep;
80 	curwp->w_doto = region.r_offset;
81 	return(ldelete(region.r_size, TRUE));
82 }
83 
84 /*
85  * Copy all of the characters in the
86  * region to the kill buffer. Don't move dot
87  * at all. This is a bit like a kill region followed
88  * by a yank. Bound to "M-W".
89  */
copyregion(f,n)90 PASCAL NEAR copyregion(f, n)
91 
92 int f,n;	/* prefix flag and argument */
93 
94 {
95 	register LINE	*linep;
96 	register int	loffs;
97 	register int	s;
98 	REGION		region;
99 
100 	if ((s=getregion(&region)) != TRUE)
101 		return(s);
102 	if ((lastflag&CFKILL) == 0)		/* Kill type command.	*/
103 		next_kill();
104 	thisflag |= CFKILL;
105 	linep = region.r_linep; 		/* Current line.	*/
106 	loffs = region.r_offset;		/* Current offset.	*/
107 	while (region.r_size--) {
108 		if (loffs == lused(linep)) {  /* End of line.	      */
109 			if ((s=kinsert(FORWARD, '\r')) != TRUE)
110 				return(s);
111 			linep = lforw(linep);
112 			loffs = 0;
113 		} else {			/* Middle of line.	*/
114 			if ((s=kinsert(FORWARD, lgetc(linep, loffs))) != TRUE)
115 				return(s);
116 			++loffs;
117 		}
118 	}
119 	mlwrite(TEXT70);
120 /*		"[region copied]" */
121 	return(TRUE);
122 }
123 
124 /*
125  * Lower case region. Zap all of the upper
126  * case characters in the region to lower case. Use
127  * the region code to set the limits. Scan the buffer,
128  * doing the changes. Call "lchange" to ensure that
129  * redisplay is done in all buffers. Bound to
130  * "C-X C-L".
131  */
lowerregion(f,n)132 PASCAL NEAR lowerregion(f, n)
133 
134 int f,n;	/* prefix flag and argument */
135 
136 {
137 	register LINE *save_dotp;/* pointer and offset to position to preserve */
138 	register int save_doto;
139 	register int status;	/* return status from fetching region */
140 	int c;			/* current character */
141 	REGION region;		/* region which is being used */
142 
143 	/* don't bother if we are in read only mode */
144 	if (curbp->b_mode&MDVIEW)
145 		return(rdonly());
146 
147 	/* get the limits of the current region */
148 	if ((status = getregion(&region)) != TRUE)
149 		return(status);
150 
151 	/* flag that we are changing this text */
152 	lchange(WFHARD);
153 
154 	/* save the current dot */
155 	save_dotp = curwp->w_dotp;
156 	save_doto = curwp->w_doto;
157 
158 	/* scan the region.... */
159 	curwp->w_dotp = region.r_linep;
160 	curwp->w_doto = region.r_offset;
161 	while (region.r_size--) {
162 
163 		if (curwp->w_doto == lused(curwp->w_dotp)) {
164 
165 			/* skip to the next line */
166 			curwp->w_dotp = lforw(curwp->w_dotp);
167 			curwp->w_doto = 0;
168 
169 		} else {
170 
171 			/* lowercase this character */
172 			c = lgetc(curwp->w_dotp, curwp->w_doto);
173 			if (is_upper(c)) {
174 				obj.obj_char = c;
175 				c = lowerc(c);
176 				lputc(curwp->w_dotp, curwp->w_doto, c);
177 				undo_insert(OP_REPC, 1L, obj);
178 			}
179 			++curwp->w_doto;
180 		}
181 	}
182 
183 	/* restore the dot position */
184 	curwp->w_dotp = save_dotp;
185 	curwp->w_doto = save_doto;
186 	return(TRUE);
187 }
188 
189 /*
190  * Upper case region. Zap all of the lower
191  * case characters in the region to upper case. Use
192  * the region code to set the limits. Scan the buffer,
193  * doing the changes. Call "lchange" to ensure that
194  * redisplay is done in all buffers. Bound to
195  * "C-X C-L".
196  */
upperregion(f,n)197 PASCAL NEAR upperregion(f, n)
198 
199 int f,n;	/* prefix flag and argument */
200 
201 {
202 	register LINE *save_dotp;/* pointer and offset to position to preserve */
203 	register int save_doto;
204 	register int status;	/* return status from fetching region */
205 	int c;			/* current character */
206 	REGION region;		/* region which is being used */
207 
208 	/* don't bother if we are in read only mode */
209 	if (curbp->b_mode&MDVIEW)
210 		return(rdonly());
211 
212 	/* get the limits of the current region */
213 	if ((status = getregion(&region)) != TRUE)
214 		return(status);
215 
216 	/* flag that we are changing this text */
217 	lchange(WFHARD);
218 
219 	/* save the current dot */
220 	save_dotp = curwp->w_dotp;
221 	save_doto = curwp->w_doto;
222 
223 	/* scan the region.... */
224 	curwp->w_dotp = region.r_linep;
225 	curwp->w_doto = region.r_offset;
226 	while (region.r_size--) {
227 
228 		if (curwp->w_doto == lused(curwp->w_dotp)) {
229 
230 			/* skip to the next line */
231 			curwp->w_dotp = lforw(curwp->w_dotp);
232 			curwp->w_doto = 0;
233 
234 		} else {
235 
236 			/* uppercase this character */
237 			c = lgetc(curwp->w_dotp, curwp->w_doto);
238 			if (is_lower(c)) {
239 				obj.obj_char = c;
240 				c = upperc(c);
241 				lputc(curwp->w_dotp, curwp->w_doto, c);
242 				undo_insert(OP_REPC, 1L, obj);
243 			}
244 			++curwp->w_doto;
245 		}
246 	}
247 
248 	/* restore the dot position */
249 	curwp->w_dotp = save_dotp;
250 	curwp->w_doto = save_doto;
251 	return(TRUE);
252 }
253 
254 /*	Narrow-to-region (^X-<) makes all but the current region in
255 	the current buffer invisable and unchangable
256 */
257 
narrow(f,n)258 PASCAL NEAR narrow(f, n)
259 
260 int f,n;	/* prefix flag and argument */
261 
262 {
263 	register int status;	/* return status */
264 	BUFFER *bp;		/* buffer being narrowed */
265 	SCREEN *scrp;		/* screen to fix pointers in */
266 	EWINDOW *wp;		/* windows to fix up pointers in as well */
267 	REGION creg;		/* region boundry structure */
268 	int cmark;		/* current mark */
269 
270 	/* find the proper buffer and make sure we aren't already narrow */
271 	bp = curwp->w_bufp;		/* find the right buffer */
272 	if (bp->b_flag&BFNAROW) {
273 		mlwrite(TEXT71);
274 /*			"%%This buffer is already narrowed" */
275 		return(FALSE);
276 	}
277 
278 	/* find the boundries of the current region */
279 	if ((status=getregion(&creg)) != TRUE)
280 		return(status);
281 	curwp->w_dotp = creg.r_linep;	/* only by full lines please! */
282 	curwp->w_doto = 0;
283 	creg.r_size += (long)creg.r_offset;
284 	if (creg.r_size <= (long)lused(curwp->w_dotp)) {
285 		mlwrite(TEXT72);
286 /*			"%%Must narrow at least 1 full line" */
287 		return(FALSE);
288 	}
289 
290 	/* archive the top fragment */
291 	if (bp->b_linep->l_fp != creg.r_linep) {
292 		bp->b_topline = bp->b_linep->l_fp;
293 		creg.r_linep->l_bp->l_fp = (LINE *)NULL;
294 		bp->b_linep->l_fp = creg.r_linep;
295 		creg.r_linep->l_bp = bp->b_linep;
296 	}
297 
298 	/* move forward to the end of this region
299 	   (a long number of bytes perhaps) */
300 	while (creg.r_size > (long)32000) {
301 		forwchar(TRUE, 32000);
302 		creg.r_size -= (long)32000;
303 	}
304 	forwchar(TRUE, (int)creg.r_size);
305 	curwp->w_doto = 0;		/* only full lines! */
306 
307 	/* archive the bottom fragment */
308 	if (bp->b_linep != curwp->w_dotp) {
309 		bp->b_botline = curwp->w_dotp;
310 		bp->b_botline->l_bp->l_fp = bp->b_linep;
311 		bp->b_linep->l_bp->l_fp = (LINE *)NULL;
312 		bp->b_linep->l_bp = bp->b_botline->l_bp;
313 	}
314 
315 	/* in all screens.... */
316 	scrp = first_screen;
317 	while (scrp) {
318 
319 		/* let all the proper windows be updated */
320 		wp = scrp->s_first_window;
321 		while (wp) {
322 			if (wp->w_bufp == bp) {
323 				wp->w_linep = creg.r_linep;
324 				wp->w_dotp = creg.r_linep;
325 				wp->w_doto = 0;
326 				for (cmark = 0; cmark < NMARKS; cmark++) {
327 					wp->w_markp[cmark] = creg.r_linep;
328 					wp->w_marko[cmark] = 0;
329 				}
330 				wp->w_flag |= (WFHARD|WFMODE);
331 			}
332 			wp = wp->w_wndp;
333 		}
334 
335 		/* next screen! */
336 		scrp = scrp->s_next_screen;
337 	}
338 
339 	/* and now remember we are narrowed */
340 	bp->b_flag |= BFNAROW;
341 	mlwrite(TEXT73);
342 /*		"[Buffer is narrowed]" */
343 	return(TRUE);
344 }
345 
346 /*	widen-from-region (^X->) restores a narrowed region	*/
347 
widen(f,n)348 PASCAL NEAR widen(f, n)
349 
350 int f,n;	/* prefix flag and argument */
351 
352 {
353 	LINE *lp;	/* temp line pointer */
354 	BUFFER *bp;	/* buffer being narrowed */
355 	SCREEN *scrp;	/* screen to fix pointers in */
356 	EWINDOW *wp;	/* windows to fix up pointers in as well */
357 	int cmark;	/* current mark */
358 
359 	/* find the proper buffer and make sure we are narrow */
360 	bp = curwp->w_bufp;		/* find the right buffer */
361 	if ((bp->b_flag&BFNAROW) == 0) {
362 		mlwrite(TEXT74);
363 /*			"%%This buffer is not narrowed" */
364 		return(FALSE);
365 	}
366 
367 	/* recover the top fragment */
368 	if (bp->b_topline != (LINE *)NULL) {
369 		lp = bp->b_topline;
370 		while (lp->l_fp != (LINE *)NULL)
371 			lp = lp->l_fp;
372 		lp->l_fp = bp->b_linep->l_fp;
373 		lp->l_fp->l_bp = lp;
374 		bp->b_linep->l_fp = bp->b_topline;
375 		bp->b_topline->l_bp = bp->b_linep;
376 		bp->b_topline = (LINE *)NULL;
377 	}
378 
379 	/* recover the bottom fragment */
380 	if (bp->b_botline != (LINE *)NULL) {
381 
382 		/* if the point is at EOF, move it to
383 		   the beginning of the bottom fragment */
384 		if (curwp->w_dotp == bp->b_linep) {
385 			curwp->w_dotp = bp->b_botline;
386 			curwp->w_doto = 0;		/* this should be redundent */
387 		}
388 
389 		/* if any marks are at EOF, move them to
390 		   the beginning of the bottom fragment */
391 		for (cmark = 0; cmark < NMARKS; cmark++) {
392 			if (curwp->w_markp[cmark] == bp->b_linep) {
393 				curwp->w_markp[cmark] = bp->b_botline;
394 				curwp->w_marko[cmark] = 0;
395 			}
396 		}
397 
398 		/* connect the bottom fragment */
399 		lp = bp->b_botline;
400 		while (lp->l_fp != (LINE *)NULL)
401 			lp = lp->l_fp;
402 		lp->l_fp = bp->b_linep;
403 		bp->b_linep->l_bp->l_fp = bp->b_botline;
404 		bp->b_botline->l_bp = bp->b_linep->l_bp;
405 		bp->b_linep->l_bp = lp;
406 		bp->b_botline = (LINE *)NULL;
407 	}
408 
409 	/* in all screens.... */
410 	scrp = first_screen;
411 	while (scrp) {
412 
413 		/* let all the proper windows be updated */
414 		wp = scrp->s_first_window;
415 		while (wp) {
416 			if (wp->w_bufp == bp)
417 				wp->w_flag |= (WFHARD|WFMODE);
418 			wp = wp->w_wndp;
419 		}
420 
421 		/* next screen! */
422 		scrp = scrp->s_next_screen;
423 	}
424 
425 	/* and now remember we are not narrowed */
426 	bp->b_flag &= (~BFNAROW);
427 	mlwrite(TEXT75);
428 /*		"[Buffer is widened]" */
429 	return(TRUE);
430 }
431 
432 /*
433  * This routine figures out the bounds of the region in the current
434  * window, and fills in the fields of the "REGION" structure pointed to by
435  * "rp". Because the dot and mark are usually very close together, we scan
436  * outward from dot looking for mark. This should save time. Return a
437  * standard code. Callers of this routine should be prepared to get an
438  * "ABORT" status; we might make this have the confirm thing later.
439  */
440 
getregion(rp)441 PASCAL NEAR getregion(rp)
442 
443 register REGION *rp;
444 
445 {
446 	register LINE	*flp;
447 	register LINE	*blp;
448 	long fsize;
449 	long bsize;
450 
451 	if (curwp->w_markp[0] == (LINE *)NULL) {
452 		mlwrite(TEXT76);
453 /*			"No mark set in this window" */
454 		return(FALSE);
455 	}
456 	if (curwp->w_dotp == curwp->w_markp[0]) {
457 		rp->r_linep = curwp->w_dotp;
458 		if (curwp->w_doto < curwp->w_marko[0]) {
459 			rp->r_offset = curwp->w_doto;
460 			rp->r_size = (long)(curwp->w_marko[0]-curwp->w_doto);
461 		} else {
462 			rp->r_offset = curwp->w_marko[0];
463 			rp->r_size = (long)(curwp->w_doto-curwp->w_marko[0]);
464 		}
465 		return(TRUE);
466 	}
467 	blp = curwp->w_dotp;
468 	bsize = (long)curwp->w_doto;
469 	flp = curwp->w_dotp;
470 	fsize = (long)(lused(flp)-curwp->w_doto+1);
471 	while (flp!=curbp->b_linep || lback(blp)!=curbp->b_linep) {
472 		if (flp != curbp->b_linep) {
473 			flp = lforw(flp);
474 			if (flp == curwp->w_markp[0]) {
475 				rp->r_linep = curwp->w_dotp;
476 				rp->r_offset = curwp->w_doto;
477 				rp->r_size = fsize+curwp->w_marko[0];
478 				return(TRUE);
479 			}
480 			fsize += lused(flp)+1;
481 		}
482 		if (lback(blp) != curbp->b_linep) {
483 			blp = lback(blp);
484 			bsize += lused(blp)+1;
485 			if (blp == curwp->w_markp[0]) {
486 				rp->r_linep = blp;
487 				rp->r_offset = curwp->w_marko[0];
488 				rp->r_size = bsize - curwp->w_marko[0];
489 				return(TRUE);
490 			}
491 		}
492 	}
493 	mlwrite(TEXT77);
494 /*		"Bug: lost mark" */
495 	return(FALSE);
496 }
497 
498 /*
499  * Copy all of the characters in the region to the string buffer.
500  * It is assumed that the buffer size is at least one plus the
501  * region size.
502  */
regtostr(buf,region)503 char *PASCAL NEAR regtostr(buf, region)
504 
505 char *buf;
506 REGION *region;
507 
508 {
509 	register LINE	*linep;
510 	register int	loffs;
511 	register long	rsize;
512 	register char	*ptr;
513 
514 	ptr = buf;
515 	linep = region->r_linep;		/* Current line.	*/
516 	loffs = region->r_offset;		/* Current offset.	*/
517 	rsize = region->r_size;
518 	while (rsize--) {
519 		if (loffs == lused(linep)) {	/* End of line. 	*/
520 			*ptr = '\r';
521 			linep = lforw(linep);
522 			loffs = 0;
523 		} else {			/* Middle of line.	*/
524 			*ptr = lgetc(linep, loffs);
525 			++loffs;
526 		}
527 		ptr++;
528 	}
529 	*ptr = '\0';
530 	return buf;
531 }
532 
getreg(value)533 char *PASCAL NEAR getreg(value) /* return some of the contents of the current region */
534 
535 char *value;
536 
537 {
538 	REGION region;
539 
540 	/* get the region limits */
541 	if (getregion(&region) != TRUE)
542 		return(errorm);
543 
544 	/* don't let the region be larger than a string can hold */
545 	if (region.r_size >= NSTRING)
546 		region.r_size = NSTRING - 1;
547 	return(regtostr(value, &region));
548 }
549 
550 
indent_region(f,n)551 PASCAL NEAR indent_region(f, n) /* indent a region n tab-stops */
552 
553 int f,n;	/* default flag and numeric repeat count */
554 
555 {
556 	register int inc;	/* increment to next line [sgn(n)] */
557 	int count;
558 
559 	if (curbp->b_mode&MDVIEW)	/* don't allow this command if	*/
560 		return(rdonly());	/* we are in read only mode	*/
561 
562 	if (f == FALSE)
563 		count = 1;
564 	else
565 		count = n;
566 	n = reglines();
567 
568 	/* loop thru indenting n lines */
569 	inc = ((n > 0) ? 1 : -1);
570 	while (n) {
571 		curwp->w_doto = 0;	/* start at the beginning */
572 
573 		/* shift current line using tabs */
574 		if (!((curbp->b_mode & MDCMOD) &&
575 		     (lgetc(curwp->w_dotp, curwp->w_doto) == '#')) ) {
576 				linsert(count, '\t');
577 		}
578 
579 		/* advance/or back to the next line */
580 		forwline(TRUE, inc);
581 		n -= inc;
582 	}
583 
584 	curwp->w_doto = 0;
585 	thisflag &= ~CFCPCN;	/* flag that this resets the goal column */
586 	lchange(WFEDIT);	/* yes, we have made at least an edit */
587 	return(TRUE);
588 }
589 
undent_region(f,n)590 PASCAL NEAR undent_region(f, n) /* undent a region n tab-stops */
591 
592 int f,n;	/* default flag and numeric repeat count */
593 
594 {
595 	register int inc;	/* increment to next line [sgn(n)] */
596 	int i, count;
597 
598 	if (curbp->b_mode&MDVIEW)	/* don't allow this command if	*/
599 		return(rdonly());	/* we are in read only mode	*/
600 
601 	if (f == FALSE)
602 		count = 1;
603 	else
604 		count = n;
605 	n = reglines();
606 
607 	/* loop thru undenting n lines */
608 	inc = ((n > 0) ? 1 : -1);
609 	while (n) {
610 		/* unshift current line using tabs */
611 		for (i = 0; i < count; i++) {
612 			curwp->w_doto = 0;	/* start at the beginning */
613 			if ((curwp->w_dotp->l_used > 0) &&
614 			    (lgetc(curwp->w_dotp, curwp->w_doto) == '\t')) {
615 				ldelete(1L, FALSE);
616 			}
617 		}
618 
619 		/* advance/or back to the next line */
620 		forwline(TRUE, inc);
621 		n -= inc;
622 	}
623 
624 	thisflag &= ~CFCPCN;	/* flag that this resets the goal column */
625 	lchange(WFEDIT);	/* yes, we have made at least an edit */
626 	return(TRUE);
627 }
628