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: [32mPASS[0m (exit status = $?)\n\"; else echo \"\nJOE: [31mFAIL[0m (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