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 char 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 printf(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(svalue(vi_SHELL), svalue(vi_SHELL), opt, up, (char *) 0); 315 printf(gettext("Invalid SHELL value: %s\n"), svalue(vi_SHELL)); 316 flush(); 317 error(NOSTR); 318 } 319 if (mode & 1) { 320 io = pvec[0]; 321 close(pvec[1]); 322 } 323 if (newstdin) 324 close(newstdin); 325 return (f); 326 } 327 328 /* 329 * Wait for the command to complete. 330 * F is for restoration of tty mode if from open/visual. 331 * C flags suppression of printing. 332 */ 333 unixwt(c, f) 334 bool c; 335 ttymode f; 336 { 337 338 waitfor(); 339 #ifdef SIGTSTP 340 if (dosusp) 341 signal(SIGTSTP, onsusp); 342 #endif 343 if (inopen) 344 setty(f); 345 setrupt(); 346 if (!inopen && c && hush == 0) { 347 printf("!\n"); 348 flush(); 349 termreset(); 350 gettmode(); 351 } 352 } 353 354 /* 355 * Setup a pipeline for the filtration implied by mode 356 * which is like a open number. If input is required to 357 * the filter, then a child editor is created to write it. 358 * If output is catch it from io which is created by unixex. 359 */ 360 vi_filter(mode) 361 register int mode; 362 { 363 static int pvec[2]; 364 ttymode f; /* was register */ 365 register int nlines = lineDOL(); 366 int status2; 367 pid_t pid2 = 0; 368 369 mode++; 370 if (mode & 2) { 371 signal(SIGINT, SIG_IGN); 372 signal(SIGPIPE, SIG_IGN); 373 if (pipe(pvec) < 0) 374 error(gettext("Can't make pipe")); 375 pid2 = fork(); 376 io = pvec[0]; 377 if (pid < 0) { 378 setrupt(); 379 close(pvec[1]); 380 error(gettext("No more processes")); 381 } 382 if (pid2 == 0) { 383 extern unsigned char tfname[]; 384 setrupt(); 385 io = pvec[1]; 386 close(pvec[0]); 387 388 /* To prevent seeking in this process and the 389 parent, we must reopen tfile here */ 390 close(tfile); 391 tfile = open(tfname, 2); 392 393 putfile(1); 394 exit(errcnt); 395 } 396 close(pvec[1]); 397 io = pvec[0]; 398 setrupt(); 399 } 400 f = unixex("-c", uxb, (mode & 2) ? pvec[0] : 0, mode); 401 if (mode == 3) { 402 delete(0); 403 addr2 = addr1 - 1; 404 } 405 if (mode == 1) 406 deletenone(); 407 if (mode & 1) { 408 if(FIXUNDO) 409 undap1 = undap2 = addr2+1; 410 (void)append(getfile, addr2); 411 #ifdef UNDOTRACE 412 if (trace) 413 vudump(gettext("after append in filter")); 414 #endif 415 } 416 close(io); 417 io = -1; 418 unixwt(!inopen, f); 419 if (pid2) { 420 (void)kill(pid2, 9); 421 do 422 rpid = waitpid(pid2, &status2, 0); 423 while (rpid == (pid_t)-1 && errno == EINTR); 424 } 425 netchHAD(nlines); 426 } 427 428 /* 429 * Set up to do a recover, getting io to be a pipe from 430 * the recover process. 431 */ 432 recover() 433 { 434 static int pvec[2]; 435 436 if (pipe(pvec) < 0) 437 error(gettext(" Can't make pipe for recovery")); 438 pid = fork(); 439 io = pvec[0]; 440 if (pid < 0) { 441 close(pvec[1]); 442 error(gettext(" Can't fork to execute recovery")); 443 } 444 if (pid == 0) { 445 unsigned char cryptkey[19]; 446 close(2); 447 dup(1); 448 close(1); 449 dup(pvec[1]); 450 close(pvec[1]); 451 if(xflag) { 452 strcpy(cryptkey, "CrYpTkEy=XXXXXXXXX"); 453 strcpy(cryptkey + 9, key); 454 if(putenv((char *)cryptkey) != 0) 455 smerror(gettext(" Cannot copy key to environment")); 456 execlp(EXRECOVER, "exrecover", "-x", svalue(vi_DIRECTORY), file, (char *) 0); 457 } else 458 execlp(EXRECOVER, "exrecover", svalue(vi_DIRECTORY), file, (char *) 0); 459 close(1); 460 dup(2); 461 error(gettext(" No recovery routine")); 462 } 463 close(pvec[1]); 464 } 465 466 /* 467 * Wait for the process (pid an external) to complete. 468 */ 469 waitfor() 470 { 471 472 do 473 rpid = waitpid(pid, &status, 0); 474 while (rpid == (pid_t)-1 && errno != ECHILD); 475 if ((status & 0377) == 0) 476 status = (status >> 8) & 0377; 477 else { 478 /* 479 * TRANSLATION_NOTE 480 * Reference order of arguments must not 481 * be changed using '%digit$', since vi's 482 * printf() does not support it. 483 */ 484 printf(gettext("%d: terminated with signal %d"), pid, status & 0177); 485 if (status & 0200) 486 printf(gettext(" -- core dumped")); 487 putchar('\n'); 488 } 489 } 490 491 /* 492 * The end of a recover operation. If the process 493 * exits non-zero, force not edited; otherwise force 494 * a write. 495 */ 496 revocer() 497 { 498 499 waitfor(); 500 if (pid == rpid && status != 0) 501 edited = 0; 502 else 503 change(); 504 } 505