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