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