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 #pragma ident "%Z%%M% %I% %E% SMI" 34 35 #include "ex.h" 36 #include "ex_temp.h" 37 #include "ex_tty.h" 38 #include "ex_vis.h" 39 40 extern int getchar(); 41 /* 42 * Unix escapes, filtering 43 */ 44 45 /* 46 * First part of a shell escape, 47 * parse the line, expanding # and % and ! and printing if implied. 48 */ 49 void 50 unix0(bool warn, int contcmd) 51 { 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" : "%#!"); 60 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")); 69 } 70 up = (unsigned char *)uxb; 71 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")); 80 } 81 strncpy(up, multic, len); 82 up += len; 83 goto loop_check; 84 } 85 } 86 87 (void) getchar(); 88 switch (c) { 89 90 case '\\': 91 if (any(peekchar(), specialchars)) { 92 c = getchar(); 93 /* 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. 98 */ 99 if (contcmd && (c == '\n')) { 100 contread = 1; 101 } 102 } 103 default: 104 if (up >= (unsigned char *)&uxb[UXBSIZE]) { 105 tunix: 106 uxb[0] = 0; 107 error(gettext("Command too long")); 108 } 109 /* 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. 115 */ 116 if (!tagflg) 117 *up++ = c; 118 break; 119 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 !")); 128 } 129 printub++; 130 while (*fp) { 131 if (up >= (unsigned char *)&uxb[UXBSIZE]) 132 goto tunix; 133 *up++ = *fp++; 134 } 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 ":!") 139 * 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. 144 */ 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 !")); 151 } 152 printub++; 153 while (*fp) { 154 if (up >= (unsigned char *)&uxb[UXBSIZE]) 155 goto tunix; 156 *up++ = *fp++; 157 } 158 } else { 159 /* 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 "\!"). 164 */ 165 if (up >= (unsigned char *)&uxb[UXBSIZE]) { 166 uxb[0] = 0; 167 error(gettext("Command too long")); 168 } 169 *up++ = c; 170 } 171 break; 172 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 #")); 180 } 181 goto uexp; 182 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 %%")); 190 } 191 uexp: 192 printub++; 193 while (*fp) { 194 if (up >= (unsigned char *)&uxb[UXBSIZE]) 195 goto tunix; 196 *up++ = *fp++; 197 } 198 break; 199 } 200 201 loop_check: 202 c = peekchar(); 203 if (c == '"' || c == '|' || (contread > 0) || !endcmd(c)) { 204 /* 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. 210 */ 211 contread = 0; 212 continue; 213 } else { 214 (void) getchar(); 215 break; 216 } 217 } 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); 242 } 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(); 253 } 254 } 255 256 /* 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. 261 */ 262 ttymode 263 unixex(opt, up, newstdin, mode) 264 unsigned char *opt, *up; 265 int newstdin, mode; 266 { 267 int pvec[2]; 268 ttymode f; 269 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")); 282 } 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]); 292 } 293 setrupt(); 294 if (inopen) 295 setty(f); 296 error(gettext("No more processes")); 297 } 298 if (pid == 0) { 299 if (mode & 2) { 300 close(0); 301 dup(newstdin); 302 close(newstdin); 303 } 304 if (mode & 1) { 305 close(pvec[0]); 306 close(1); 307 dup(pvec[1]); 308 if (inopen) { 309 close(2); 310 dup(1); 311 } 312 close(pvec[1]); 313 } 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); 328 } 329 if (mode & 1) { 330 io = pvec[0]; 331 close(pvec[1]); 332 } 333 if (newstdin) 334 close(newstdin); 335 return (f); 336 } 337 338 /* 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. 342 */ 343 void 344 unixwt(c, f) 345 bool c; 346 ttymode f; 347 { 348 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(); 362 } 363 } 364 365 /* 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. 370 */ 371 int 372 vi_filter(int mode) 373 { 374 static int pvec[2]; 375 ttymode f; /* was register */ 376 int nlines = lineDOL(); 377 int status2; 378 pid_t pid2 = 0; 379 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")); 392 } 393 if (pid2 == 0) { 394 extern unsigned char tfname[]; 395 setrupt(); 396 io = pvec[1]; 397 close(pvec[0]); 398 399 /* To prevent seeking in this process and the 400 parent, we must reopen tfile here */ 401 close(tfile); 402 tfile = open(tfname, 2); 403 404 putfile(1); 405 exit(errcnt); 406 } 407 close(pvec[1]); 408 io = pvec[0]; 409 setrupt(); 410 } 411 f = unixex("-c", uxb, (mode & 2) ? pvec[0] : 0, mode); 412 if (mode == 3) { 413 (void) delete(0); 414 addr2 = addr1 - 1; 415 } 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 426 } 427 close(io); 428 io = -1; 429 unixwt(!inopen, f); 430 if (pid2) { 431 (void)kill(pid2, 9); 432 do 433 rpid = waitpid(pid2, &status2, 0); 434 while (rpid == (pid_t)-1 && errno == EINTR); 435 } 436 netchHAD(nlines); 437 return (0); 438 } 439 440 /* 441 * Set up to do a recover, getting io to be a pipe from 442 * the recover process. 443 */ 444 void 445 recover(void) 446 { 447 static int pvec[2]; 448 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")); 456 } 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")); 475 } 476 close(pvec[1]); 477 } 478 479 /* 480 * Wait for the process (pid an external) to complete. 481 */ 482 void 483 waitfor(void) 484 { 485 486 do 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 { 492 /* 493 * TRANSLATION_NOTE 494 * Reference order of arguments must not 495 * be changed using '%digit$', since vi's 496 * viprintf() does not support it. 497 */ 498 viprintf(gettext("%d: terminated with signal %d"), pid, 499 status & 0177); 500 if (status & 0200) 501 viprintf(gettext(" -- core dumped")); 502 putchar('\n'); 503 } 504 } 505 506 /* 507 * The end of a recover operation. If the process 508 * exits non-zero, force not edited; otherwise force 509 * a write. 510 */ 511 void 512 revocer(void) 513 { 514 515 waitfor(); 516 if (pid == rpid && status != 0) 517 edited = 0; 518 else 519 change(); 520 } 521