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