1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright (c) 2016 by Delphix. All rights reserved.
25 */
26
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
29
30
31 /* Copyright (c) 1979 Regents of the University of California */
32
33 #include "ex.h"
34 #include "ex_temp.h"
35 #include "ex_tty.h"
36 #include "ex_vis.h"
37
38 extern int getchar();
39 /*
40 * Unix escapes, filtering
41 */
42
43 /*
44 * First part of a shell escape,
45 * parse the line, expanding # and % and ! and printing if implied.
46 */
47 void
unix0(bool warn,int contcmd)48 unix0(bool warn, int contcmd)
49 {
50 unsigned char *up, *fp;
51 short c;
52 char multic[MB_LEN_MAX + 1];
53 int len;
54 int contread = 0;
55 wchar_t wc;
56 unsigned char printub, puxb[UXBSIZE + sizeof (int)];
57 const char *specialchars = (contcmd ? "%#!\n" : "%#!");
58
59 printub = 0;
60 CP(puxb, uxb);
61 c = peekchar();
62 if (c == '\n' || c == EOF) {
63 (void) getchar();
64 error(value(vi_TERSE) ?
65 gettext("Incomplete shell escape command") :
66 gettext("Incomplete shell escape command - use 'shell' to get a shell"));
67 }
68 up = (unsigned char *)uxb;
69
70 for (;;) {
71 if (!isascii(c)) {
72 if (c == EOF)
73 break;
74 if ((len = _mbftowc(multic, &wc, getchar, &peekc)) > 0) {
75 if ((up + len) >= (unsigned char *)&uxb[UXBSIZE]) {
76 uxb[0] = 0;
77 error(gettext("Command too long"));
78 }
79 strncpy(up, multic, len);
80 up += len;
81 goto loop_check;
82 }
83 }
84
85 (void) getchar();
86 switch (c) {
87
88 case '\\':
89 if (any(peekchar(), specialchars)) {
90 c = getchar();
91 /*
92 * If we encountered a backslash-escaped
93 * newline, and we're processing a continuation
94 * command, then continue processing until
95 * non-backslash-escaped newline is reached.
96 */
97 if (contcmd && (c == '\n')) {
98 contread = 1;
99 }
100 }
101 /* FALLTHROUGH */
102 default:
103 if (up >= (unsigned char *)&uxb[UXBSIZE]) {
104 tunix:
105 uxb[0] = 0;
106 error(gettext("Command too long"));
107 }
108 /*
109 * If this is a tag command (-t or :tag),
110 * then don't save any command that follows
111 * '!' in the invalid tags file, ie:
112 * '!!' should not repeat the invalid command
113 * later on when tagflg has been cleared.
114 */
115 if (!tagflg)
116 *up++ = c;
117 break;
118
119 case '!':
120 if (up != (unsigned char *)uxb && *puxb != 0) {
121 fp = puxb;
122 if (*fp == 0) {
123 uxb[0] = 0;
124 error(value(vi_TERSE) ?
125 gettext("No previous command") :
126 gettext("No previous command to substitute for !"));
127 }
128 printub++;
129 while (*fp) {
130 if (up >= (unsigned char *)&uxb[UXBSIZE])
131 goto tunix;
132 *up++ = *fp++;
133 }
134 } else if (up == (unsigned char *)uxb) {
135 /* If up = uxb it means we are on the first
136 * character inside the shell command.
137 * (i.e., after the ":!")
138 *
139 * The user has just entered ":!!" which
140 * means that though there is only technically
141 * one '!' we know they really meant ":!!!". So
142 * substitute the last command for them.
143 */
144 fp = puxb;
145 if (*fp == 0) {
146 uxb[0] = 0;
147 error(value(vi_TERSE) ?
148 gettext("No previous command") :
149 gettext("No previous command to substitute for !"));
150 }
151 printub++;
152 while (*fp) {
153 if (up >= (unsigned char *)&uxb[UXBSIZE])
154 goto tunix;
155 *up++ = *fp++;
156 }
157 } else {
158 /*
159 * Treat a lone "!" as just a regular character
160 * so commands like "mail machine!login" will
161 * work as usual (i.e., the user doesn't need
162 * to dereference the "!" with "\!").
163 */
164 if (up >= (unsigned char *)&uxb[UXBSIZE]) {
165 uxb[0] = 0;
166 error(gettext("Command too long"));
167 }
168 *up++ = c;
169 }
170 break;
171
172 case '#':
173 fp = (unsigned char *)altfile;
174 if (*fp == 0) {
175 uxb[0] = 0;
176 error(value(vi_TERSE) ?
177 gettext("No alternate filename") :
178 gettext("No alternate filename to substitute for #"));
179 }
180 goto uexp;
181
182 case '%':
183 fp = savedfile;
184 if (*fp == 0) {
185 uxb[0] = 0;
186 error(value(vi_TERSE) ?
187 gettext("No filename") :
188 gettext("No filename to substitute for %%"));
189 }
190 uexp:
191 printub++;
192 while (*fp) {
193 if (up >= (unsigned char *)&uxb[UXBSIZE])
194 goto tunix;
195 *up++ = *fp++;
196 }
197 break;
198 }
199
200 loop_check:
201 c = peekchar();
202 if (c == '"' || c == '|' || (contread > 0) || !endcmd(c)) {
203 /*
204 * If contread was set, then the newline just
205 * processed was preceeded by a backslash, and
206 * not considered the end of the command. Reset
207 * it here in case another backslash-escaped
208 * newline is processed.
209 */
210 contread = 0;
211 continue;
212 } else {
213 (void) getchar();
214 break;
215 }
216 }
217 if (c == EOF)
218 ungetchar(c);
219 *up = 0;
220 if (!inopen)
221 resetflav();
222 if (warn)
223 ckaw();
224 if (warn && hush == 0 && chng && xchng != chng && value(vi_WARN) && dol > zero) {
225 xchng = chng;
226 vnfl();
227 viprintf(mesg(value(vi_TERSE) ? gettext("[No write]") :
228 gettext("[No write since last change]")));
229 noonl();
230 flush();
231 } else
232 warn = 0;
233 if (printub) {
234 if (uxb[0] == 0)
235 error(value(vi_TERSE) ? gettext("No previous command") :
236 gettext("No previous command to repeat"));
237 if (inopen) {
238 splitw++;
239 vclean();
240 vgoto(WECHO, 0);
241 }
242 if (warn)
243 vnfl();
244 if (hush == 0)
245 lprintf("!%s", uxb);
246 if (inopen && Outchar != termchar) {
247 vclreol();
248 vgoto(WECHO, 0);
249 } else
250 putnl();
251 flush();
252 }
253 }
254
255 /*
256 * Do the real work for execution of a shell escape.
257 * Mode is like the number passed to open system calls
258 * and indicates filtering. If input is implied, newstdin
259 * must have been setup already.
260 */
261 ttymode
unixex(opt,up,newstdin,mode)262 unixex(opt, up, newstdin, mode)
263 unsigned char *opt, *up;
264 int newstdin, mode;
265 {
266 int pvec[2];
267 ttymode f;
268
269 signal(SIGINT, SIG_IGN);
270 #ifdef SIGTSTP
271 if (dosusp)
272 signal(SIGTSTP, SIG_DFL);
273 #endif
274 if (inopen)
275 f = setty(normf);
276 if ((mode & 1) && pipe(pvec) < 0) {
277 /* Newstdin should be io so it will be closed */
278 if (inopen)
279 setty(f);
280 error(gettext("Can't make pipe for filter"));
281 }
282 #ifndef VFORK
283 pid = fork();
284 #else
285 pid = vfork();
286 #endif
287 if (pid < 0) {
288 if (mode & 1) {
289 close(pvec[0]);
290 close(pvec[1]);
291 }
292 setrupt();
293 if (inopen)
294 setty(f);
295 error(gettext("No more processes"));
296 }
297 if (pid == 0) {
298 if (mode & 2) {
299 close(0);
300 dup(newstdin);
301 close(newstdin);
302 }
303 if (mode & 1) {
304 close(pvec[0]);
305 close(1);
306 dup(pvec[1]);
307 if (inopen) {
308 close(2);
309 dup(1);
310 }
311 close(pvec[1]);
312 }
313 if (io)
314 close(io);
315 if (tfile)
316 close(tfile);
317 signal(SIGHUP, oldhup);
318 signal(SIGQUIT, oldquit);
319 if (ruptible)
320 signal(SIGINT, SIG_DFL);
321 execlp((char *)svalue(vi_SHELL), (char *)svalue(vi_SHELL),
322 opt, up, (char *)0);
323 viprintf(gettext("Invalid SHELL value: %s\n"),
324 svalue(vi_SHELL));
325 flush();
326 error(NOSTR);
327 }
328 if (mode & 1) {
329 io = pvec[0];
330 close(pvec[1]);
331 }
332 if (newstdin)
333 close(newstdin);
334 return (f);
335 }
336
337 /*
338 * Wait for the command to complete.
339 * F is for restoration of tty mode if from open/visual.
340 * C flags suppression of printing.
341 */
342 void
unixwt(c,f)343 unixwt(c, f)
344 bool c;
345 ttymode f;
346 {
347
348 waitfor();
349 #ifdef SIGTSTP
350 if (dosusp)
351 signal(SIGTSTP, onsusp);
352 #endif
353 if (inopen)
354 setty(f);
355 setrupt();
356 if (!inopen && c && hush == 0) {
357 viprintf("!\n");
358 flush();
359 termreset();
360 gettmode();
361 }
362 }
363
364 /*
365 * Setup a pipeline for the filtration implied by mode
366 * which is like a open number. If input is required to
367 * the filter, then a child editor is created to write it.
368 * If output is catch it from io which is created by unixex.
369 */
370 int
vi_filter(int mode)371 vi_filter(int mode)
372 {
373 static int pvec[2];
374 ttymode f; /* was register */
375 int nlines = lineDOL();
376 int status2;
377 pid_t pid2 = 0;
378
379 mode++;
380 if (mode & 2) {
381 signal(SIGINT, SIG_IGN);
382 signal(SIGPIPE, SIG_IGN);
383 if (pipe(pvec) < 0)
384 error(gettext("Can't make pipe"));
385 pid2 = fork();
386 io = pvec[0];
387 if (pid < 0) {
388 setrupt();
389 close(pvec[1]);
390 error(gettext("No more processes"));
391 }
392 if (pid2 == 0) {
393 extern unsigned char tfname[];
394 setrupt();
395 io = pvec[1];
396 close(pvec[0]);
397
398 /* To prevent seeking in this process and the
399 parent, we must reopen tfile here */
400 close(tfile);
401 tfile = open(tfname, 2);
402
403 putfile(1);
404 exit(errcnt);
405 }
406 close(pvec[1]);
407 io = pvec[0];
408 setrupt();
409 }
410 f = unixex("-c", uxb, (mode & 2) ? pvec[0] : 0, mode);
411 if (mode == 3) {
412 (void) delete(0);
413 addr2 = addr1 - 1;
414 }
415 if (mode == 1)
416 deletenone();
417 if (mode & 1) {
418 if(FIXUNDO)
419 undap1 = undap2 = addr2+1;
420 (void)append(getfile, addr2);
421 #ifdef UNDOTRACE
422 if (trace)
423 vudump(gettext("after append in filter"));
424 #endif
425 }
426 close(io);
427 io = -1;
428 unixwt(!inopen, f);
429 if (pid2) {
430 (void)kill(pid2, 9);
431 do
432 rpid = waitpid(pid2, &status2, 0);
433 while (rpid == (pid_t)-1 && errno == EINTR);
434 }
435 netchHAD(nlines);
436 return (0);
437 }
438
439 /*
440 * Set up to do a recover, getting io to be a pipe from
441 * the recover process.
442 */
443 void
recover(void)444 recover(void)
445 {
446 static int pvec[2];
447
448 if (pipe(pvec) < 0)
449 error(gettext(" Can't make pipe for recovery"));
450 pid = fork();
451 io = pvec[0];
452 if (pid < 0) {
453 close(pvec[1]);
454 error(gettext(" Can't fork to execute recovery"));
455 }
456 if (pid == 0) {
457 unsigned char cryptkey[19];
458 close(2);
459 dup(1);
460 close(1);
461 dup(pvec[1]);
462 close(pvec[1]);
463 if(xflag) {
464 strcpy(cryptkey, "CrYpTkEy=XXXXXXXXX");
465 strcpy(cryptkey + 9, key);
466 if(putenv((char *)cryptkey) != 0)
467 smerror(gettext(" Cannot copy key to environment"));
468 execlp(EXRECOVER, "exrecover", "-x", svalue(vi_DIRECTORY), file, (char *) 0);
469 } else
470 execlp(EXRECOVER, "exrecover", svalue(vi_DIRECTORY), file, (char *) 0);
471 close(1);
472 dup(2);
473 error(gettext(" No recovery routine"));
474 }
475 close(pvec[1]);
476 }
477
478 /*
479 * Wait for the process (pid an external) to complete.
480 */
481 void
waitfor(void)482 waitfor(void)
483 {
484
485 do
486 rpid = waitpid(pid, &status, 0);
487 while (rpid == (pid_t)-1 && errno != ECHILD);
488 if ((status & 0377) == 0)
489 status = (status >> 8) & 0377;
490 else {
491 /*
492 * TRANSLATION_NOTE
493 * Reference order of arguments must not
494 * be changed using '%digit$', since vi's
495 * viprintf() does not support it.
496 */
497 viprintf(gettext("%d: terminated with signal %d"), pid,
498 status & 0177);
499 if (status & 0200)
500 viprintf(gettext(" -- core dumped"));
501 putchar('\n');
502 }
503 }
504
505 /*
506 * The end of a recover operation. If the process
507 * exits non-zero, force not edited; otherwise force
508 * a write.
509 */
510 void
revocer(void)511 revocer(void)
512 {
513
514 waitfor();
515 if (pid == rpid && status != 0)
516 edited = 0;
517 else
518 change();
519 }
520