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 */ 25 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 30 /* Copyright (c) 1979 Regents of the University of California */ 31 32 #pragma ident "%Z%%M% %I% %E% SMI" 33 34 #include "ex.h" 35 #include "ex_temp.h" 36 #include "ex_tty.h" 37 #include "ex_vis.h" 38 39 extern int getchar(); 40 /* 41 * Unix escapes, filtering 42 */ 43 44 /* 45 * First part of a shell escape, 46 * parse the line, expanding # and % and ! and printing if implied. 47 */ 48 void 49 unix0(bool warn, int contcmd) 50 { 51 unsigned char *up, *fp; 52 short c; 53 char multic[MB_LEN_MAX + 1]; 54 int len; 55 int contread = 0; 56 wchar_t wc; 57 unsigned char printub, puxb[UXBSIZE + sizeof (int)]; 58 const char *specialchars = (contcmd ? "%#!\n" : "%#!"); 59 60 printub = 0; 61 CP(puxb, uxb); 62 c = peekchar(); 63 if (c == '\n' || c == EOF) { 64 (void) getchar(); 65 error(value(vi_TERSE) ? 66 gettext("Incomplete shell escape command") : 67 gettext("Incomplete shell escape command - use 'shell' to get a shell")); 68 } 69 up = (unsigned char *)uxb; 70 71 for (;;) { 72 if (!isascii(c)) { 73 if (c == EOF) 74 break; 75 if ((len = _mbftowc(multic, &wc, getchar, &peekc)) > 0) { 76 if ((up + len) >= (unsigned char *)&uxb[UXBSIZE]) { 77 uxb[0] = 0; 78 error(gettext("Command too long")); 79 } 80 strncpy(up, multic, len); 81 up += len; 82 goto loop_check; 83 } 84 } 85 86 (void) getchar(); 87 switch (c) { 88 89 case '\\': 90 if (any(peekchar(), specialchars)) { 91 c = getchar(); 92 /* 93 * If we encountered a backslash-escaped 94 * newline, and we're processing a continuation 95 * command, then continue processing until 96 * non-backslash-escaped newline is reached. 97 */ 98 if (contcmd && (c == '\n')) { 99 contread = 1; 100 } 101 } 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 he really meant ":!!!". So 142 * substitute the last command for him. 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 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 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 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 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 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 511 revocer(void) 512 { 513 514 waitfor(); 515 if (pid == rpid && status != 0) 516 edited = 0; 517 else 518 change(); 519 } 520