1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <signal.h>
4 #include <sys/ioctl.h>
5 #if HAVE_UNISTD_H
6 #   include <unistd.h>
7 #endif
8 #if HAVE_SYS_WAIT_H
9 #   include <sys/wait.h>
10 #endif
11 #include "deco.h"
12 #include "scr.h"
13 #include "dir.h"
14 #include "env.h"
15 
16 #define SWAP(a,b) { register t; t = a; a = b; b = t; }
17 
18 #define HISTSZ 16                       /* length of history */
19 
20 char command [CMDLEN];                  /* command string */
21 
22 static char *history [HISTSZ];          /* previous executed commands */
23 static hpos;                            /* history pointer */
24 static char *prompt;
25 
26 static struct {
27 	char *command;
28 	int pid;
29 } job [HISTSZ];                         /* background jobs */
30 
31 static int insname (char *name);
32 static void erasecmd (int plen);
33 static int mycmd (int scr);
34 static int mycmd2 (int scr);
35 static char *getpar (char *s);
36 static int esclen (char *str);
37 static void putescstr (char *str, int maxlen);
38 static void wresccmd (char *str, int n);
39 static void wrescchar (int c);
40 static void wrescback (int c);
41 static int inputcmd (int plen);
42 
drawcmd()43 void drawcmd ()
44 {
45 	int c;
46 	CURSOR cursor;
47 
48 	if (H==LINES-7 && strlen (cur->shortcwd) + 3 + esclen (command) > 79) {
49 		fullscreen (0, 0);
50 		draw ();
51 	}
52 	for (c=H+5; c<LINES-1; ++c) {
53 		VMove (c, 0);
54 		VClearLine ();
55 	}
56 	VSetDim ();
57 	VMPutString (H+5, 0, cur->shortcwd);
58 	VSetBold ();
59 	VPutString (" > ");
60 	VSetNormal ();
61 	cursor = VGetCursor ();
62 	putescstr (command, 0);
63 	if ((c = command [cpos])) {
64 		VSetCursor (cursor);
65 		command [cpos] = 0;
66 		putescstr (command, 0);
67 		command [cpos] = c;
68 	}
69 }
70 
insname(char * name)71 static int insname (char *name)
72 {
73 	register char *s;
74 
75 	for (s=name; *s; ++s) {
76 		switch (*s) {
77 		case ' ': case '\\': case '?': case '*': case '|':
78 		case '&': case '[': case ']': case '(': case ')':
79 		case '$': case '\'': case '"': case '<': case '>':
80 		case ';': case '`': case '!':
81 			inscmd ('\\');
82 		}
83 		inscmd (*s);
84 		if (cpos > CMDLEN-2)
85 			return (0);
86 	}
87 	inscmd (' ');
88 	return (1);
89 }
90 
namecmd()91 void namecmd ()
92 {
93 	register i, savepos;
94 
95 	if (command [cpos])
96 		return;
97 	savepos = cpos;
98 	if (tagged ()) {
99 		for (i=0; i<cur->num; ++i)
100 			if (cur->cat[i].tag && ! insname (cur->cat[i].name))
101 				goto fail;
102 	} else
103 		if (! insname (cur->cat[cur->curfile].name)) {
104 fail:                   command [cpos = savepos] = 0;
105 			return;
106 		}
107 }
108 
inscmd(int key)109 void inscmd (int key)
110 {
111 	register k;
112 
113 	for (k=cpos; command[k]; ++k)
114 		SWAP (key, command[k]);
115 	if (k < CMDLEN-1) {
116 		command [k] = key;
117 		command [k+1] = 0;
118 	}
119 	rightcmd ();
120 }
121 
delcmd()122 void delcmd ()
123 {
124 	register k;
125 
126 	if (! command [cpos])
127 		return;
128 	for (k=cpos+1; command[k]; ++k)
129 		command[k-1] = command[k];
130 	command [k-1] = 0;
131 }
132 
homecmd()133 void homecmd ()
134 {
135 	cpos = 0;
136 }
137 
endcmd()138 void endcmd ()
139 {
140 	while (command [cpos])
141 		++cpos;
142 }
143 
rightcmd()144 void rightcmd ()
145 {
146 	if (command [cpos]) {
147 		++cpos;
148 		if (cpos >= CMDLEN)
149 			cpos = CMDLEN-1;
150 	}
151 }
152 
leftcmd()153 void leftcmd ()
154 {
155 	if (--cpos < 0)
156 		cpos = 0;
157 }
158 
upcmd()159 void upcmd ()
160 {
161 	while (cpos > 0 && command [--cpos] == ' ');
162 	if (cpos <= 0)
163 		return;
164 	while (cpos > 0 && command [--cpos] != ' ');
165 	if (command [cpos] == ' ')
166 		++cpos;
167 }
168 
downcmd()169 void downcmd ()
170 {
171 	while (command [cpos] && command [++cpos] == ' ');
172 	if (! command [cpos])
173 		return;
174 	while (command [cpos] && command [++cpos] != ' ');
175 	if (command [cpos] == ' ')
176 		--cpos;
177 }
178 
nextcmd()179 void nextcmd ()
180 {
181 	if (hpos <= 0) {
182 		hpos = -1;
183 		command [cpos = 0] = 0;
184 		return;
185 	}
186 	--hpos;
187 	strcpy (command, history [hpos]);
188 	for (cpos=0; command[cpos]; ++cpos);
189 }
190 
prevcmd()191 void prevcmd ()
192 {
193 	command [cpos = 0] = 0;
194 	if (! history [0])
195 		return;
196 	if (hpos >= HISTSZ)
197 		return;
198 	if (hpos >= 0 && ! history [hpos])
199 		return;
200 	++hpos;
201 	if (hpos >= HISTSZ)
202 		return;
203 	if (! history [hpos])
204 		return;
205 	strcpy (command, history [hpos]);
206 	for (cpos=0; command[cpos]; ++cpos);
207 }
208 
histcmd()209 void histcmd ()         /* store command in history */
210 {
211 	register n;
212 
213 	/* find repeated command */
214 	for (n=0; n<HISTSZ && history[n]; ++n) {
215 		if (*history[n] == *command &&
216 		    ! strcmp (history[n], command)) {
217 			register k;
218 			char *p;
219 
220 			p = history [n];
221 			for (k=n; k>0; --k)
222 				history [k] = history [k-1];
223 			history [0] = p;
224 			return;
225 		}
226 	}
227 
228 	/* forget oldest command */
229 	if (history [HISTSZ-1]) {
230 		free (history [HISTSZ-1]);
231 		history [HISTSZ-1] = 0;
232 	}
233 	/* shift the rest */
234 	for (n=0; history[n]; ++n);
235 	while (--n >= 0)
236 		history [n+1] = history [n];
237 
238 	/* create copy of command */
239 	history [0] = mdup (command);
240 }
241 
jobcmd(int pid,char * cmd)242 void jobcmd (int pid, char *cmd)        /* store command in job list */
243 {
244 	register n;
245 
246 	/* forget oldest command */
247 	if (job[HISTSZ-1].command) {
248 		free (job[HISTSZ-1].command);
249 		job[HISTSZ-1].command = 0;
250 	}
251 	/* shift the rest */
252 	for (n=0; job[n].command; ++n)
253 		continue;
254 	while (--n >= 0)
255 		job[n+1] = job[n];
256 
257 	/* create copy of command */
258 	job[0].command = mdup (cmd);
259 	job[0].pid = pid;
260 }
261 
outprompt()262 void outprompt ()
263 {
264 	write (1, prompt, (unsigned) strlen (prompt));
265 }
266 
execmd(int hit,int savhist)267 void execmd (int hit, int savhist)
268 {
269 	char pr [256];
270 	char buf [256];
271 
272 	/* run command */
273 	if (savhist)
274 		histcmd ();     /* save it in history */
275 	hpos = -1;              /* reset history pointer */
276 	if (mycmd (1)) {        /* exec built in commands */
277 		command [cpos = 0] = 0;
278 		return;
279 	}
280 	VClear ();              /* clear screen */
281 	endcmd ();              /* move cursor to the end of command */
282 	drawcmd ();             /* draw comand */
283 	VSync ();
284 	VRestore ();            /* restore terminal */
285 	write (1, "\n", 1);     /* new line */
286 	if (! mycmd2 (1))       /* exec built in commands */
287 		syscmd (command);       /* execute command */
288 	if (hit) {
289 		sprintf (buf, "\7\1%s\3 > \2", cur->shortcwd);
290 		VExpandString (buf, pr);
291 		prompt = pr;
292 		VReopen ();
293 		command [cpos = 0] = 0;
294 		for (;;) {
295 			if (! inputcmd (strlen (cur->shortcwd) + 3) || ! command [0])
296 				break;
297 			histcmd ();
298 			hpos = -1;
299 			if (mycmd (0) || mycmd2 (0)) {
300 				sprintf (buf, "\7\1%s\3 > \2", cur->shortcwd);
301 				VExpandString (buf, pr);
302 			} else {
303 				VRestore ();
304 				syscmd (command);
305 				VReopen ();
306 			}
307 		}
308 	} else {
309 		VReopen ();
310 		command [cpos = 0] = 0;
311 	}
312 	VClear ();
313 	if (visualwin) {
314 		setdir (cur==&left ? &right : &left, 0);
315 		setdir (cur, 0);
316 	}
317 }
318 
inputcmd(int plen)319 static int inputcmd (int plen)
320 {
321 	register c;
322 	char cc;
323 
324 	outprompt ();
325 	command [cpos = 0] = 0;
326 	for (;;) {
327 		c = KeyGet ();
328 		if ((c>=' ' && c<='~') || (c>=0300 && c<=0377)) {
329 			if (cpos || c!=' ') {
330 				inscmd (c);
331 				cc = c;
332 				write (1, &cc, 1);
333 			}
334 			continue;
335 		}
336 		switch (c) {
337 		default:
338 			write (1, "\7", 1);
339 			continue;
340 		case cntrl ('V'):       /* quote next char */
341 			c = quote ();
342 			inscmd (c);
343 			wrescchar (c);
344 			continue;
345 		case cntrl ('K'):
346 		case cntrl ('I'):
347 			KeyUnget (c);
348 			visualwin = 1;
349 			cmdreg = 0;
350 			return (0);
351 		case cntrl ('C'):
352 		case cntrl ('B'):
353 		case meta ('A'):
354 		case meta ('B'):
355 		case meta ('G'):
356 		case meta ('I'):
357 		case meta ('J'):
358 			KeyUnget (c);
359 		case cntrl ('P'):
360 		case cntrl ('O'):
361 			cmdreg = 1;
362 			visualwin = 0;
363 			return (0);
364 		case cntrl ('M'):
365 		case cntrl ('J'):
366 			cmdreg = 0;
367 			visualwin = 1;
368 			write (1, "\r\n", 2);
369 			hpos = -1;              /* reset history pointer */
370 			return (1);
371 		case meta ('u'):          /* up */
372 		case cntrl ('E'):
373 			erasecmd (plen);
374 			prevcmd ();
375 			if (cpos)
376 				wresccmd (command, cpos);
377 			continue;
378 		case meta ('d'):          /* down */
379 		case cntrl ('X'):
380 			erasecmd (plen);
381 			nextcmd ();
382 			if (cpos)
383 				wresccmd (command, cpos);
384 			continue;
385 		case cntrl ('Y'):
386 			erasecmd (plen);
387 			continue;
388 		case meta ('b'):        /* backspace */
389 			if (cpos) {
390 				wrescback (command [cpos-1]);
391 				leftcmd ();
392 				delcmd ();
393 			}
394 			continue;
395 		}
396 	}
397 }
398 
erasecmd(int plen)399 static void erasecmd (int plen)
400 {
401 	if (plen+esclen(command) >= 80) {
402 		write (1, "\r\n", 2);
403 		write (1, prompt, (unsigned) strlen (prompt));
404 	} else
405 		while (--cpos >= 0)
406 			wrescback (command [cpos]);
407 	command [cpos = 0] = 0;
408 }
409 
mycmd(int scr)410 static int mycmd (int scr)
411 {
412 	/* Execute built in commands which print nothing:
413 	 *      cd,
414 	 *      chdir           - chdir to $HOME
415 	 *      cd dirname,
416 	 *      chdir dirname   - chdir to dirname
417 	 *      exit            - quit from deco
418 	 * Return 1 if executed one of these else return 0.
419 	 */
420 
421 	if (strbcmp (command, "cd") || strbcmp (command, "chdir")) {
422 		char dir [MAXPATHLEN];
423 
424 		getpar (dir);
425 		if (! dir [0])
426 			setdir (cur, home);
427 		else if (! strcmp (dir, cur->cwd))
428 			setdir (cur, 0);
429 		else if (chdir (dir) < 0)
430 			if (scr)
431 				error ("Cannot chdir to %s", dir);
432 			else
433 				outerr ("Cannot chdir to %s\n", dir);
434 		else
435 			setdir (cur, ".");
436 		return (1);
437 	}
438 	if (strbcmp (command, "exit")) {
439 		quitdeco ();
440 		return (1);
441 	}
442 	return (0);
443 }
444 
445 /* ARGSUSED */
mycmd2(int scr)446 static int mycmd2 (int scr)
447 {
448 	/* Execute built in commands wqhich print something:
449 	 *      set,
450 	 *      setenv          - set global variable
451 	 *      unset,
452 	 *      unsetenv        - unset global variable
453 	 *      env,
454 	 *      printenv        - print list of global variables
455 	 *      pwd             - print current directory name
456 	 * Return 1 if executed one of these, else return 0.
457 	 */
458 
459 	if (strbcmp (command, "env") || strbcmp (command, "printenv")) {
460 		register char **p;
461 printenv:
462 		for (p=EnvVector; p && *p; ++p) {
463 			write (1, *p, (unsigned) strlen (*p));
464 			write (1, "\r\n", 2);
465 		}
466 		return (1);
467 	}
468 	if (strbcmp (command, "pwd")) {
469 		write (1, cur->cwd, (unsigned) strlen (cur->cwd));
470 		write (1, "\r\n", 2);
471 		return (1);
472 	}
473 	if (strbcmp (command, "set") || strbcmp (command, "setenv")) {
474 		char dir [100];
475 		char *v;
476 
477 		v = getpar (dir);
478 		if (! dir [0])
479 			goto printenv;
480 		EnvPut (dir, v);
481 		return (1);
482 	}
483 	if (strbcmp (command, "unset") || strbcmp (command, "unsetenv")) {
484 		char dir [100];
485 
486 		getpar (dir);
487 		if (! dir [0])
488 			goto printenv;
489 		EnvDelete (dir);
490 		return (1);
491 	}
492 	return (0);
493 }
494 
getpar(char * s)495 static char *getpar (char *s)
496 {
497 	register char *p;
498 
499 	*s = 0;
500 	p = command;
501 	while (*p && *p!=' ')           /* skip command name */
502 		++p;
503 	while (*p && *p==' ')           /* skip spaces */
504 		++p;
505 	if (! *p)                       /* return if no parameters */
506 		return (0);
507 	while (*p && *p!=' ')           /* copy parameter # 1 */
508 		*s++ = *p++;
509 	*s = 0;
510 	if (*p == ' ')
511 		++p;
512 	return (p);                     /* address of next parameter */
513 }
514 
jobmenu()515 static void jobmenu ()
516 {
517 	register ch, nh;
518 	int n, histwid, histrow, histcol, execit, status = 0;
519 	BOX *box, *curbox;
520 	char buf [256], buf2 [256];
521 
522 	/* Remove finished background jobs from the list. */
523 	for (n=0; n<HISTSZ && job[n].command; ++n)
524 		if (kill (job[n].pid, 0) < 0) {
525 			free (job[n].command);
526 			job[n].command = 0;
527 			for (ch=n+1; ch<HISTSZ && job[ch].command; ++ch)
528 				job[ch-1] = job[ch];
529 			job[ch-1].command = 0;
530 		}
531 	histwid = 1;
532 	for (nh=0; nh<HISTSZ && job[nh].command; ++nh)
533 		if ((n = esclen (job[nh].command)) > histwid)
534 			histwid = n;
535 	if (! nh) {
536 		error ("No background jobs");
537 		return;
538 	}
539 	histwid += 2+7;
540 	if (histwid < 20)
541 		histwid = 20;
542 	else if (histwid > 70)
543 		histwid = 70;
544 	histrow = (LINES-nh) / 2;
545 	histcol = (79-histwid) / 2;
546 
547 	box = VGetBox (histrow-2, histcol-4, nh+4, histwid+8);
548 	VSetDim ();
549 	VStandOut ();
550 	VFillBox (histrow-2, histcol-4, nh+4, histwid+8, ' ');
551 	VDrawBox (histrow-1, histcol-1, nh+2, histwid+2);
552 	mvcaddstr (histrow-1, 40, " Jobs ");
553 
554 	for (ch=0; ch<nh; ++ch) {
555 		char buf[10];
556 		sprintf (buf, "[%d]", job[nh-ch-1].pid);
557 		VMove (histrow+ch, histcol+1+6 - strlen (buf));
558 		VPutString (buf);
559 		VMove (histrow+ch, histcol+1+7);
560 		putescstr (job[nh-ch-1].command, histwid-1);
561 	}
562 
563 	VSetNormal ();
564 	VStandEnd ();
565 	ch = 0;
566 	for (;;) {
567 		curbox = VGetBox (histrow+nh-ch-1, histcol, 1, histwid);
568 		VPrintBox (curbox);
569 		hidecursor ();
570 		VSync ();
571 		n = KeyGet ();
572 		VUngetBox (curbox);
573 		VFreeBox (curbox);
574 		switch (n) {
575 		default:
576 			VBeep ();
577 			continue;
578 		case cntrl (']'):          /* redraw screen */
579 			VRedraw ();
580 			continue;
581 		case cntrl ('J'):
582 			execit = 0;
583 			break;
584 		case cntrl ('M'):
585 			execit = 1;
586 			break;
587 		case cntrl ('C'):
588 		case cntrl ('['):
589 		case meta ('J'):           /* f0 */
590 			ch = -1;
591 			break;
592 		case meta ('d'):          /* down */
593 			if (--ch < 0)
594 				ch = nh-1;
595 			continue;
596 		case meta ('u'):          /* up */
597 			if (++ch >= nh)
598 				ch = 0;
599 			continue;
600 		}
601 		break;
602 	}
603 	VUngetBox (box);
604 	VFreeBox (box);
605 	if (ch < 0)
606 		return;
607 
608 	VClear ();              /* clear screen */
609 	VMove (LINES-1, 0);
610 	VSync ();
611 	VRestore ();            /* restore terminal */
612 	sprintf (buf, "\1[%d]\2 %s\n", job[ch].pid, job[ch].command);
613 	VExpandString (buf, buf2);
614 	write (1, buf2, strlen (buf2));
615 
616 	/* Put the job into foreground. */
617 	signal (SIGCHLD, SIG_IGN);
618 	tcsetpgrp (2, job[ch].pid);
619 	killpg (job[ch].pid, SIGCONT);
620 
621 	/* Wait fo the job. */
622 	waitpid (job[ch].pid, &status, WUNTRACED);
623 
624 	/* Get back the terminal. */
625 	tcsetpgrp (2, main_pid);
626 	signal (SIGCHLD, sigchild);
627 
628 	if (WIFSTOPPED (status)) {
629 		/* Job stopped, let it run in background. */
630 		killpg (job[ch].pid, SIGCONT);
631 		TtyReset ();
632 	} else {
633 		/* Job finished, remove it from the history. */
634 		free (job[ch].command);
635 		job[ch].command = 0;
636 		for (n=ch+1; n<HISTSZ && job[n].command; ++n)
637 			job[n-1] = job[n];
638 		job[n-1].command = 0;
639 	}
640 
641 	TtyReset ();
642 	VReopen ();
643 	VClear ();
644 	if (visualwin) {
645 		setdir (cur==&left ? &right : &left, 0);
646 		setdir (cur, 0);
647 	}
648 }
649 
histmenu()650 void histmenu ()
651 {
652 	register ch, nh;
653 	int n, histwid, histrow, histcol, execit = 0;
654 	BOX *box, *curbox;
655 
656 	histwid = 1;
657 	for (nh=0; nh<HISTSZ && history[nh]; ++nh)
658 		if ((n = esclen (history[nh])) > histwid)
659 			histwid = n;
660 	if (! nh) {
661 		error ("History is empty");
662 		return;
663 	}
664 	histwid += 2;
665 	if (histwid < 20)
666 		histwid = 20;
667 	else if (histwid > 70)
668 		histwid = 70;
669 	histrow = (LINES-nh) / 2;
670 	histcol = (79-histwid) / 2;
671 
672 	box = VGetBox (histrow-2, histcol-4, nh+4, histwid+8);
673 	VSetDim ();
674 	VStandOut ();
675 	VFillBox (histrow-2, histcol-4, nh+4, histwid+8, ' ');
676 	VDrawBox (histrow-1, histcol-1, nh+2, histwid+2);
677 	mvcaddstr (histrow-1, 40, " History ");
678 	if (job[0].command)
679 		mvcaddstr (histrow+nh+1, 40, " ^B - Jobs ");
680 
681 	for (ch=0; ch<nh; ++ch) {
682 		VMove (histrow+ch, histcol+1);
683 		putescstr (history[nh-ch-1], histwid-1);
684 	}
685 
686 	VSetNormal ();
687 	VStandEnd ();
688 	ch = 0;
689 	for (;;) {
690 		curbox = VGetBox (histrow+nh-ch-1, histcol, 1, histwid);
691 		VPrintBox (curbox);
692 		hidecursor ();
693 		VSync ();
694 		n = KeyGet ();
695 		VUngetBox (curbox);
696 		VFreeBox (curbox);
697 		switch (n) {
698 		default:
699 			VBeep ();
700 			continue;
701 		case cntrl (']'):          /* redraw screen */
702 			VRedraw ();
703 			continue;
704 		case cntrl ('J'):
705 			execit = 0;
706 			break;
707 		case cntrl ('M'):
708 			execit = 1;
709 			break;
710 		case cntrl ('C'):
711 		case cntrl ('['):
712 		case meta ('J'):           /* f0 */
713 			ch = -1;
714 			break;
715 		case cntrl ('B'):         /* jobs */
716 			VUngetBox (box);
717 			VFreeBox (box);
718 			jobmenu ();
719 			return;
720 		case meta ('d'):          /* down */
721 			if (--ch < 0)
722 				ch = nh-1;
723 			continue;
724 		case meta ('u'):          /* up */
725 			if (++ch >= nh)
726 				ch = 0;
727 			continue;
728 		}
729 		break;
730 	}
731 	if (ch >= 0) {
732 		/* execute command from history */
733 		strcpy (command, history [ch]);
734 		cpos = strlen (command);
735 		if (command [0] && execit)
736 			execmd (1, 1);
737 	}
738 	VUngetBox (box);
739 	VFreeBox (box);
740 }
741 
esclen(char * str)742 static int esclen (char *str)
743 {
744 	register c, count;
745 
746 	for (count=0; (c = 0377 & *str); ++count, ++str)
747 		if (c<' ' || c==0177)
748 			++count;
749 		else if ((c>=0200 && c<0300) || c==0377)
750 			count += 3;
751 	return (count);
752 }
753 
putescstr(char * str,int maxlen)754 static void putescstr (char *str, int maxlen)
755 {
756 	register c;
757 
758 	if (maxlen <= 0)
759 		maxlen = 9999;
760 	for (; (c = 0377 & *str); ++str)
761 		if (c<' ' || c==0177) {
762 			maxlen -= 2;
763 			if (maxlen < 0)
764 				break;
765 			VPutChar ('^');
766 			VPutChar (c ^ '@');
767 		} else if ((c>=0200 && c<0300) || c==0377) {
768 			maxlen -= 4;
769 			if (maxlen < 0)
770 				break;
771 			VPutChar ('\\');
772 			VPutChar (c>>6 | '0');
773 			VPutChar ((c>>3 & 7) | '0');
774 			VPutChar ((c & 7) | '0');
775 		} else {
776 			maxlen -= 1;
777 			if (maxlen < 0)
778 				break;
779 			VPutChar (c);
780 		}
781 }
782 
wresccmd(char * str,int n)783 static void wresccmd (char *str, int n)
784 {
785 	char buf [CMDLEN*2];
786 	register char *p;
787 	register c;
788 
789 	for (p=buf; --n>=0; ++str) {
790 		c = 0377 & *str;
791 		if (c<' ' || c==0177) {
792 			*p++ = '^';
793 			*p++ = c ^ '@';
794 		} else if ((c>=0200 && c<0300) || c==0377) {
795 			*p++ = '\\';
796 			*p++ = c>>6 | '0';
797 			*p++ = (c>>3 & 7) | '0';
798 			*p++ = (c & 7) | '0';
799 		} else
800 			*p++ = c;
801 	}
802 	write (1, buf, (unsigned) (p-buf));
803 }
804 
wrescchar(int c)805 static void wrescchar (int c)
806 {
807 	char buf [8];
808 	register char *p;
809 
810 	p = buf;
811 	c &= 0377;
812 	if (c<' ' || c==0177) {
813 		*p++ = '^';
814 		*p++ = c ^ '@';
815 	} else if ((c>=0200 && c<0300) || c==0377) {
816 		*p++ = '\\';
817 		*p++ = c>>6 | '0';
818 		*p++ = (c>>3 & 7) | '0';
819 		*p++ = (c & 7) | '0';
820 	} else
821 		*p++ = c;
822 	write (1, buf, (unsigned) (p-buf));
823 }
824 
wrescback(int c)825 static void wrescback (int c)
826 {
827 	c &= 0377;
828 	if (c<' ' || c==0177)
829 		write (1, "\b\b  \b\b", 6);
830 	else if ((c>=0200 && c<0300) || c==0377)
831 		write (1, "\b\b\b\b    \b\b\b\b", 12);
832 	else
833 		write (1, "\b \b", 3);
834 }
835