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(®ion) != 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(®ion)) != 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(®ion)) != 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(®ion)) != 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(®ion)) != 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(®ion) != 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, ®ion));
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