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.
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.
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]
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.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 /* Copyright (c) 1979 Regents of the University of California */
33 #pragma ident "%Z%%M% %I% %E% SMI"
42 * Unix escapes, filtering
46 * First part of a shell escape,
47 * parse the line, expanding # and % and ! and printing if implied.
50 unix0(bool warn
, int contcmd
)
52 unsigned char *up
, *fp
;
54 char multic
[MB_LEN_MAX
+ 1];
58 unsigned char printub
, puxb
[UXBSIZE
+ sizeof (int)];
59 const char *specialchars
= (contcmd
? "%#!\n" : "%#!");
64 if (c
== '\n' || c
== EOF
) {
66 error(value(vi_TERSE
) ?
67 gettext("Incomplete shell escape command") :
68 gettext("Incomplete shell escape command - use 'shell' to get a shell"));
70 up
= (unsigned char *)uxb
;
76 if ((len
= _mbftowc(multic
, &wc
, getchar
, &peekc
)) > 0) {
77 if ((up
+ len
) >= (unsigned char *)&uxb
[UXBSIZE
]) {
79 error(gettext("Command too long"));
81 strncpy(up
, multic
, len
);
91 if (any(peekchar(), specialchars
)) {
94 * If we encountered a backslash-escaped
95 * newline, and we're processing a continuation
96 * command, then continue processing until
97 * non-backslash-escaped newline is reached.
99 if (contcmd
&& (c
== '\n')) {
104 if (up
>= (unsigned char *)&uxb
[UXBSIZE
]) {
107 error(gettext("Command too long"));
110 * If this is a tag command (-t or :tag),
111 * then don't save any command that follows
112 * '!' in the invalid tags file, ie:
113 * '!!' should not repeat the invalid command
114 * later on when tagflg has been cleared.
121 if (up
!= (unsigned char *)uxb
&& *puxb
!= 0) {
125 error(value(vi_TERSE
) ?
126 gettext("No previous command") :
127 gettext("No previous command to substitute for !"));
131 if (up
>= (unsigned char *)&uxb
[UXBSIZE
])
135 } else if (up
== (unsigned char *)uxb
) {
136 /* If up = uxb it means we are on the first
137 * character inside the shell command.
138 * (i.e., after the ":!")
140 * The user has just entered ":!!" which
141 * means that though there is only technically
142 * one '!' we know they really meant ":!!!". So
143 * substitute the last command for them.
148 error(value(vi_TERSE
) ?
149 gettext("No previous command") :
150 gettext("No previous command to substitute for !"));
154 if (up
>= (unsigned char *)&uxb
[UXBSIZE
])
160 * Treat a lone "!" as just a regular character
161 * so commands like "mail machine!login" will
162 * work as usual (i.e., the user doesn't need
163 * to dereference the "!" with "\!").
165 if (up
>= (unsigned char *)&uxb
[UXBSIZE
]) {
167 error(gettext("Command too long"));
174 fp
= (unsigned char *)altfile
;
177 error(value(vi_TERSE
) ?
178 gettext("No alternate filename") :
179 gettext("No alternate filename to substitute for #"));
187 error(value(vi_TERSE
) ?
188 gettext("No filename") :
189 gettext("No filename to substitute for %%"));
194 if (up
>= (unsigned char *)&uxb
[UXBSIZE
])
203 if (c
== '"' || c
== '|' || (contread
> 0) || !endcmd(c
)) {
205 * If contread was set, then the newline just
206 * processed was preceeded by a backslash, and
207 * not considered the end of the command. Reset
208 * it here in case another backslash-escaped
209 * newline is processed.
225 if (warn
&& hush
== 0 && chng
&& xchng
!= chng
&& value(vi_WARN
) && dol
> zero
) {
228 viprintf(mesg(value(vi_TERSE
) ? gettext("[No write]") :
229 gettext("[No write since last change]")));
236 error(value(vi_TERSE
) ? gettext("No previous command") :
237 gettext("No previous command to repeat"));
247 if (inopen
&& Outchar
!= termchar
) {
257 * Do the real work for execution of a shell escape.
258 * Mode is like the number passed to open system calls
259 * and indicates filtering. If input is implied, newstdin
260 * must have been setup already.
263 unixex(opt
, up
, newstdin
, mode
)
264 unsigned char *opt
, *up
;
270 signal(SIGINT
, SIG_IGN
);
273 signal(SIGTSTP
, SIG_DFL
);
277 if ((mode
& 1) && pipe(pvec
) < 0) {
278 /* Newstdin should be io so it will be closed */
281 error(gettext("Can't make pipe for filter"));
296 error(gettext("No more processes"));
318 signal(SIGHUP
, oldhup
);
319 signal(SIGQUIT
, oldquit
);
321 signal(SIGINT
, SIG_DFL
);
322 execlp((char *)svalue(vi_SHELL
), (char *)svalue(vi_SHELL
),
324 viprintf(gettext("Invalid SHELL value: %s\n"),
339 * Wait for the command to complete.
340 * F is for restoration of tty mode if from open/visual.
341 * C flags suppression of printing.
352 signal(SIGTSTP
, onsusp
);
357 if (!inopen
&& c
&& hush
== 0) {
366 * Setup a pipeline for the filtration implied by mode
367 * which is like a open number. If input is required to
368 * the filter, then a child editor is created to write it.
369 * If output is catch it from io which is created by unixex.
375 ttymode f
; /* was register */
376 int nlines
= lineDOL();
382 signal(SIGINT
, SIG_IGN
);
383 signal(SIGPIPE
, SIG_IGN
);
385 error(gettext("Can't make pipe"));
391 error(gettext("No more processes"));
394 extern unsigned char tfname
[];
399 /* To prevent seeking in this process and the
400 parent, we must reopen tfile here */
402 tfile
= open(tfname
, 2);
411 f
= unixex("-c", uxb
, (mode
& 2) ? pvec
[0] : 0, mode
);
420 undap1
= undap2
= addr2
+1;
421 (void)append(getfile
, addr2
);
424 vudump(gettext("after append in filter"));
433 rpid
= waitpid(pid2
, &status2
, 0);
434 while (rpid
== (pid_t
)-1 && errno
== EINTR
);
441 * Set up to do a recover, getting io to be a pipe from
442 * the recover process.
450 error(gettext(" Can't make pipe for recovery"));
455 error(gettext(" Can't fork to execute recovery"));
458 unsigned char cryptkey
[19];
465 strcpy(cryptkey
, "CrYpTkEy=XXXXXXXXX");
466 strcpy(cryptkey
+ 9, key
);
467 if(putenv((char *)cryptkey
) != 0)
468 smerror(gettext(" Cannot copy key to environment"));
469 execlp(EXRECOVER
, "exrecover", "-x", svalue(vi_DIRECTORY
), file
, (char *) 0);
471 execlp(EXRECOVER
, "exrecover", svalue(vi_DIRECTORY
), file
, (char *) 0);
474 error(gettext(" No recovery routine"));
480 * Wait for the process (pid an external) to complete.
487 rpid
= waitpid(pid
, &status
, 0);
488 while (rpid
== (pid_t
)-1 && errno
!= ECHILD
);
489 if ((status
& 0377) == 0)
490 status
= (status
>> 8) & 0377;
494 * Reference order of arguments must not
495 * be changed using '%digit$', since vi's
496 * viprintf() does not support it.
498 viprintf(gettext("%d: terminated with signal %d"), pid
,
501 viprintf(gettext(" -- core dumped"));
507 * The end of a recover operation. If the process
508 * exits non-zero, force not edited; otherwise force
516 if (pid
== rpid
&& status
!= 0)