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 1995 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 unix0(warn) 50 bool warn; 51 { 52 register unsigned char *up, *fp; 53 register short c; 54 char multic[MB_LEN_MAX + 1]; 55 int len; 56 wchar_t wc; 57 unsigned char printub, puxb[UXBSIZE + sizeof (int)]; 58 59 printub = 0; 60 CP(puxb, uxb); 61 c = peekchar(); 62 if (c == '\n' || c == EOF) { 63 (void) getchar(); 64 error(value(vi_TERSE) ? 65 gettext("Incomplete shell escape command") : 66 gettext("Incomplete shell escape command - use 'shell' to get a shell")); 67 } 68 up = (unsigned char *)uxb; 69 70 for (;;) { 71 if (!isascii(c)) { 72 if (c == EOF) 73 break; 74 if ((len = _mbftowc(multic, &wc, getchar, &peekc)) > 0) { 75 if ((up + len) >= (unsigned char *)&uxb[UXBSIZE]) { 76 uxb[0] = 0; 77 error(gettext("Command too long")); 78 } 79 strncpy(up, multic, len); 80 up += len; 81 goto loop_check; 82 } 83 } 84 85 (void) getchar(); 86 switch (c) { 87 88 case '\\': 89 if (any(peekchar(), "%#!")) 90 c = getchar(); 91 default: 92 if (up >= (unsigned char *)&uxb[UXBSIZE]) { 93 tunix: 94 uxb[0] = 0; 95 error(gettext("Command too long")); 96 } 97 *up++ = c; 98 break; 99 100 case '!': 101 if (up != (unsigned char *)uxb && *puxb != 0) { 102 fp = puxb; 103 if (*fp == 0) { 104 uxb[0] = 0; 105 error(value(vi_TERSE) ? 106 gettext("No previous command") : 107 gettext("No previous command to substitute for !")); 108 } 109 printub++; 110 while (*fp) { 111 if (up >= (unsigned char *)&uxb[UXBSIZE]) 112 goto tunix; 113 *up++ = *fp++; 114 } 115 } else if (up == (unsigned char *)uxb) { 116 /* If up = uxb it means we are on the first 117 * character inside the shell command. 118 * (i.e., after the ":!") 119 * 120 * The user has just entered ":!!" which 121 * means that though there is only technically 122 * one '!' we know he really meant ":!!!". So 123 * substitute the last command for him. 124 */ 125 fp = puxb; 126 if (*fp == 0) { 127 uxb[0] = 0; 128 error(value(vi_TERSE) ? 129 gettext("No previous command") : 130 gettext("No previous command to substitute for !")); 131 } 132 printub++; 133 while (*fp) { 134 if (up >= (unsigned char *)&uxb[UXBSIZE]) 135 goto tunix; 136 *up++ = *fp++; 137 } 138 } else { 139 /* 140 * Treat a lone "!" as just a regular character 141 * so commands like "mail machine!login" will 142 * work as usual (i.e., the user doesn't need 143 * to dereference the "!" with "\!"). 144 */ 145 if (up >= (unsigned char *)&uxb[UXBSIZE]) { 146 uxb[0] = 0; 147 error(gettext("Command too long")); 148 } 149 *up++ = c; 150 } 151 break; 152 153 case '#': 154 fp = (unsigned char *)altfile; 155 if (*fp == 0) { 156 uxb[0] = 0; 157 error(value(vi_TERSE) ? 158 gettext("No alternate filename") : 159 gettext("No alternate filename to substitute for #")); 160 } 161 goto uexp; 162 163 case '%': 164 fp = savedfile; 165 if (*fp == 0) { 166 uxb[0] = 0; 167 error(value(vi_TERSE) ? 168 gettext("No filename") : 169 gettext("No filename to substitute for %%")); 170 } 171 uexp: 172 printub++; 173 while (*fp) { 174 if (up >= (unsigned char *)&uxb[UXBSIZE]) 175 goto tunix; 176 *up++ = *fp++; 177 } 178 break; 179 } 180 181 loop_check: 182 c = peekchar(); 183 if (c == '"' || c == '|' || !endcmd(c)) { 184 continue; 185 } else { 186 (void) getchar(); 187 break; 188 } 189 } 190 if (c == EOF) 191 ungetchar(c); 192 *up = 0; 193 if (!inopen) 194 resetflav(); 195 if (warn) 196 ckaw(); 197 if (warn && hush == 0 && chng && xchng != chng && value(vi_WARN) && dol > zero) { 198 xchng = chng; 199 vnfl(); 200 printf(mesg(value(vi_TERSE) ? gettext("[No write]") : 201 gettext("[No write since last change]"))); 202 noonl(); 203 flush(); 204 } else 205 warn = 0; 206 if (printub) { 207 if (uxb[0] == 0) 208 error(value(vi_TERSE) ? gettext("No previous command") : 209 gettext("No previous command to repeat")); 210 if (inopen) { 211 splitw++; 212 vclean(); 213 vgoto(WECHO, 0); 214 } 215 if (warn) 216 vnfl(); 217 if (hush == 0) 218 lprintf("!%s", uxb); 219 if (inopen && Outchar != termchar) { 220 vclreol(); 221 vgoto(WECHO, 0); 222 } else 223 putnl(); 224 flush(); 225 } 226 } 227 228 /* 229 * Do the real work for execution of a shell escape. 230 * Mode is like the number passed to open system calls 231 * and indicates filtering. If input is implied, newstdin 232 * must have been setup already. 233 */ 234 ttymode 235 unixex(opt, up, newstdin, mode) 236 unsigned char *opt, *up; 237 int newstdin, mode; 238 { 239 int pvec[2]; 240 ttymode f; 241 242 signal(SIGINT, SIG_IGN); 243 #ifdef SIGTSTP 244 if (dosusp) 245 signal(SIGTSTP, SIG_DFL); 246 #endif 247 if (inopen) 248 f = setty(normf); 249 if ((mode & 1) && pipe(pvec) < 0) { 250 /* Newstdin should be io so it will be closed */ 251 if (inopen) 252 setty(f); 253 error(gettext("Can't make pipe for filter")); 254 } 255 #ifndef VFORK 256 pid = fork(); 257 #else 258 pid = vfork(); 259 #endif 260 if (pid < 0) { 261 if (mode & 1) { 262 close(pvec[0]); 263 close(pvec[1]); 264 } 265 setrupt(); 266 if (inopen) 267 setty(f); 268 error(gettext("No more processes")); 269 } 270 if (pid == 0) { 271 if (mode & 2) { 272 close(0); 273 dup(newstdin); 274 close(newstdin); 275 } 276 if (mode & 1) { 277 close(pvec[0]); 278 close(1); 279 dup(pvec[1]); 280 if (inopen) { 281 close(2); 282 dup(1); 283 } 284 close(pvec[1]); 285 } 286 if (io) 287 close(io); 288 if (tfile) 289 close(tfile); 290 signal(SIGHUP, oldhup); 291 signal(SIGQUIT, oldquit); 292 if (ruptible) 293 signal(SIGINT, SIG_DFL); 294 execlp(svalue(vi_SHELL), svalue(vi_SHELL), opt, up, (char *) 0); 295 printf(gettext("Invalid SHELL value: %s\n"), svalue(vi_SHELL)); 296 flush(); 297 error(NOSTR); 298 } 299 if (mode & 1) { 300 io = pvec[0]; 301 close(pvec[1]); 302 } 303 if (newstdin) 304 close(newstdin); 305 return (f); 306 } 307 308 /* 309 * Wait for the command to complete. 310 * F is for restoration of tty mode if from open/visual. 311 * C flags suppression of printing. 312 */ 313 unixwt(c, f) 314 bool c; 315 ttymode f; 316 { 317 318 waitfor(); 319 #ifdef SIGTSTP 320 if (dosusp) 321 signal(SIGTSTP, onsusp); 322 #endif 323 if (inopen) 324 setty(f); 325 setrupt(); 326 if (!inopen && c && hush == 0) { 327 printf("!\n"); 328 flush(); 329 termreset(); 330 gettmode(); 331 } 332 } 333 334 /* 335 * Setup a pipeline for the filtration implied by mode 336 * which is like a open number. If input is required to 337 * the filter, then a child editor is created to write it. 338 * If output is catch it from io which is created by unixex. 339 */ 340 vi_filter(mode) 341 register int mode; 342 { 343 static int pvec[2]; 344 ttymode f; /* was register */ 345 register int nlines = lineDOL(); 346 int status2; 347 pid_t pid2 = 0; 348 349 mode++; 350 if (mode & 2) { 351 signal(SIGINT, SIG_IGN); 352 signal(SIGPIPE, SIG_IGN); 353 if (pipe(pvec) < 0) 354 error(gettext("Can't make pipe")); 355 pid2 = fork(); 356 io = pvec[0]; 357 if (pid < 0) { 358 setrupt(); 359 close(pvec[1]); 360 error(gettext("No more processes")); 361 } 362 if (pid2 == 0) { 363 extern unsigned char tfname[]; 364 setrupt(); 365 io = pvec[1]; 366 close(pvec[0]); 367 368 /* To prevent seeking in this process and the 369 parent, we must reopen tfile here */ 370 close(tfile); 371 tfile = open(tfname, 2); 372 373 putfile(1); 374 exit(errcnt); 375 } 376 close(pvec[1]); 377 io = pvec[0]; 378 setrupt(); 379 } 380 f = unixex("-c", uxb, (mode & 2) ? pvec[0] : 0, mode); 381 if (mode == 3) { 382 delete(0); 383 addr2 = addr1 - 1; 384 } 385 if (mode == 1) 386 deletenone(); 387 if (mode & 1) { 388 if(FIXUNDO) 389 undap1 = undap2 = addr2+1; 390 (void)append(getfile, addr2); 391 #ifdef UNDOTRACE 392 if (trace) 393 vudump(gettext("after append in filter")); 394 #endif 395 } 396 close(io); 397 io = -1; 398 unixwt(!inopen, f); 399 if (pid2) { 400 (void)kill(pid2, 9); 401 do 402 rpid = waitpid(pid2, &status2, 0); 403 while (rpid == (pid_t)-1 && errno == EINTR); 404 } 405 netchHAD(nlines); 406 } 407 408 /* 409 * Set up to do a recover, getting io to be a pipe from 410 * the recover process. 411 */ 412 recover() 413 { 414 static int pvec[2]; 415 416 if (pipe(pvec) < 0) 417 error(gettext(" Can't make pipe for recovery")); 418 pid = fork(); 419 io = pvec[0]; 420 if (pid < 0) { 421 close(pvec[1]); 422 error(gettext(" Can't fork to execute recovery")); 423 } 424 if (pid == 0) { 425 unsigned char cryptkey[19]; 426 close(2); 427 dup(1); 428 close(1); 429 dup(pvec[1]); 430 close(pvec[1]); 431 if(xflag) { 432 strcpy(cryptkey, "CrYpTkEy=XXXXXXXXX"); 433 strcpy(cryptkey + 9, key); 434 if(putenv((char *)cryptkey) != 0) 435 smerror(gettext(" Cannot copy key to environment")); 436 execlp(EXRECOVER, "exrecover", "-x", svalue(vi_DIRECTORY), file, (char *) 0); 437 } else 438 execlp(EXRECOVER, "exrecover", svalue(vi_DIRECTORY), file, (char *) 0); 439 close(1); 440 dup(2); 441 error(gettext(" No recovery routine")); 442 } 443 close(pvec[1]); 444 } 445 446 /* 447 * Wait for the process (pid an external) to complete. 448 */ 449 waitfor() 450 { 451 452 do 453 rpid = waitpid(pid, &status, 0); 454 while (rpid == (pid_t)-1 && errno != ECHILD); 455 if ((status & 0377) == 0) 456 status = (status >> 8) & 0377; 457 else { 458 /* 459 * TRANSLATION_NOTE 460 * Reference order of arguments must not 461 * be changed using '%digit$', since vi's 462 * printf() does not support it. 463 */ 464 printf(gettext("%d: terminated with signal %d"), pid, status & 0177); 465 if (status & 0200) 466 printf(gettext(" -- core dumped")); 467 putchar('\n'); 468 } 469 } 470 471 /* 472 * The end of a recover operation. If the process 473 * exits non-zero, force not edited; otherwise force 474 * a write. 475 */ 476 revocer() 477 { 478 479 waitfor(); 480 if (pid == rpid && status != 0) 481 edited = 0; 482 else 483 change(); 484 } 485