8354 sync regcomp(3C) with upstream (fix make catalog)
[unleashed/tickless.git] / usr / src / cmd / vi / port / ex_unix.c
blob5a050acf06c0187b69c5aee6e03d1a3a31114dd4
1 /*
2 * CDDL HEADER START
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]
19 * CDDL HEADER END
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"
35 #include "ex.h"
36 #include "ex_temp.h"
37 #include "ex_tty.h"
38 #include "ex_vis.h"
40 extern int getchar();
42 * Unix escapes, filtering
46 * First part of a shell escape,
47 * parse the line, expanding # and % and ! and printing if implied.
49 void
50 unix0(bool warn, int contcmd)
52 unsigned char *up, *fp;
53 short c;
54 char multic[MB_LEN_MAX + 1];
55 int len;
56 int contread = 0;
57 wchar_t wc;
58 unsigned char printub, puxb[UXBSIZE + sizeof (int)];
59 const char *specialchars = (contcmd ? "%#!\n" : "%#!");
61 printub = 0;
62 CP(puxb, uxb);
63 c = peekchar();
64 if (c == '\n' || c == EOF) {
65 (void) getchar();
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;
72 for (;;) {
73 if (!isascii(c)) {
74 if (c == EOF)
75 break;
76 if ((len = _mbftowc(multic, &wc, getchar, &peekc)) > 0) {
77 if ((up + len) >= (unsigned char *)&uxb[UXBSIZE]) {
78 uxb[0] = 0;
79 error(gettext("Command too long"));
81 strncpy(up, multic, len);
82 up += len;
83 goto loop_check;
87 (void) getchar();
88 switch (c) {
90 case '\\':
91 if (any(peekchar(), specialchars)) {
92 c = getchar();
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')) {
100 contread = 1;
103 default:
104 if (up >= (unsigned char *)&uxb[UXBSIZE]) {
105 tunix:
106 uxb[0] = 0;
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.
116 if (!tagflg)
117 *up++ = c;
118 break;
120 case '!':
121 if (up != (unsigned char *)uxb && *puxb != 0) {
122 fp = puxb;
123 if (*fp == 0) {
124 uxb[0] = 0;
125 error(value(vi_TERSE) ?
126 gettext("No previous command") :
127 gettext("No previous command to substitute for !"));
129 printub++;
130 while (*fp) {
131 if (up >= (unsigned char *)&uxb[UXBSIZE])
132 goto tunix;
133 *up++ = *fp++;
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.
145 fp = puxb;
146 if (*fp == 0) {
147 uxb[0] = 0;
148 error(value(vi_TERSE) ?
149 gettext("No previous command") :
150 gettext("No previous command to substitute for !"));
152 printub++;
153 while (*fp) {
154 if (up >= (unsigned char *)&uxb[UXBSIZE])
155 goto tunix;
156 *up++ = *fp++;
158 } else {
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]) {
166 uxb[0] = 0;
167 error(gettext("Command too long"));
169 *up++ = c;
171 break;
173 case '#':
174 fp = (unsigned char *)altfile;
175 if (*fp == 0) {
176 uxb[0] = 0;
177 error(value(vi_TERSE) ?
178 gettext("No alternate filename") :
179 gettext("No alternate filename to substitute for #"));
181 goto uexp;
183 case '%':
184 fp = savedfile;
185 if (*fp == 0) {
186 uxb[0] = 0;
187 error(value(vi_TERSE) ?
188 gettext("No filename") :
189 gettext("No filename to substitute for %%"));
191 uexp:
192 printub++;
193 while (*fp) {
194 if (up >= (unsigned char *)&uxb[UXBSIZE])
195 goto tunix;
196 *up++ = *fp++;
198 break;
201 loop_check:
202 c = peekchar();
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.
211 contread = 0;
212 continue;
213 } else {
214 (void) getchar();
215 break;
218 if (c == EOF)
219 ungetchar(c);
220 *up = 0;
221 if (!inopen)
222 resetflav();
223 if (warn)
224 ckaw();
225 if (warn && hush == 0 && chng && xchng != chng && value(vi_WARN) && dol > zero) {
226 xchng = chng;
227 vnfl();
228 viprintf(mesg(value(vi_TERSE) ? gettext("[No write]") :
229 gettext("[No write since last change]")));
230 noonl();
231 flush();
232 } else
233 warn = 0;
234 if (printub) {
235 if (uxb[0] == 0)
236 error(value(vi_TERSE) ? gettext("No previous command") :
237 gettext("No previous command to repeat"));
238 if (inopen) {
239 splitw++;
240 vclean();
241 vgoto(WECHO, 0);
243 if (warn)
244 vnfl();
245 if (hush == 0)
246 lprintf("!%s", uxb);
247 if (inopen && Outchar != termchar) {
248 vclreol();
249 vgoto(WECHO, 0);
250 } else
251 putnl();
252 flush();
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.
262 ttymode
263 unixex(opt, up, newstdin, mode)
264 unsigned char *opt, *up;
265 int newstdin, mode;
267 int pvec[2];
268 ttymode f;
270 signal(SIGINT, SIG_IGN);
271 #ifdef SIGTSTP
272 if (dosusp)
273 signal(SIGTSTP, SIG_DFL);
274 #endif
275 if (inopen)
276 f = setty(normf);
277 if ((mode & 1) && pipe(pvec) < 0) {
278 /* Newstdin should be io so it will be closed */
279 if (inopen)
280 setty(f);
281 error(gettext("Can't make pipe for filter"));
283 #ifndef VFORK
284 pid = fork();
285 #else
286 pid = vfork();
287 #endif
288 if (pid < 0) {
289 if (mode & 1) {
290 close(pvec[0]);
291 close(pvec[1]);
293 setrupt();
294 if (inopen)
295 setty(f);
296 error(gettext("No more processes"));
298 if (pid == 0) {
299 if (mode & 2) {
300 close(0);
301 dup(newstdin);
302 close(newstdin);
304 if (mode & 1) {
305 close(pvec[0]);
306 close(1);
307 dup(pvec[1]);
308 if (inopen) {
309 close(2);
310 dup(1);
312 close(pvec[1]);
314 if (io)
315 close(io);
316 if (tfile)
317 close(tfile);
318 signal(SIGHUP, oldhup);
319 signal(SIGQUIT, oldquit);
320 if (ruptible)
321 signal(SIGINT, SIG_DFL);
322 execlp((char *)svalue(vi_SHELL), (char *)svalue(vi_SHELL),
323 opt, up, (char *)0);
324 viprintf(gettext("Invalid SHELL value: %s\n"),
325 svalue(vi_SHELL));
326 flush();
327 error(NOSTR);
329 if (mode & 1) {
330 io = pvec[0];
331 close(pvec[1]);
333 if (newstdin)
334 close(newstdin);
335 return (f);
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.
343 void
344 unixwt(c, f)
345 bool c;
346 ttymode f;
349 waitfor();
350 #ifdef SIGTSTP
351 if (dosusp)
352 signal(SIGTSTP, onsusp);
353 #endif
354 if (inopen)
355 setty(f);
356 setrupt();
357 if (!inopen && c && hush == 0) {
358 viprintf("!\n");
359 flush();
360 termreset();
361 gettmode();
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.
372 vi_filter(int mode)
374 static int pvec[2];
375 ttymode f; /* was register */
376 int nlines = lineDOL();
377 int status2;
378 pid_t pid2 = 0;
380 mode++;
381 if (mode & 2) {
382 signal(SIGINT, SIG_IGN);
383 signal(SIGPIPE, SIG_IGN);
384 if (pipe(pvec) < 0)
385 error(gettext("Can't make pipe"));
386 pid2 = fork();
387 io = pvec[0];
388 if (pid < 0) {
389 setrupt();
390 close(pvec[1]);
391 error(gettext("No more processes"));
393 if (pid2 == 0) {
394 extern unsigned char tfname[];
395 setrupt();
396 io = pvec[1];
397 close(pvec[0]);
399 /* To prevent seeking in this process and the
400 parent, we must reopen tfile here */
401 close(tfile);
402 tfile = open(tfname, 2);
404 putfile(1);
405 exit(errcnt);
407 close(pvec[1]);
408 io = pvec[0];
409 setrupt();
411 f = unixex("-c", uxb, (mode & 2) ? pvec[0] : 0, mode);
412 if (mode == 3) {
413 (void) delete(0);
414 addr2 = addr1 - 1;
416 if (mode == 1)
417 deletenone();
418 if (mode & 1) {
419 if(FIXUNDO)
420 undap1 = undap2 = addr2+1;
421 (void)append(getfile, addr2);
422 #ifdef UNDOTRACE
423 if (trace)
424 vudump(gettext("after append in filter"));
425 #endif
427 close(io);
428 io = -1;
429 unixwt(!inopen, f);
430 if (pid2) {
431 (void)kill(pid2, 9);
433 rpid = waitpid(pid2, &status2, 0);
434 while (rpid == (pid_t)-1 && errno == EINTR);
436 netchHAD(nlines);
437 return (0);
441 * Set up to do a recover, getting io to be a pipe from
442 * the recover process.
444 void
445 recover(void)
447 static int pvec[2];
449 if (pipe(pvec) < 0)
450 error(gettext(" Can't make pipe for recovery"));
451 pid = fork();
452 io = pvec[0];
453 if (pid < 0) {
454 close(pvec[1]);
455 error(gettext(" Can't fork to execute recovery"));
457 if (pid == 0) {
458 unsigned char cryptkey[19];
459 close(2);
460 dup(1);
461 close(1);
462 dup(pvec[1]);
463 close(pvec[1]);
464 if(xflag) {
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);
470 } else
471 execlp(EXRECOVER, "exrecover", svalue(vi_DIRECTORY), file, (char *) 0);
472 close(1);
473 dup(2);
474 error(gettext(" No recovery routine"));
476 close(pvec[1]);
480 * Wait for the process (pid an external) to complete.
482 void
483 waitfor(void)
487 rpid = waitpid(pid, &status, 0);
488 while (rpid == (pid_t)-1 && errno != ECHILD);
489 if ((status & 0377) == 0)
490 status = (status >> 8) & 0377;
491 else {
493 * TRANSLATION_NOTE
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,
499 status & 0177);
500 if (status & 0200)
501 viprintf(gettext(" -- core dumped"));
502 putchar('\n');
507 * The end of a recover operation. If the process
508 * exits non-zero, force not edited; otherwise force
509 * a write.
511 void
512 revocer(void)
515 waitfor();
516 if (pid == rpid && status != 0)
517 edited = 0;
518 else
519 change();