1 /*
2  *	Shell-window functions
3  *	Copyright
4  *		(C) 1992 Joseph H. Allen
5  *
6  *	This file is part of JOE (Joe's Own Editor)
7  */
8 #include "types.h"
9 
10 /* Executed when shell process terminates */
11 
cdone(void * obj)12 static void cdone(void *obj)
13 {
14 	B *b = (B *)obj;
15 	b->pid = 0;
16 	close(b->out);
17 	b->out = -1;
18 	if (b->vt) {
19 		vtrm(b->vt);
20 		b->vt = 0;
21 	}
22 }
23 
cdone_parse(void * obj)24 static void cdone_parse(void *obj)
25 {
26 	B *b = (B *)obj;
27 	b->pid = 0;
28 	close(b->out);
29 	b->out = -1;
30 	if (b->vt) {
31 		vtrm(b->vt);
32 		b->vt = 0;
33 	}
34 	parserrb(b);
35 }
36 
37 /* Set up for shell mode in each window with the buffer */
38 
ansiall(B * b)39 static void ansiall(B *b)
40 {
41 	W *w;
42 	if ((w = maint->topwin) != NULL) {
43 		do {
44 	 		if (w->watom->what & TYPETW) {
45 	 			BW *bw = (BW *)w->object;
46 		 		if (bw->b == b) {
47 		 			bw->o.ansi = bw->b->o.ansi;
48 		 			bw->o.syntax = bw->b->o.syntax;
49 		 		}
50 			}
51 		w = w->link.next;
52 	 	} while (w != maint->topwin);
53 	 }
54 }
55 
56 /* Executed for each chunk of data we get from the shell */
57 
58 /* Mark each window which needs to follow the shell output */
59 
cready(B * b,off_t byte)60 static void cready(B *b, off_t byte)
61 {
62 	W *w;
63 	if (b->oldcur && b->oldcur->byte == byte)
64 		b->shell_flag = 1;
65 	else
66 		b->shell_flag = 0;
67 	if ((w = maint->topwin) != NULL) {
68 	 	do {
69 	 		if (w->watom->what & TYPETW) {
70 	 			BW *bw = (BW *)w->object;
71 		 		if (bw->b == b && bw->cursor->byte == byte) {
72 		 			bw->shell_flag = 1;
73 		 		} else {
74 		 			bw->shell_flag = 0;
75 		 		}
76 			}
77 		w = w->link.next;
78 	 	} while (w != maint->topwin);
79 	}
80 }
81 
cfollow(B * b,VT * vt,off_t byte)82 static void cfollow(B *b, VT *vt, off_t byte)
83 {
84 	W *w;
85 	int xn;
86 	if (vt && piscol(vt->vtcur) >= vt->width)
87 		xn = 1; /* xn glitch: cursor is past end of line */
88 	else
89 		xn = 0;
90 	if (b->oldcur && b->shell_flag) {
91 		b->shell_flag = 0;
92 		pgoto(b->oldcur, byte);
93 		b->oldcur->xcol = piscol(b->oldcur);
94 		if (xn)
95 			b->oldcur->xcol = vt->width - 1;
96 	}
97 	if ((w = maint->topwin) != NULL) {
98 	 	do {
99 	 		if (w->watom->what & TYPETW) {
100 	 			BW *bw = (BW *)w->object;
101 	 			if (bw->shell_flag) {
102 	 				bw->shell_flag = 0;
103 	 				pgoto(bw->cursor, byte);
104 	 				bw->cursor->xcol = piscol(bw->cursor);
105 					if (xn)
106 						bw->cursor->xcol = vt->width - 1;
107 	 				if (vt && bw->top->line != vt->top->line && (1 + vt->vtcur->line - vt->top->line) <= bw->h)
108 	 					pline(bw->top, vt->top->line);
109 	 				dofollows();
110 	 			}
111 			}
112 		w = w->link.next;
113 	 	} while (w != maint->topwin);
114 	}
115 }
116 
vt_scrdn()117 void vt_scrdn()
118 {
119 	W *w;
120 	 if ((w = maint->topwin) != NULL) {
121 	 	do {
122 	 		if (w->watom->what & TYPETW) {
123 	 			BW *bw = (BW *)w->object;
124 	 			if (bw->shell_flag) {
125 	 				pnextl(bw->top);
126 	 				if (bw->parent->y != -1)
127 						nscrlup(bw->parent->t->t, bw->y, bw->y + bw->h, 1);
128 	 			}
129 			}
130 		w = w->link.next;
131 	 	} while (w != maint->topwin);
132 	 }
133 }
134 
cdata(void * obj,char * dat,ptrdiff_t siz)135 static void cdata(void *obj, char *dat, ptrdiff_t siz)
136 {
137 	B *b = (B *)obj;
138 	if (b->vt) { /* ANSI terminal emulator */
139 		MACRO *m;
140 		do {
141 			cready(b, b->vt->vtcur->byte);
142 
143 			m = vt_data(b->vt, &dat, &siz);
144 
145 			cfollow(b, b->vt, b->vt->vtcur->byte);
146 			undomark();
147 			if (m) {
148 				/* only do this if cursor is on window */
149 				if ((maint->curwin->watom->what & TYPETW) && ((BW *)maint->curwin->object)->b == b) {
150 					exmacro(m, 1, NO_MORE_DATA);
151 					edupd(1);
152 				}
153 				rmmacro(m);
154 			}
155 		} while (m);
156 	} else { /* Dumb terminal */
157 		P *q = pdup(b->eof, "cdata");
158 		P *r = pdup(b->eof, "cdata");
159 		off_t byte = q->byte;
160 		char bf[1024];
161 		int x, y;
162 		cready(b, byte);
163 		for (x = y = 0; x != siz; ++x) {
164 			if (dat[x] == 13 || dat[x] == 0) {
165 				;
166 			} else if (dat[x] == 8 || dat[x] == 127) {
167 				if (y) {
168 					--y;
169 				} else {
170 					pset(q, r);
171 					prgetc(q);
172 					bdel(q, r);
173 					--byte;
174 				}
175 			} else if (dat[x] == 7) {
176 				ttputc(7);
177 			} else {
178 				bf[y++] = dat[x];
179 			}
180 		}
181 		if (y) {
182 			binsm(r, bf, y);
183 		}
184 		prm(r);
185 		prm(q);
186 		cfollow(b, NULL, b->eof->byte);
187 		undomark();
188 	}
189 }
190 
cstart(BW * bw,const char * name,char ** s,void * obj,int * notify,int build,int out_only,const char * first_command,int vt)191 int cstart(BW *bw, const char *name, char **s, void *obj, int *notify, int build, int out_only, const char *first_command, int vt)
192 {
193 #ifdef __MSDOS__
194 	if (notify) {
195 		*notify = 1;
196 	}
197 	varm(s);
198 	msgnw(bw->parent, joe_gettext(_("Sorry, no sub-processes in DOS (yet)")));
199 	return -1;
200 #else
201 	MPX *m;
202 	ptrdiff_t shell_w = -1, shell_h = -1;
203 
204 
205 	if (notify) {
206 		*notify = 1;
207 	}
208 	if (bw->b->pid) {
209 		if (!vt) { /* Don't complain if shell already running.. makes F-key switching nicer */
210 			/* Keep old behavior for dumb terminal */
211 			msgnw(bw->parent, joe_gettext(_("Program already running in this window")));
212 		}
213 		varm(s);
214 		return -1;
215 	}
216 
217 	if (vt) {
218 		BW *master = vtmaster(bw->parent->t, bw->b); /* In case of multiple BWs on one B, pick one to be the master */
219 		if (!master) master = bw; /* Should never happen */
220 		shell_w = master->w;
221 		shell_h = master->h;
222 		bw->b->vt = mkvt(bw->b, master->top, master->h, master->w);
223 
224 		bw->b->o.ansi = 1;
225 		bw->b->o.syntax = load_syntax("ansi");
226 
227 		/* Turn on shell mode for each window */
228 		ansiall(bw->b);
229 	}
230 
231 	/* p_goto_eof(bw->cursor); */
232 
233 	if (!(m = mpxmk(&bw->b->out, name, s, cdata, bw->b, build ? cdone_parse : cdone, bw->b, out_only, shell_w, shell_h))) {
234 		varm(s);
235 		msgnw(bw->parent, joe_gettext(_("No ptys available")));
236 		return -1;
237 	} else {
238 		bw->b->pid = m->pid;
239 		if (first_command)
240 			if (-1 == write(bw->b->out, first_command, strlen(first_command)))
241 				msgnw(bw->parent, joe_gettext(_("Write failed when writing first command to shell")));
242 	}
243 	return 0;
244 #endif
245 }
246 
dobknd(BW * bw,int vt)247 static int dobknd(BW *bw, int vt)
248 {
249 	char **a;
250 	char *s;
251 	const char *sh;
252 	const char start_sh[] = ". " JOERC "shell.sh\n";
253 	const char start_csh[] = "source " JOERC "shell.csh\n";
254 
255         if (!modify_logic(bw,bw->b))
256         	return -1;
257 
258         sh=getenv("SHELL");
259 
260         if (file_exists(sh) && zcmp(sh,"/bin/sh")) goto ok;
261         if (file_exists(sh="/bin/bash")) goto ok;
262         if (file_exists(sh="/usr/bin/bash")) goto ok;
263         if (file_exists(sh="/bin/sh")) goto ok;
264 
265         msgnw(bw->parent, joe_gettext(_("\"SHELL\" environment variable not defined or exported")));
266         return -1;
267 
268         ok:
269 	a = vamk(3);
270 	s = vsncpy(NULL, 0, sz(sh));
271 	a = vaadd(a, s);
272 	s = vsncpy(NULL, 0, sc("-i"));
273 	a = vaadd(a, s);
274 	return cstart(bw, sh, a, NULL, NULL, 0, 0, (vt ? (zstr(sh, "csh") ? start_csh : start_sh) : NULL), vt);
275 }
276 
277 /* Start ANSI shell */
278 
uvtbknd(W * w,int k)279 int uvtbknd(W *w, int  k)
280 {
281 	BW *bw;
282 	WIND_BW(bw, w);
283 	if (kmap_empty(kmap_getcontext("vtshell"))) {
284 		msgnw(bw->parent, joe_gettext(_(":vtshell keymap is missing")));
285 		return -1;
286 	}
287 	return dobknd(bw, 1);
288 }
289 
290 /* Start dumb shell */
291 
ubknd(W * w,int k)292 int ubknd(W *w, int k)
293 {
294 	BW *bw;
295 	WIND_BW(bw, w);
296 	if (kmap_empty(shell_kbd->topmap)) {
297 		msgnw(bw->parent, joe_gettext(_(":shell keymap is missing")));
298 		return -1;
299 	}
300 	return dobknd(bw, 0);
301 }
302 
303 /* Run a program in a window */
304 
dorun(W * w,char * s,void * object,int * notify)305 static int dorun(W *w, char *s, void *object, int *notify)
306 {
307 	BW *bw;
308 	char **a;
309 	char *cmd;
310 	WIND_BW(bw, w);
311         if (!modify_logic(bw,bw->b))
312         	return -1;
313 
314 	a = vamk(10);
315 	cmd = vsncpy(NULL, 0, sc("/bin/sh"));
316 
317 	a = vaadd(a, cmd);
318 	cmd = vsncpy(NULL, 0, sc("-c"));
319 	a = vaadd(a, cmd);
320 	a = vaadd(a, s);
321 	return cstart(bw, "/bin/sh", a, NULL, notify, 0, 0, NULL, 0);
322 }
323 
324 B *runhist = NULL;
325 
urun(W * w,int k)326 int urun(W *w, int k)
327 {
328 	if (wmkpw(w, joe_gettext(_("Program to run: ")), &runhist, dorun, "Run", NULL, cmplt_command, NULL, NULL, locale_map, PWFLAG_COMMAND)) {
329 		return 0;
330 	} else {
331 		return -1;
332 	}
333 }
334 
dobuild(W * w,char * s,void * object,int * notify)335 static int dobuild(W *w, char *s, void *object, int *notify)
336 {
337 	BW *bw;
338 	char **a = vamk(10);
339 	char *cmd = vsncpy(NULL, 0, sc("/bin/sh"));
340 	char *t = NULL;
341 	WIND_BW(bw, w);
342 
343 
344 	bw->b->o.ansi = 1;
345 	bw->b->o.syntax = load_syntax("ansi");
346 	/* Turn on shell mode for each window */
347 	ansiall(bw->b);
348 
349 	a = vaadd(a, cmd);
350 	cmd = vsncpy(NULL, 0, sc("-c"));
351 	a = vaadd(a, cmd);
352 	if (bw->b->current_dir && bw->b->current_dir[0]) {
353 		// Change directory before we run
354 		t = vsncpy(sv(t), sc("cd '"));
355 		t = vsncpy(sv(t), sv(bw->b->current_dir));
356 		t = vsncpy(sv(t), sc("' && "));
357 	}
358 	t = vsncpy(sv(t), sc("echo \"\nJOE: cd `pwd`\n\" && if ("));
359 	t = vsncpy(sv(t), sv(s));
360 	t = vsncpy(sv(t), sc("); then echo \"\nJOE: PASS (exit status = $?)\n\"; else echo \"\nJOE: FAIL (exit status = $?)\n\"; fi"));
361 	vsrm(s);
362 	s = t;
363 	a = vaadd(a, s);
364 	return cstart(bw, "/bin/sh", a, NULL, notify, 1, 0, NULL, 0);
365 }
366 
367 B *buildhist = NULL;
368 
ubuild(W * w,int k)369 int ubuild(W *w, int k)
370 {
371 	BW *bw;
372 	WIND_BW(bw, w);
373 	if (buildhist) {
374 		if ((bw=wmkpw(bw->parent, joe_gettext(_("Build command: ")), &buildhist, dobuild, "Run", NULL, cmplt_command, NULL, NULL, locale_map, PWFLAG_COMMAND))) {
375 			uuparw(bw->parent, 0);
376 			u_goto_eol(bw->parent, 0);
377 			bw->cursor->xcol = piscol(bw->cursor);
378 			return 0;
379 		} else {
380 		return -1;
381 		}
382 	} else {
383 		if (wmkpw(bw->parent, joe_gettext(_("Enter build command (for example, 'make'): ")), &buildhist, dobuild, "Run", NULL, cmplt_command, NULL, NULL, locale_map, PWFLAG_COMMAND)) {
384 			return 0;
385 		} else {
386 		return -1;
387 		}
388 	}
389 }
390 
391 B *grephist = NULL;
392 
ugrep(W * w,int k)393 int ugrep(W *w, int k)
394 {
395 	BW *bw;
396 	WIND_BW(bw, w);
397 	/* Set parser to grep */
398 	bw->b->parseone = parseone_grep;
399 	if (grephist) {
400 		if ((bw=wmkpw(bw->parent, joe_gettext(_("Grep command: ")), &grephist, dobuild, "Run", NULL, cmplt_command, NULL, NULL, locale_map, PWFLAG_COMMAND))) {
401 			uuparw(bw->parent, 0);
402 			u_goto_eol(bw->parent, 0);
403 			bw->cursor->xcol = piscol(bw->cursor);
404 			return 0;
405 		} else {
406 		return -1;
407 		}
408 	} else {
409 		if (wmkpw(bw->parent, joe_gettext(_("Enter grep command (for example, 'grep -n foo *.c'): ")), &grephist, dobuild, "Run", NULL, cmplt_command, NULL, NULL, locale_map, PWFLAG_COMMAND)) {
410 			return 0;
411 		} else {
412 		return -1;
413 		}
414 	}
415 }
416 
417 /* Kill program */
418 
pidabort(W * w,int c,void * object,int * notify)419 static int pidabort(W *w, int c, void *object, int *notify)
420 {
421 	BW *bw;
422 	WIND_BW(bw, w);
423 	if (notify) {
424 		*notify = 1;
425 	}
426 	if (c != YES_CODE && !yncheck(yes_key, c)) {
427 		return -1;
428 	}
429 	if (bw->b->pid) {
430 		kill(bw->b->pid, 1);
431 		return -1;
432 	} else {
433 		return -1;
434 	}
435 }
436 
ukillpid(W * w,int k)437 int ukillpid(W *w, int k)
438 {
439 	BW *bw;
440 	WIND_BW(bw, w);
441 	if (bw->b->pid) {
442 		if (mkqw(bw->parent, sz(joe_gettext(_("Kill program (y,n,%{abort})?"))), pidabort, NULL, NULL, NULL)) {
443 			return 0;
444 		} else {
445 			return -1;
446 		}
447 	} else {
448 		return 0;
449 	}
450 }
451