1 /*
2 * This file contains the command processing functions for a number of random
3 * commands. There is no functional grouping here, for sure.
4 */
5
6 #include <stdio.h>
7 #include "estruct.h"
8 #include "eproto.h"
9 #include "edef.h"
10 #include "elang.h"
11
12 /*
13 * Display the current position of the cursor, in origin 1 X-Y coordinates,
14 * the character that is under the cursor (in hex), and the fraction of the
15 * text that is before the cursor. The displayed column is not the current
16 * column, but the column that would be used on an infinite width display.
17 * Normally this is bound to "C-X =".
18 */
19
showcpos(f,n)20 PASCAL NEAR showcpos(f, n)
21
22 int f, n; /* prefix flag and argument */
23
24 {
25 register LINE *lp; /* current line */
26 register long numchars; /* # of chars in file */
27 register long numlines; /* # of lines in file */
28 register long predchars; /* # chars preceding point */
29 register long predlines; /* # lines preceding point */
30 register int curchar; /* character under cursor */
31 int ratio;
32 int col;
33 int savepos; /* temp save for current offset */
34 int ecol; /* column pos/end of current line */
35
36 /* starting at the beginning of the buffer */
37 lp = lforw(curbp->b_linep);
38 curchar = '\r';
39
40 /* start counting chars and lines */
41 numchars = 0;
42 numlines = 0L;
43 while (lp != curbp->b_linep) {
44
45 /* if we are on the current line, record it */
46 if (lp == curwp->w_dotp) {
47 predlines = numlines;
48 predchars = numchars + curwp->w_doto;
49 if ((curwp->w_doto) == lused(lp))
50 curchar = '\r';
51 else
52 curchar = lgetc(lp, curwp->w_doto);
53 }
54
55 /* on to the next line */
56 ++numlines;
57 numchars += lused(lp) + 1;
58 lp = lforw(lp);
59 }
60
61 /* if at end of file, record it */
62 if (curwp->w_dotp == curbp->b_linep) {
63 predlines = numlines;
64 predchars = numchars;
65 }
66
67 /* Get real column and end-of-line column. */
68 col = getccol(FALSE);
69 savepos = curwp->w_doto;
70 curwp->w_doto = lused(curwp->w_dotp);
71 ecol = getccol(FALSE);
72 curwp->w_doto = savepos;
73
74 ratio = 0; /* Ratio before dot. */
75 if (numchars != 0)
76 ratio = (100L * predchars) / numchars;
77
78 /* summarize and report the info */
79 #if DBCS
80 if (is2byte(ltext(curwp->w_dotp),
81 ltext(curwp->w_dotp) + curwp->w_doto)) {
82 mlwrite(TEXT220,
83 /* "Line %D/%D Col %d/%d Char %D/%D (%d%%) char = 0x%x%x" */
84 predlines+1, numlines+1, col, ecol,
85 predchars, numchars, ratio, (unsigned char)curchar,
86 (unsigned char)(lgetc(curwp->w_dotp, curwp->w_doto+1)));
87 return(TRUE);
88 }
89 #endif
90 mlwrite(TEXT60,
91 /* "Line %D/%D Col %d/%d Char %D/%D (%d%%) char = 0x%x" */
92 predlines + 1, numlines + 1, col, ecol,
93 predchars, numchars, ratio, curchar);
94 return(TRUE);
95 }
96
getlinenum(bp,sline)97 long PASCAL NEAR getlinenum(bp, sline) /* get the a line number */
98
99 BUFFER *bp; /* buffer to get current line from */
100 LINE *sline; /* line to search for */
101 {
102 register LINE *lp; /* current line */
103 register long numlines; /* # of lines before point */
104
105 /* starting at the beginning of the buffer */
106 lp = lforw(bp->b_linep);
107
108 /* start counting lines */
109 numlines = 0L;
110 while (lp != bp->b_linep)
111 {
112 /* if we are on the current line, record it */
113 if (lp == sline)
114 break;
115 ++numlines;
116 lp = lforw(lp);
117 }
118
119 /* and return the resulting count */
120 return(numlines + 1L);
121 }
122
123 /*
124 * Return current column. Stop at first non-blank given TRUE argument.
125 */
126
getccol(bflg)127 PASCAL NEAR getccol(bflg)
128 int bflg;
129
130 {
131 register int i, col;
132 register unsigned char c;
133
134 col = 0;
135 for (i = 0; i < curwp->w_doto; ++i) {
136 c = lgetc(curwp->w_dotp, i) & 0xFF;
137 if (c != ' ' && c != '\t' && bflg)
138 break;
139 if (c == '\t' && tabsize > 0)
140 col += -(col % tabsize) + (tabsize - 1);
141 else {
142 if (disphigh && c > 0x7f) {
143 col += 2;
144 c -= 0x80;
145 }
146 if (c < 0x20 || c == 0x7f)
147 ++col;
148 }
149 ++col;
150 }
151 return(col);
152 }
153
154 /* findcol: Return display column in line at char position */
155
findcol(lp,pos)156 int PASCAL NEAR findcol(lp, pos)
157
158 LINE * lp; /* line to scan */
159 int pos; /* character offset */
160 {
161 register int c, i, col;
162
163 col = 0;
164 for (i = 0; i < pos; ++i) {
165 c = lgetc(lp, i);
166 if (c == '\t' && tabsize > 0)
167 col += -(col % tabsize) + (tabsize - 1);
168 else {
169 if (disphigh && c > 0x7f) {
170 col += 2;
171 c -= 0x80;
172 }
173 if (c < 0x20 || c == 0x7F)
174 ++col;
175 }
176 ++col;
177 }
178 return(col);
179 }
180
181 /*
182 * Set current column.
183 */
184
setccol(pos)185 PASCAL NEAR setccol(pos)
186
187 int pos; /* position to set cursor */
188 {
189 register int c; /* character being scanned */
190 register int i; /* index into current line */
191 register int col; /* current cursor column */
192 register int llen; /* length of line in bytes */
193
194 col = 0;
195 llen = lused(curwp->w_dotp);
196
197 /* scan the line until we are at or past the target column */
198 for (i = 0; i < llen; ++i) {
199 /* upon reaching the target, drop out */
200 if (col >= pos)
201 break;
202
203 /* advance one character */
204 c = lgetc(curwp->w_dotp, i);
205 if (c == '\t' && tabsize > 0)
206 col += -(col % tabsize) + (tabsize - 1);
207 else {
208 if (disphigh && c > 0x7f) {
209 col += 2;
210 c -= 0x80;
211 }
212 if (c < 0x20 || c == 0x7F)
213 ++col;
214 }
215 ++col;
216 }
217
218 /* set us at the new position */
219 curwp->w_doto = i;
220
221 /* and tell whether we made it */
222 return(col >= pos);
223 }
224
225 /*
226 * Twiddle the two characters on either side of dot. If dot is at the end of
227 * the line twiddle the two characters before it. Return with an error if dot
228 * is at the beginning of line; it seems to be a bit pointless to make this
229 * work. This fixes up a very common typo with a single stroke. Normally bound
230 * to "C-T". This always works within a line, so "WFEDIT" is good enough.
231 * Backward compatibility forces the save/restore of the cursor position
232 * to keep this working as it always has.
233 */
234
twiddle(f,n)235 PASCAL NEAR twiddle(f, n)
236
237 int f, n; /* prefix flag and argument */
238
239 {
240 register LINE *dotp; /* shorthand to current line pointer */
241 int saved_doto; /* restore the cursor afterwards */
242 register int cl; /* characters to swap! */
243 register int cr;
244
245 if (curbp->b_mode & MDVIEW) /* don't allow this command if */
246 return(rdonly()); /* we are in read only mode */
247
248 /* get the current position */
249 saved_doto = curwp->w_doto;
250 dotp = curwp->w_dotp;
251
252 /* get the 2 chars to swap */
253 if (curwp->w_doto == lused(dotp) && --curwp->w_doto < 0) {
254 curwp->w_doto = saved_doto;
255 return(FALSE);
256 }
257 cr = lgetc(dotp, curwp->w_doto);
258 if (--curwp->w_doto < 0) {
259 curwp->w_doto = saved_doto;
260 return(FALSE);
261 }
262 cl = lgetc(dotp, curwp->w_doto);
263
264 /* swap the characters */
265 obj.obj_char = cl;
266 lputc(dotp, curwp->w_doto, cr);
267 undo_insert(OP_REPC, 1L, obj);
268
269 curwp->w_doto++;
270 obj.obj_char = cr;
271 lputc(dotp, curwp->w_doto, cl);
272 undo_insert(OP_REPC, 1L, obj);
273
274 /* restore the cursor position */
275 curwp->w_doto = saved_doto;
276
277 /* flag the change */
278 lchange(WFEDIT);
279 return(TRUE);
280 }
281
282 /*
283 * Quote the next character, and insert it into the buffer. All the characters
284 * are taken literally, including the newline, which does not then have
285 * its line splitting meaning. The character is always read, even if it is
286 * inserted 0 times, for regularity. Bound to "C-Q". If a mouse action or
287 * function key is pressed, its symbolic MicroEMACS name gets inserted!
288 */
289
quote(f,n)290 PASCAL NEAR quote(f, n)
291
292 int f, n; /* prefix flag and argument */
293 {
294 register int ec; /* current extended key fetched */
295 register int c; /* current ascii key fetched */
296 register int status; /* return value to hold from linstr */
297 char key_name[10]; /* name of a keystroke for quoting */
298
299 if (curbp->b_mode & MDVIEW) /* don't allow this command if */
300 return(rdonly()); /* we are in read only mode */
301 ec = get_key();
302
303 /* fail this on a negative argument */
304 if (n < 0)
305 return(FALSE);
306
307 /* nothing to insert . . . blow it off */
308 if (n == 0)
309 return(TRUE);
310
311 /* if this is a mouse event or function key, put its name in */
312 if ((ec & MOUS) || (ec & SPEC))
313 {
314 cmdstr(ec, key_name);
315 while (n--)
316 {
317 status = linstr(key_name);
318 if (status != TRUE)
319 return(status);
320 }
321 return(TRUE);
322 }
323
324 /* otherwise, just insert the raw character */
325 c = ectoc(ec);
326 return(linsert(n, c));
327 }
328
329 /*
330 * Set tab size if given non-default argument (n <> 1). Otherwise, insert a
331 * tab into file. If given argument, n, of zero, change to hard tabs.
332 * If n > 1, simulate tab stop every n-characters using spaces. This has to be
333 * done in this slightly funny way because the tab (in ASCII) has been turned
334 * into "C-I" (in 10 bit code) already. Bound to "C-I".
335 */
336
tab(f,n)337 PASCAL NEAR tab(f, n)
338
339 int f, n; /* prefix flag and argument */
340 {
341 if (n < 0)
342 return(FALSE);
343 if (n == 0 || n > 1)
344 {
345 stabsize = n;
346 return(TRUE);
347 }
348 if (!stabsize)
349 return(linsert(1, '\t'));
350 return(linsert(stabsize - (getccol(FALSE) % stabsize), ' '));
351 }
352
detab(f,n)353 PASCAL NEAR detab(f, n) /* change tabs to spaces */
354
355 int f, n; /* default flag and numeric repeat count */
356 {
357 register int inc; /* increment to next line [sgn(n)] */
358
359 if (curbp->b_mode & MDVIEW) /* don't allow this command if */
360 return(rdonly()); /* we are in read only mode */
361
362 if (tabsize == 0)
363 return FALSE;
364
365 if (f == FALSE)
366 n = reglines();
367
368 /* loop thru detabbing n lines */
369 inc = ((n > 0) ? 1 : -1);
370 while (n)
371 {
372 curwp->w_doto = 0; /* start at the beginning */
373
374 /* detab the entire current line */
375 while (curwp->w_doto < lused(curwp->w_dotp))
376 {
377 /* if we have a tab */
378 if (lgetc(curwp->w_dotp, curwp->w_doto) == '\t')
379 {
380 ldelete(1L, FALSE);
381 /* insspace(TRUE, 8 - (curwp->w_doto & 7));*/
382 insspace(TRUE, tabsize - (curwp->w_doto % tabsize));
383 }
384 forwchar(FALSE, 1);
385 }
386
387 /* advance/or back to the next line */
388 forwline(TRUE, inc);
389 n -= inc;
390 }
391 curwp->w_doto = 0; /* to the begining of the line */
392 thisflag &= ~CFCPCN; /* flag that this resets the goal column */
393 lchange(WFEDIT); /* yes, we have made at least an edit */
394 return(TRUE);
395 }
396
397
entab(f,n)398 PASCAL NEAR entab(f, n) /* change spaces to tabs where posible */
399
400 int f, n; /* default flag and numeric repeat count */
401 {
402 register int inc; /* increment to next line [sgn(n)] */
403 register int fspace; /* pointer to first space if in a run */
404 register int ccol; /* current cursor column */
405 register char cchar; /* current character */
406
407 if (curbp->b_mode & MDVIEW) /* don't allow this command if */
408 return(rdonly()); /* we are in read only mode */
409
410 if (tabsize == 0)
411 return FALSE;
412
413 if (f == FALSE)
414 n = reglines();
415
416 /* loop thru entabbing n lines */
417 inc = ((n > 0) ? 1 : -1);
418 while (n)
419 {
420 /* entab the entire current line */
421
422 ccol = curwp->w_doto = 0; /* start at the beginning */
423 fspace = -1;
424
425 while (curwp->w_doto < lused(curwp->w_dotp))
426 {
427 /* see if it is time to compress */
428 if ((fspace >= 0) && (nextab(fspace) <= ccol))
429 if (ccol - fspace < 2)
430 fspace = -1;
431 else {
432 backchar(TRUE, ccol - fspace);
433 ldelete((long) (ccol - fspace), FALSE);
434 linsert(1, '\t');
435 fspace = -1;
436 }
437
438 /* get the current character */
439 cchar = lgetc(curwp->w_dotp, curwp->w_doto);
440
441 switch (cchar)
442 {
443 case '\t': /* a tab...count em up (no break here) */
444 ldelete(1L, FALSE);
445 insspace(TRUE, tabsize - (ccol % tabsize));
446
447 case ' ': /* a space...compress? */
448 if (fspace == -1)
449 fspace = ccol;
450 break;
451
452 default: /* any other char...just count */
453 fspace = -1;
454 break;
455 }
456 ccol++;
457 forwchar(FALSE, 1);
458 }
459
460 /* advance/or back to the next line */
461 forwline(TRUE, inc);
462 n -= inc;
463 curwp->w_doto = 0; /* start at the beginning */
464 }
465 curwp->w_doto = 0; /* to the begining of the line */
466 thisflag &= ~CFCPCN; /* flag that this resets the goal column */
467 lchange(WFEDIT); /* yes, we have made at least an edit */
468 return(TRUE);
469 }
470
471 /* trim: trim trailing whitespace from the point to eol
472 with no arguments, it trims the current region
473 */
474
trim(f,n)475 PASCAL NEAR trim(f, n)
476
477 int f, n; /* default flag and numeric repeat count */
478 {
479 register LINE *lp; /* current line pointer */
480 register int offset; /* original line offset position */
481 register int length; /* current length */
482 register int inc; /* increment to next line [sgn(n)] */
483
484 if (curbp->b_mode & MDVIEW) /* don't allow this command if */
485 return(rdonly()); /* we are in read only mode */
486
487 if (f == FALSE)
488 n = reglines();
489
490 /* loop thru trimming n lines */
491 inc = ((n > 0) ? 1 : -1);
492 while (n)
493 {
494 lp = curwp->w_dotp; /* find current line text */
495 offset = curwp->w_doto; /* save original offset */
496 length = lused(lp); /* find current length */
497
498 /* trim the current line */
499 while (length > offset)
500 {
501 if (lgetc(lp, length-1) != ' ' &&
502 lgetc(lp, length-1) != '\t')
503 break;
504 length--;
505 }
506 lp->l_used = length;
507
508 /* advance/or back to the next line */
509 forwline(TRUE, inc);
510 n -= inc;
511 }
512 lchange(WFEDIT);
513 thisflag &= ~CFCPCN; /* flag that this resets the goal column */
514 return(TRUE);
515 }
516
517 /*
518 * Open up some blank space. The basic plan is to insert a bunch of newlines,
519 * and then back up over them. Everything is done by the subcommand
520 * procerssors. They even handle the looping. Normally this is bound to "C-O".
521 */
522
openline(f,n)523 PASCAL NEAR openline(f, n)
524
525 int f, n; /* prefix flag and argument */
526 {
527 register int i;
528 register int s;
529
530 if (curbp->b_mode & MDVIEW) /* don't allow this command if */
531 return(rdonly()); /* we are in read only mode */
532 if (n < 0)
533 return(FALSE);
534 if (n == 0)
535 return(TRUE);
536 i = n; /* Insert newlines. */
537 do {
538 s = lnewline();
539 }
540 while (s == TRUE && --i);
541 if (s == TRUE) /* Then back up overtop */
542 s = backchar(f, n); /* of them all. */
543 return(s);
544 }
545
546 /*
547 * Insert a newline. Bound to "C-M". If we are in CMODE, do automatic
548 * indentation as specified.
549 */
550
newline(f,n)551 PASCAL NEAR newline(f, n)
552
553 int f, n; /* prefix flag and argument */
554 {
555 register int s;
556
557 if (curbp->b_mode & MDVIEW) /* don't allow this command if */
558 return(rdonly()); /* we are in read only mode */
559 if (n < 0)
560 return(FALSE);
561
562 /* if we are in C mode and this is a default <NL> */
563 if (n == 1 && (curbp->b_mode & MDCMOD) &&
564 curwp->w_dotp != curbp->b_linep)
565 return(cinsert());
566
567 /*
568 * If a newline was typed, fill column is defined, the argument is non-
569 * negative, wrap mode is enabled, and we are now past fill column,
570 * and we are not read-only, perform word wrap.
571 */
572 if ((curwp->w_bufp->b_mode & MDWRAP) && fillcol > 0 &&
573 getccol(FALSE) > fillcol &&
574 (curwp->w_bufp->b_mode & MDVIEW) == FALSE)
575 execkey(&wraphook, FALSE, 1);
576
577 /* insert some lines */
578 while (n--)
579 {
580 if ((s = lnewline()) != TRUE)
581 return(s);
582 }
583 return(TRUE);
584 }
585
cinsert()586 PASCAL NEAR cinsert() /* insert a newline and indentation for C */
587
588 {
589 register char *cptr; /* string pointer into text to copy */
590 register int i; /* index into line to copy indent from */
591 register int llen; /* length of line to copy indent from */
592 register int bracef; /* was there a brace at the end of line? */
593 register LINE *lp; /* current line pointer */
594 register int offset;
595 char ichar[NSTRING]; /* buffer to hold indent of last line */
596
597 /* trim the whitespace before the point */
598 lp = curwp->w_dotp;
599 offset = curwp->w_doto;
600 while (offset > 0 &&
601 (lgetc(lp, offset - 1) == ' ' ||
602 lgetc(lp, offset - 1) == '\t'))
603 {
604 backdel(FALSE, 1);
605 offset--;
606 }
607
608 /* check for a brace */
609 bracef = (offset > 0 && lgetc(lp, offset - 1) == '{');
610
611 /* put in the newline */
612 if (lnewline() == FALSE)
613 return(FALSE);
614
615 /* if the new line is not blank... don't indent it! */
616 lp = curwp->w_dotp;
617 if (lused(lp) != 0)
618 return(TRUE);
619
620 /* hunt for the last non-blank line to get indentation from */
621 while (lused(lp) == 0 && lp != curbp->b_linep)
622 lp = lback(lp);
623
624 /* grab a pointer to text to copy indentation from */
625 cptr = ltext(lp);
626 llen = lused(lp);
627
628 /* save the indent of the last non blank line */
629 i = 0;
630 while ((i < llen) && (cptr[i] == ' ' || cptr[i] == '\t')
631 && (i < NSTRING - 1))
632 {
633 ichar[i] = cptr[i];
634 ++i;
635 }
636 ichar[i] = 0; /* terminate it */
637
638 /* insert this saved indentation */
639 linstr(ichar);
640
641 /* and one more tab for a brace */
642 if (bracef)
643 tab(FALSE, 1);
644
645 return(TRUE);
646 }
647
insbrace(n,c)648 PASCAL NEAR insbrace(n, c) /* insert a brace into the text here...we are in CMODE */
649
650 int n; /* repeat count */
651 int c; /* brace to insert (always } for now) */
652 {
653 register int ch; /* last character before input */
654 register int oc; /* caractere oppose a c */
655 register int i, count;
656 register int target; /* column brace should go after */
657 register LINE *oldlp;
658 register int oldoff;
659
660 /* if we aren't at the beginning of the line... */
661 if (curwp->w_doto != 0)
662
663 /* scan to see if all space before this is white space */
664 for (i = curwp->w_doto - 1; i >= 0; --i)
665 {
666 ch = lgetc(curwp->w_dotp, i);
667 if (ch != ' ' && ch != '\t')
668 return(linsert(n, c));
669 }
670
671 /* chercher le caractere oppose correspondant */
672 switch (c)
673 {
674 case '}':
675 oc = '{';
676 break;
677 case ']':
678 oc = '[';
679 break;
680 case ')':
681 oc = '(';
682 break;
683 default:
684 return(FALSE);
685 }
686
687 oldlp = curwp->w_dotp;
688 oldoff = curwp->w_doto;
689
690 count = 1;
691 backchar(FALSE, 1);
692
693 while (count > 0)
694 {
695 if (curwp->w_doto == lused(curwp->w_dotp))
696 ch = '\r';
697 else
698 ch = lgetc(curwp->w_dotp, curwp->w_doto);
699
700 if (ch == c)
701 ++count;
702 if (ch == oc)
703 --count;
704
705 backchar(FALSE, 1);
706 if (boundry(curwp->w_dotp, curwp->w_doto, REVERSE))
707 break;
708 }
709
710 if (count != 0)
711 { /* no match */
712 curwp->w_dotp = oldlp;
713 curwp->w_doto = oldoff;
714 return(linsert(n, c));
715 }
716
717 curwp->w_doto = 0; /* debut de ligne */
718 /* aller au debut de la ligne apres la tabulation */
719 while ((ch = lgetc(curwp->w_dotp, curwp->w_doto)) == ' ' || ch == '\t')
720 forwchar(FALSE, 1);
721
722 /* delete back first */
723 target = getccol(FALSE); /* c'est l'indent que l'on doit avoir */
724 curwp->w_dotp = oldlp;
725 curwp->w_doto = oldoff;
726
727 while (target != getccol(FALSE))
728 {
729 if (target < getccol(FALSE)) /* on doit detruire des caracteres */
730 while (getccol(FALSE) > target)
731 backdel(FALSE, 1);
732 else { /* on doit en inserer */
733 if (tabsize > 0)
734 while (target - getccol(FALSE) >= tabsize)
735 linsert(1, '\t');
736 linsert(target - getccol(FALSE), ' ');
737 }
738 }
739
740 /* and insert the required brace(s) */
741 return(linsert(n, c));
742 }
743
inspound()744 PASCAL NEAR inspound() /* insert a # into the text here...we are in CMODE */
745
746 {
747 register int ch; /* last character before input */
748 register int i;
749
750 /* if we are at the beginning of the line, no go */
751 if (curwp->w_doto == 0)
752 return(linsert(1, '#'));
753
754 /* scan to see if all space before this is white space */
755 for (i = curwp->w_doto - 1; i >= 0; --i)
756 {
757 ch = lgetc(curwp->w_dotp, i);
758 if (ch != ' ' && ch != '\t')
759 return(linsert(1, '#'));
760 }
761
762 /* delete back first */
763 while (getccol(FALSE) >= 1)
764 backdel(FALSE, 1);
765
766 /* and insert the required pound */
767 return(linsert(1, '#'));
768 }
769
770 /*
771 * Delete blank lines around dot. What this command does depends if dot is
772 * sitting on a blank line. If dot is sitting on a blank line, this command
773 * deletes all the blank lines above and below the current line. If it is
774 * sitting on a non blank line then it deletes all of the blank lines after
775 * the line. Normally this command is bound to "C-X C-O". Any argument is
776 * ignored.
777 */
778
deblank(f,n)779 PASCAL NEAR deblank(f, n)
780
781 int f, n; /* prefix flag and argument */
782 {
783 register LINE *lp1;
784 register LINE *lp2;
785 long nld;
786
787 if (curbp->b_mode & MDVIEW) /* don't allow this command if */
788 return(rdonly()); /* we are in read only mode */
789 lp1 = curwp->w_dotp;
790 while (lused(lp1) == 0 && (lp2 = lback(lp1)) != curbp->b_linep)
791 lp1 = lp2;
792 lp2 = lp1;
793 nld = 0;
794 while ((lp2 = lforw(lp2)) != curbp->b_linep && lused(lp2) == 0)
795 ++nld;
796 if (nld == 0)
797 return(TRUE);
798 curwp->w_dotp = lforw(lp1);
799 curwp->w_doto = 0;
800 return(ldelete(nld, FALSE));
801 }
802
803 /*
804 * Insert a newline, then enough tabs and spaces to duplicate the indentation
805 * of the previous line. Tabs are every tabsize characters. Quite simple.
806 * Figure out the indentation of the current line. Insert a newline by calling
807 * the standard routine. Insert the indentation by inserting the right number
808 * of tabs and spaces. Return TRUE if all ok. Return FALSE if one of the
809 * subcomands failed. Normally bound to "C-J".
810 */
811
indent(f,n)812 PASCAL NEAR indent(f, n)
813
814 int f, n; /* prefix flag and argument */
815 {
816 register int nicol;
817 register int c;
818 register int i;
819
820 if (curbp->b_mode & MDVIEW) /* don't allow this command if */
821 return(rdonly()); /* we are in read only mode */
822 if (n < 0)
823 return(FALSE);
824 while (n--)
825 {
826 nicol = 0;
827 for (i = 0; i < lused(curwp->w_dotp); ++i)
828 {
829 c = lgetc(curwp->w_dotp, i);
830 if (c != ' ' && c != '\t')
831 break;
832 if (c == '\t')
833 if (tabsize > 0)
834 nicol += -(nicol % tabsize) + (tabsize - 1);
835 else
836 break;
837 ++nicol;
838 }
839 if (lnewline() == FALSE)
840 return FALSE;
841 if (tabsize == 0) {
842 if (linsert(nicol, ' ') == FALSE)
843 return(FALSE);
844 }
845 else
846 if (((i = nicol / tabsize) != 0 && linsert(i, '\t') == FALSE)
847 || ((i = nicol % tabsize) != 0 && linsert(i, ' ') == FALSE))
848 return(FALSE);
849 }
850 return(TRUE);
851 }
852
853 /*
854 * Delete forward. This is real easy, because the basic delete routine does
855 * all of the work. Watches for negative arguments, and does the right thing.
856 * If any argument is present, it kills rather than deletes, to prevent loss
857 * of text if typed with a big argument. Normally bound to "C-D".
858 */
859
forwdel(f,n)860 PASCAL NEAR forwdel(f, n)
861
862 int f, n; /* prefix flag and argument */
863
864 {
865 /* Don't allow this in read-only mode */
866 if (curbp->b_mode & MDVIEW)
867 return(rdonly());
868
869 /* with a negative argument, this is a backwards delete */
870 if (n < 0)
871 return(backdel(f, -n));
872
873 /* with an argument, flag this to go to the kill buffer! */
874 if (f != FALSE) {
875 if ((lastflag & CFKILL) == 0)
876 next_kill();
877 thisflag |= CFKILL;
878 }
879 return(ldelete((long)n, f));
880 }
881
882 /*
883 * Delete backwards. This is quite easy too, because it's all done with other
884 * functions. Just move the cursor back, and delete forwards. Like delete
885 * forward, this actually does a kill if presented with an argument. Bound to
886 * both "RUBOUT" and "C-H".
887 */
888
backdel(f,n)889 PASCAL NEAR backdel(f, n)
890
891 int f, n; /* prefix flag and argument */
892
893 {
894 register int status;
895
896 /* Don't do this command in read-only mode */
897 if (curbp->b_mode & MDVIEW)
898 return(rdonly());
899
900 /* with a negative argument, this becomes a delete forward */
901 if (n < 0)
902 return(forwdel(f, -n));
903
904 /* with an argument, flag this to go to the kill buffer! */
905 if (f != FALSE) {
906 if ((lastflag & CFKILL) == 0)
907 next_kill();
908 thisflag |= CFKILL;
909 }
910
911 /* make sure the cursor gets back to the right place on an undo */
912 undo_insert(OP_CPOS, 0L, obj);
913
914 /* and now delete the characters */
915 if ((status = backchar(f, n)) == TRUE)
916 status = ldelete((long) n, f);
917 return(status);
918 }
919
920 /*
921 * Kill text. If called without an argument, it kills from dot to the end of
922 * the line, unless it is at the end of the line, when it kills the newline.
923 * If called with an argument of 0, it kills from the start of the line to dot.
924 * If called with a positive argument, it kills from dot forward over that
925 * number of newlines. If called with a negative argument it kills backwards
926 * that number of newlines. Normally bound to "C-K".
927 */
928
killtext(f,n)929 PASCAL NEAR killtext(f, n)
930
931 int f, n; /* prefix flag and argument */
932
933 {
934 register LINE *nextp;
935 long chunk;
936
937 /* Don't do this command in read-only mode */
938 if (curbp->b_mode & MDVIEW)
939 return(rdonly());
940
941 /* flag this as a kill */
942 if ((lastflag & CFKILL) == 0)
943 next_kill();
944 thisflag |= CFKILL;
945
946 /* make sure the cursor gets back to the right place on an undo */
947 undo_insert(OP_CPOS, 0L, obj);
948
949 if (f == FALSE) {
950 chunk = lused(curwp->w_dotp) - curwp->w_doto;
951 if (chunk == 0)
952 chunk = 1;
953 } else if (n == 0) {
954 chunk = -curwp->w_doto;
955 } else if (n > 0) {
956 chunk = lused(curwp->w_dotp) - curwp->w_doto + 1;
957 nextp = lforw(curwp->w_dotp);
958 while (--n) {
959 if (nextp == curbp->b_linep)
960 return(FALSE);
961 chunk += lused(nextp) + 1;
962 nextp = lforw(nextp);
963 }
964 } else if (n < 0) {
965 chunk = -curwp->w_doto;
966 nextp = lback(curwp->w_dotp);
967 while (n++) {
968 if (nextp == curbp->b_linep)
969 return(FALSE);
970 chunk -= lused(nextp) + 1;
971 nextp = lback(nextp);
972 }
973 }
974 return(ldelete(chunk, TRUE));
975 }
976
setmod(f,n)977 PASCAL NEAR setmod(f, n) /* prompt and set an editor mode */
978
979 int f, n; /* default and argument */
980 {
981 return(adjustmode(TRUE, FALSE));
982 }
983
delmode(f,n)984 PASCAL NEAR delmode(f, n) /* prompt and delete an editor mode */
985
986 int f, n; /* default and argument */
987 {
988 return(adjustmode(FALSE, FALSE));
989 }
990
setgmode(f,n)991 PASCAL NEAR setgmode(f, n) /* prompt and set a global editor mode */
992
993 int f, n; /* default and argument */
994 {
995 return(adjustmode(TRUE, TRUE));
996 }
997
delgmode(f,n)998 PASCAL NEAR delgmode(f, n) /* prompt and delete a global editor mode */
999
1000 int f, n; /* default and argument */
1001 {
1002 return(adjustmode(FALSE, TRUE));
1003 }
1004
adjustmode(kind,global)1005 PASCAL NEAR adjustmode(kind, global) /* change the editor mode status */
1006
1007 int kind; /* true = set, false = delete */
1008 int global; /* true = global flag, false = current buffer flag */
1009 {
1010 register char *scan; /* scanning pointer to convert prompt */
1011 register int i; /* loop index */
1012 register int status; /* error return on input */
1013 #if COLOR
1014 register int uflag; /* was modename uppercase? */
1015 #endif
1016 char prompt[50]; /* string to prompt user with */
1017 char cbuf[NPAT]; /* buffer to recieve mode name into */
1018
1019 /* build the proper prompt string */
1020 if (global)
1021 strcpy(prompt, TEXT62);
1022 /* "Global mode to " */
1023 else
1024 strcpy(prompt, TEXT63);
1025 /* "Mode to " */
1026
1027 if (kind == TRUE)
1028 strcat(prompt, TEXT64);
1029 /* "add: " */
1030 else
1031 strcat(prompt, TEXT65);
1032 /* "delete: " */
1033
1034 /* prompt the user and get an answer */
1035
1036 status = mlreply(prompt, cbuf, NPAT - 1);
1037 if (status != TRUE)
1038 return(status);
1039
1040 /* make it uppercase */
1041
1042 scan = cbuf;
1043 #if COLOR
1044 uflag = (*scan >= 'A' && *scan <= 'Z');
1045 #endif
1046 while (*scan)
1047 uppercase((unsigned char *) scan++);
1048
1049 /* test it first against the colors we know */
1050 if ((i = lookup_color(cbuf)) != -1)
1051 {
1052
1053 #if COLOR
1054 /* finding the match, we set the color */
1055 if (global)
1056 {
1057 if (uflag)
1058 gfcolor = i;
1059 else
1060 gbcolor = i;
1061 #if WINDOW_TEXT & 0
1062 refresh_screen(first_screen);
1063 #endif
1064 }
1065 else
1066 if (uflag)
1067 curwp->w_fcolor = i;
1068 else
1069 curwp->w_bcolor = i;
1070
1071 curwp->w_flag |= WFCOLR;
1072 #endif
1073 mlerase();
1074 return(TRUE);
1075 }
1076
1077 /* test it against the modes we know */
1078
1079 for (i = 0; i < NUMMODES; i++)
1080 {
1081 if (strcmp(cbuf, modename[i]) == 0)
1082 {
1083 /* finding a match, we process it */
1084 if (kind == TRUE)
1085 if (global)
1086 {
1087 gmode |= (1 << i);
1088 if ((1 << i) == MDOVER)
1089 gmode &= ~MDREPL;
1090 else if ((1 << i) == MDREPL)
1091 gmode &= ~MDOVER;
1092 }
1093 else {
1094 curbp->b_mode |= (1 << i);
1095 if ((1 << i) == MDOVER)
1096 curbp->b_mode &= ~MDREPL;
1097 else if ((1 << i) == MDREPL)
1098 curbp->b_mode &= ~MDOVER;
1099 }
1100 else
1101 if (global)
1102 gmode &= ~(1 << i);
1103 else
1104 curbp->b_mode &= ~(1 << i);
1105 /* display new mode line */
1106 if (global == 0)
1107 upmode();
1108 mlerase(); /* erase the junk */
1109 return(TRUE);
1110 }
1111 }
1112
1113 mlwrite(TEXT66);
1114 /* "No such mode!" */
1115 return(FALSE);
1116 }
1117
1118 /* This function simply clears the message line,
1119 mainly for macro usage */
1120
clrmes(f,n)1121 PASCAL NEAR clrmes(f, n)
1122
1123 int f, n; /* arguments ignored */
1124 {
1125 mlforce("");
1126 return(TRUE);
1127 }
1128
1129 /* This function writes a string on the message line
1130 mainly for macro usage */
1131
writemsg(f,n)1132 PASCAL NEAR writemsg(f, n)
1133
1134 int f, n; /* arguments ignored */
1135 {
1136 register int status;
1137 char buf[NPAT]; /* buffer to recieve message into */
1138
1139 if ((status = mlreply(TEXT67, buf, NPAT - 1)) != TRUE)
1140 /* "Message to write: " */
1141 return(status);
1142
1143 /* write the message out */
1144 mlforce(buf);
1145 return(TRUE);
1146 }
1147
1148 /* the cursor is moved to a matching fence */
1149
getfence(f,n)1150 PASCAL NEAR getfence(f, n)
1151
1152 int f, n; /* not used */
1153 {
1154 register LINE *oldlp; /* original line pointer */
1155 register int oldoff; /* and offset */
1156 register int sdir; /* direction of search (1/-1) */
1157 register int count; /* current fence level count */
1158 register char ch; /* fence type to match against */
1159 register char ofence; /* open fence */
1160 register char c; /* current character in scan */
1161
1162 /* save the original cursor position */
1163 oldlp = curwp->w_dotp;
1164 oldoff = curwp->w_doto;
1165
1166 /* get the current character */
1167 if (oldoff == lused(oldlp))
1168 ch = '\r';
1169 else
1170 ch = lgetc(oldlp, oldoff);
1171
1172 /* setup proper matching fence */
1173 switch (ch)
1174 {
1175 case '(':
1176 ofence = ')';
1177 sdir = FORWARD;
1178 break;
1179 case '{':
1180 ofence = '}';
1181 sdir = FORWARD;
1182 break;
1183 case '[':
1184 ofence = ']';
1185 sdir = FORWARD;
1186 break;
1187 case ')':
1188 ofence = '(';
1189 sdir = REVERSE;
1190 break;
1191 case '}':
1192 ofence = '{';
1193 sdir = REVERSE;
1194 break;
1195 case ']':
1196 ofence = '[';
1197 sdir = REVERSE;
1198 break;
1199 default:
1200 TTbeep();
1201 return(FALSE);
1202 }
1203
1204 /* set up for scan */
1205 count = 1;
1206
1207 /* scan until we find it, or reach the end of file */
1208 while (count > 0)
1209 {
1210 if (sdir == FORWARD)
1211 forwchar(FALSE, 1);
1212 else
1213 backchar(FALSE, 1);
1214
1215 if (curwp->w_doto == lused(curwp->w_dotp))
1216 c = '\r';
1217 else
1218 c = lgetc(curwp->w_dotp, curwp->w_doto);
1219 if (c == ch)
1220 ++count;
1221 if (c == ofence)
1222 --count;
1223 if (boundry(curwp->w_dotp, curwp->w_doto, sdir))
1224 break;
1225 }
1226
1227 /* if count is zero, we have a match, move the sucker */
1228 if (count == 0)
1229 {
1230 curwp->w_flag |= WFMOVE;
1231 return(TRUE);
1232 }
1233
1234 /* restore the current position */
1235 curwp->w_dotp = oldlp;
1236 curwp->w_doto = oldoff;
1237 TTbeep();
1238 return(FALSE);
1239 }
1240
1241 /* Close fences are matched against their partners, and if
1242 on screen the cursor briefly lights there */
1243
1244 #if PROTO
fmatch(char ch)1245 PASCAL NEAR fmatch(char ch)
1246 #else
1247 PASCAL NEAR fmatch(ch)
1248
1249 char ch; /* fence type to match against */
1250 #endif
1251
1252 {
1253 register LINE *oldlp; /* original line pointer */
1254 register int oldoff; /* and offset */
1255 register LINE *toplp; /* top line in current window */
1256 register int count; /* current fence level count */
1257 register char opench; /* open fence */
1258 register char c; /* current character in scan */
1259 register int i;
1260
1261 /* first get the display update out there */
1262 update(FALSE);
1263
1264 /* save the original cursor position */
1265 oldlp = curwp->w_dotp;
1266 oldoff = curwp->w_doto;
1267
1268 /* setup proper open fence for passed close fence */
1269 if (ch == ')')
1270 opench = '(';
1271 else if (ch == '}')
1272 opench = '{';
1273 else
1274 opench = '[';
1275
1276 /* find the top line and set up for scan */
1277 toplp = lback(curwp->w_linep);
1278 count = 1;
1279 backchar(FALSE, 1);
1280
1281 /* scan back until we find it, or reach past the top of the window */
1282 while (count > 0 && curwp->w_dotp != toplp)
1283 {
1284 backchar(FALSE, 1);
1285 if (curwp->w_doto == lused(curwp->w_dotp))
1286 c = '\r';
1287 else
1288 c = lgetc(curwp->w_dotp, curwp->w_doto);
1289 if (c == ch)
1290 ++count;
1291 if (c == opench)
1292 --count;
1293 if (curwp->w_dotp == lforw(curwp->w_bufp->b_linep) &&
1294 curwp->w_doto == 0)
1295 break;
1296 }
1297
1298 /* if count is zero, we have a match, display the sucker */
1299 /* there is a real machine dependant timing problem here we have
1300 yet to solve......... */
1301 if (count == 0)
1302 {
1303 #if WINDOW_MSWIN
1304 update(FALSE);
1305 term.t_sleep (term.t_pause);
1306 #else
1307 for (i = 0; i < term.t_pause; i++)
1308 update(FALSE);
1309 #endif
1310 }
1311
1312 /* restore the current position */
1313 curwp->w_dotp = oldlp;
1314 curwp->w_doto = oldoff;
1315 return(TRUE);
1316 }
1317
1318 /* ask for and insert a string into the current
1319 buffer at the current point */
1320
istring(f,n)1321 PASCAL NEAR istring(f, n)
1322
1323 int f, n; /* ignored arguments */
1324
1325 {
1326 register int status; /* status return code */
1327 char tstring[NPAT + 1]; /* string to add */
1328
1329 /* ask for string to insert */
1330 status = nextarg(TEXT68, tstring, NPAT, sterm);
1331 /* "String to insert: " */
1332 if (status != TRUE)
1333 return(status);
1334
1335 if (f == FALSE)
1336 n = 1;
1337
1338 if (n < 0)
1339 n = -n;
1340
1341 /* insert it */
1342 while (n-- && (status = linstr(tstring)))
1343 ;
1344 return(status);
1345 }
1346
ovstring(f,n)1347 PASCAL NEAR ovstring(f, n) /* ask for and overwite a string into the current
1348 buffer at the current point */
1349
1350 int f, n; /* ignored arguments */
1351 {
1352 register int status; /* status return code */
1353 char tstring[NPAT + 1]; /* string to add */
1354
1355 /* ask for string to insert */
1356 status = nextarg(TEXT69, tstring, NPAT, sterm);
1357 /* "String to overwrite: " */
1358 if (status != TRUE)
1359 return(status);
1360
1361 if (f == FALSE)
1362 n = 1;
1363
1364 if (n < 0)
1365 n = -n;
1366
1367 /* insert it */
1368 while (n-- && (status = lover(tstring)))
1369 ;
1370 return(status);
1371 }
1372
lookup_color(sp)1373 int PASCAL NEAR lookup_color(sp)
1374
1375 char *sp; /* name to look up */
1376 {
1377 register int i; /* index into color list */
1378
1379 /* test it against the colors we know */
1380 for (i = 0; i < NCOLORS; i++)
1381 {
1382 if (strcmp(sp, cname[i]) == 0)
1383 return(i);
1384 }
1385 return(-1);
1386 }
1387