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 /* Copyright (c) 1981 Regents of the University of California */
  30 
  31 #pragma ident   "%Z%%M% %I%     %E% SMI"
  32 
  33 #include "ex.h"
  34 #include "ex_argv.h"
  35 #include "ex_temp.h"
  36 #include "ex_tty.h"
  37 #include <stdlib.h>
  38 #include <locale.h>
  39 #include <stdio.h>
  40 #ifdef TRACE
  41 unsigned char   tttrace[BUFSIZ];
  42 #endif
  43 
  44 #define EQ(a, b)        (strcmp(a, b) == 0)
  45 
  46 char    *strrchr();
  47 void    init_re(void);
  48 
  49 /*
  50  * The code for ex is divided as follows:
  51  *
  52  * ex.c                 Entry point and routines handling interrupt, hangup
  53  *                      signals; initialization code.
  54  *
  55  * ex_addr.c            Address parsing routines for command mode decoding.
  56  *                      Routines to set and check address ranges on commands.
  57  *
  58  * ex_cmds.c            Command mode command decoding.
  59  *
  60  * ex_cmds2.c           Subroutines for command decoding and processing of
  61  *                      file names in the argument list.  Routines to print
  62  *                      messages and reset state when errors occur.
  63  *
  64  * ex_cmdsub.c          Subroutines which implement command mode functions
  65  *                      such as append, delete, join.
  66  *
  67  * ex_data.c            Initialization of options.
  68  *
  69  * ex_get.c             Command mode input routines.
  70  *
  71  * ex_io.c              General input/output processing: file i/o, unix
  72  *                      escapes, filtering, source commands, preserving
  73  *                      and recovering.
  74  *
  75  * ex_put.c             Terminal driving and optimizing routines for low-level
  76  *                      output (cursor-positioning); output line formatting
  77  *                      routines.
  78  *
  79  * ex_re.c              Global commands, substitute, regular expression
  80  *                      compilation and execution.
  81  *
  82  * ex_set.c             The set command.
  83  *
  84  * ex_subr.c            Loads of miscellaneous subroutines.
  85  *
  86  * ex_temp.c            Editor buffer routines for main buffer and also
  87  *                      for named buffers (Q registers if you will.)
  88  *
  89  * ex_tty.c             Terminal dependent initializations from termcap
  90  *                      data base, grabbing of tty modes (at beginning
  91  *                      and after escapes).
  92  *
  93  * ex_unix.c            Routines for the ! command and its variations.
  94  *
  95  * ex_v*.c              Visual/open mode routines... see ex_v.c for a
  96  *                      guide to the overall organization.
  97  */
  98 
  99 /*
 100  * This sets the Version of ex/vi for both the exstrings file and
 101  * the version command (":ver").
 102  */
 103 
 104 /* variable used by ":ver" command */
 105 unsigned char *Version = (unsigned char *)"Version SVR4.0, Solaris 2.5.0";
 106 
 107 /*
 108  * NOTE: when changing the Version number, it must be changed in the
 109  *       following files:
 110  *
 111  *                        port/READ_ME
 112  *                        port/ex.c
 113  *                        port/ex.news
 114  *
 115  */
 116 #ifdef XPG4
 117 unsigned char *savepat = (unsigned char *) NULL; /* firstpat storage    */
 118 #endif /* XPG4 */
 119 
 120 /*
 121  * Main procedure.  Process arguments and then
 122  * transfer control to the main command processing loop
 123  * in the routine commands.  We are entered as either "ex", "edit", "vi"
 124  * or "view" and the distinction is made here. For edit we just diddle options;
 125  * for vi we actually force an early visual command.
 126  */
 127 static unsigned char cryptkey[19]; /* contents of encryption key */
 128 
 129 static void usage(unsigned char *);
 130 
 131 static int validate_exrc(unsigned char *);
 132 
 133 void init(void);
 134 
 135 int
 136 main(int ac, char *av[])
 137 {
 138         extern  char    *optarg;
 139         extern  int     optind;
 140         unsigned char   *rcvname = 0;
 141         unsigned char *cp;
 142         int c;
 143         unsigned char   *cmdnam;
 144         bool recov = 0;
 145         bool ivis = 0;
 146         bool itag = 0;
 147         bool fast = 0;
 148         extern int verbose;
 149         int argcounter = 0;
 150         extern int tags_flag;   /* Set if tag file is not sorted (-S flag) */
 151         unsigned char scratch [PATH_MAX+1];   /* temp for sourcing rc file(s) */
 152         int vret = 0;
 153         unsigned char exrcpath [PATH_MAX+1]; /* temp for sourcing rc file(s) */
 154         int toptseen = 0;
 155 #ifdef TRACE
 156         unsigned char *tracef;
 157 #endif
 158         tagflg = 0;
 159         (void) setlocale(LC_ALL, "");
 160 #if !defined(TEXT_DOMAIN)
 161 #define TEXT_DOMAIN "SYS_TEST"
 162 #endif
 163         (void) textdomain(TEXT_DOMAIN);
 164 
 165         /*
 166          * Immediately grab the tty modes so that we won't
 167          * get messed up if an interrupt comes in quickly.
 168          */
 169         (void) gTTY(2);
 170         normf = tty;
 171         ppid = getpid();
 172         /* Note - this will core dump if you didn't -DSINGLE in CFLAGS */
 173         lines = 24;
 174         columns = 80;   /* until defined right by setupterm */
 175         /*
 176          * Defend against d's, v's, w's, and a's in directories of
 177          * path leading to our true name.
 178          */
 179         if ((cmdnam = (unsigned char *)strrchr(av[0], '/')) != 0)
 180                 cmdnam++;
 181         else
 182                 cmdnam = (unsigned char *)av[0];
 183 
 184         if (EQ((char *)cmdnam, "vi"))
 185                 ivis = 1;
 186         else if (EQ(cmdnam, "view")) {
 187                 ivis = 1;
 188                 value(vi_READONLY) = 1;
 189         } else if (EQ(cmdnam, "vedit")) {
 190                 ivis = 1;
 191                 value(vi_NOVICE) = 1;
 192                 value(vi_REPORT) = 1;
 193                 value(vi_MAGIC) = 0;
 194                 value(vi_SHOWMODE) = 1;
 195         } else if (EQ(cmdnam, "edit")) {
 196                 value(vi_NOVICE) = 1;
 197                 value(vi_REPORT) = 1;
 198                 value(vi_MAGIC) = 0;
 199                 value(vi_SHOWMODE) = 1;
 200         }
 201 
 202 #ifdef  XPG4
 203         {
 204                 struct winsize jwin;
 205                 char *envptr;
 206 
 207                 envlines = envcolumns = -1;
 208                 oldlines = oldcolumns = -1;
 209 
 210                 if (ioctl(0, TIOCGWINSZ, &jwin) != -1) {
 211                         oldlines = jwin.ws_row;
 212                         oldcolumns = jwin.ws_col;
 213                 }
 214 
 215                 if ((envptr = getenv("LINES")) != NULL &&
 216                     *envptr != '\0' && isdigit(*envptr)) {
 217                         if ((envlines = atoi(envptr)) <= 0) {
 218                                 envlines = -1;
 219                         }
 220                 }
 221 
 222                 if ((envptr = getenv("COLUMNS")) != NULL &&
 223                     *envptr != '\0' && isdigit(*envptr)) {
 224                         if ((envcolumns = atoi(envptr)) <= 0) {
 225                                 envcolumns = -1;
 226                         }
 227                 }
 228         }
 229 #endif /* XPG4 */
 230 
 231         draino();
 232         pstop();
 233 
 234         /*
 235          * Initialize interrupt handling.
 236          */
 237         oldhup = signal(SIGHUP, SIG_IGN);
 238         if (oldhup == SIG_DFL)
 239                 signal(SIGHUP, onhup);
 240         oldquit = signal(SIGQUIT, SIG_IGN);
 241         ruptible = signal(SIGINT, SIG_IGN) == SIG_DFL;
 242         if (signal(SIGTERM, SIG_IGN) == SIG_DFL)
 243                 signal(SIGTERM, onhup);
 244         if (signal(SIGEMT, SIG_IGN) == SIG_DFL)
 245                 signal(SIGEMT, onemt);
 246         signal(SIGILL, oncore);
 247         signal(SIGTRAP, oncore);
 248         signal(SIGIOT, oncore);
 249         signal(SIGFPE, oncore);
 250         signal(SIGBUS, oncore);
 251         signal(SIGSEGV, oncore);
 252         signal(SIGPIPE, oncore);
 253         init_re();
 254         while (1) {
 255 #ifdef TRACE
 256                 while ((c = getopt(ac, (char **)av, "VU:Lc:Tvt:rlw:xRCsS")) !=
 257                     EOF)
 258 #else
 259                 while ((c = getopt(ac, (char **)av,
 260                     "VLc:vt:rlw:xRCsS")) != EOF)
 261 #endif
 262                         switch (c) {
 263                         case 's':
 264                                 hush = 1;
 265                                 value(vi_AUTOPRINT) = 0;
 266                                 fast++;
 267                                 break;
 268 
 269                         case 'R':
 270                                 value(vi_READONLY) = 1;
 271                                 break;
 272                         case 'S':
 273                                 tags_flag = 1;
 274                                 break;
 275 #ifdef TRACE
 276                         case 'T':
 277                                 tracef = (unsigned char *)"trace";
 278                                 goto trace;
 279 
 280                         case 'U':
 281                                 tracef = tttrace;
 282                                 strcpy(tracef, optarg);
 283                 trace:
 284                                 trace = fopen((char *)tracef, "w");
 285 #define tracbuf NULL
 286                                 if (trace == NULL)
 287                                         viprintf("Trace create error\n");
 288                                 else
 289                                         setbuf(trace, (char *)tracbuf);
 290                                 break;
 291 #endif
 292                         case 'c':
 293                                 if (optarg != NULL)
 294                                         firstpat = (unsigned char *)optarg;
 295                                 else
 296                                         firstpat = (unsigned char *)"";
 297                                 break;
 298 
 299                         case 'l':
 300                                 value(vi_LISP) = 1;
 301                                 value(vi_SHOWMATCH) = 1;
 302                                 break;
 303 
 304                         case 'r':
 305                                 if (av[optind] && (c = av[optind][0]) &&
 306                                     c != '-') {
 307                                         if ((strlen(av[optind])) >=
 308                                             sizeof (savedfile)) {
 309                                                 (void) fprintf(stderr, gettext(
 310                                                     "Recovered file name"
 311                                                     " too long\n"));
 312                                                 exit(1);
 313                                         }
 314 
 315                                         rcvname = (unsigned char *)av[optind];
 316                                         optind++;
 317                                 }
 318 
 319                         case 'L':
 320                                 recov++;
 321                                 break;
 322 
 323                         case 'V':
 324                                 verbose = 1;
 325                                 break;
 326 
 327                         case 't':
 328                                 if (toptseen) {
 329                                         usage(cmdnam);
 330                                         exit(1);
 331                                 } else {
 332                                         toptseen++;
 333                                 }
 334                                 itag = tagflg = 1; /* -t option */
 335                                 if (strlcpy(lasttag, optarg,
 336                                     sizeof (lasttag)) >= sizeof (lasttag)) {
 337                                         (void) fprintf(stderr, gettext("Tag"
 338                                             " file name too long\n"));
 339                                         exit(1);
 340                                 }
 341                                 break;
 342 
 343                         case 'w':
 344                                 defwind = 0;
 345                                 if (optarg[0] == NULL)
 346                                         defwind = 3;
 347                                 else for (cp = (unsigned char *)optarg;
 348                                     isdigit(*cp); cp++)
 349                                         defwind = 10*defwind + *cp - '0';
 350                                 if (defwind < 0)
 351                                         defwind = 3;
 352                                 break;
 353 
 354                         case 'C':
 355                                 crflag = 1;
 356                                 xflag = 1;
 357                                 break;
 358 
 359                         case 'x':
 360                                 /* encrypted mode */
 361                                 xflag = 1;
 362                                 crflag = -1;
 363                                 break;
 364 
 365                         case 'v':
 366                                 ivis = 1;
 367                                 break;
 368 
 369                         default:
 370                                 usage(cmdnam);
 371                                 exit(1);
 372                         }
 373                 if (av[optind] && av[optind][0] == '+' &&
 374                     av[optind-1] && strcmp(av[optind-1], "--")) {
 375                                 firstpat = (unsigned char *)&av[optind][1];
 376                                 optind++;
 377                                 continue;
 378                 } else if (av[optind] && av[optind][0] == '-' &&
 379                     av[optind-1] && strcmp(av[optind-1], "--")) {
 380                         hush = 1;
 381                         value(vi_AUTOPRINT) = 0;
 382                         fast++;
 383                         optind++;
 384                         continue;
 385                 }
 386                 break;
 387         }
 388 
 389         if (isatty(0) == 0) {
 390                 /*
 391                  * If -V option is set and input is coming in via
 392                  * stdin then vi behavior should be ignored. The vi
 393                  * command should act like ex and only process ex commands
 394                  * and echo the input ex commands to stderr
 395                  */
 396                 if (verbose == 1) {
 397                         ivis = 0;
 398                 }
 399 
 400                 /*
 401                  * If the standard input is not a terminal device,
 402                  * it is as if the -s option has been specified.
 403                  */
 404                 if (ivis == 0) {
 405                         hush = 1;
 406                         value(vi_AUTOPRINT) = 0;
 407                         fast++;
 408                 }
 409         }
 410 
 411         ac -= optind;
 412         av  = &av[optind];
 413 
 414         for (argcounter = 0; argcounter < ac; argcounter++) {
 415                 if ((strlen(av[argcounter])) >= sizeof (savedfile)) {
 416                         (void) fprintf(stderr, gettext("File argument"
 417                             " too long\n"));
 418                         exit(1);
 419                 }
 420         }
 421 
 422 #ifdef SIGTSTP
 423         if (!hush && signal(SIGTSTP, SIG_IGN) == SIG_DFL)
 424                 signal(SIGTSTP, onsusp), dosusp++;
 425 #endif
 426 
 427         if (xflag) {
 428                 permflag = 1;
 429                 if ((kflag = run_setkey(perm,
 430                     (key = (unsigned char *)getpass(
 431                     gettext("Enter key:"))))) == -1) {
 432                         kflag = 0;
 433                         xflag = 0;
 434                         smerror(gettext("Encryption facility not available\n"));
 435                 }
 436                 if (kflag == 0)
 437                         crflag = 0;
 438                 else {
 439                         strcpy(cryptkey, "CrYpTkEy=XXXXXXXXX");
 440                         strcpy(cryptkey + 9, key);
 441                         if (putenv((char *)cryptkey) != 0)
 442                         smerror(gettext(" Cannot copy key to environment"));
 443                 }
 444 
 445         }
 446 #ifndef PRESUNEUC
 447         /*
 448          * Perform locale-specific initialization
 449          */
 450         localize();
 451 #endif /* PRESUNEUC */
 452 
 453         /*
 454          * Initialize end of core pointers.
 455          * Normally we avoid breaking back to fendcore after each
 456          * file since this can be expensive (much core-core copying).
 457          * If your system can scatter load processes you could do
 458          * this as ed does, saving a little core, but it will probably
 459          * not often make much difference.
 460          */
 461         fendcore = (line *) sbrk(0);
 462         endcore = fendcore - 2;
 463 
 464         /*
 465          * If we are doing a recover and no filename
 466          * was given, then execute an exrecover command with
 467          * the -r option to type out the list of saved file names.
 468          * Otherwise set the remembered file name to the first argument
 469          * file name so the "recover" initial command will find it.
 470          */
 471         if (recov) {
 472                 if (ac == 0 && (rcvname == NULL || *rcvname == NULL)) {
 473                         ppid = 0;
 474                         setrupt();
 475                         execlp(EXRECOVER, "exrecover", "-r", (char *)0);
 476                         filioerr(EXRECOVER);
 477                         exit(++errcnt);
 478                 }
 479                 if (rcvname && *rcvname)
 480                         (void) strlcpy(savedfile, rcvname, sizeof (savedfile));
 481                 else {
 482                         (void) strlcpy(savedfile, *av++, sizeof (savedfile));
 483                         ac--;
 484                 }
 485         }
 486 
 487         /*
 488          * Initialize the argument list.
 489          */
 490         argv0 = (unsigned char **)av;
 491         argc0 = ac;
 492         args0 = (unsigned char *)av[0];
 493         erewind();
 494 
 495         /*
 496          * Initialize a temporary file (buffer) and
 497          * set up terminal environment.  Read user startup commands.
 498          */
 499         if (setexit() == 0) {
 500                 setrupt();
 501                 intty = isatty(0);
 502                 value(vi_PROMPT) = intty;
 503                 if (((cp = (unsigned char *)getenv("SHELL")) != NULL) &&
 504                     (*cp != '\0')) {
 505                         if (strlen(cp) < sizeof (shell)) {
 506                                 (void) strlcpy(shell, cp, sizeof (shell));
 507                         }
 508                 }
 509                 if (fast)
 510                         setterm((unsigned char *)"dumb");
 511                 else {
 512                         gettmode();
 513                         cp = (unsigned char *)getenv("TERM");
 514                         if (cp == NULL || *cp == '\0')
 515                                 cp = (unsigned char *)"unknown";
 516                         setterm(cp);
 517                 }
 518         }
 519 
 520         /*
 521          * Bring up some code from init()
 522          * This is still done in init later. This
 523          * avoids null pointer problems
 524          */
 525 
 526         dot = zero = truedol = unddol = dol = fendcore;
 527         one = zero+1;
 528         {
 529         int i;
 530 
 531         for (i = 0; i <= 'z'-'a'+1; i++)
 532                 names[i] = 1;
 533         }
 534 
 535         if (setexit() == 0 && !fast) {
 536                 if ((globp =
 537                     (unsigned char *) getenv("EXINIT")) && *globp) {
 538                         if (ivis)
 539                                 inexrc = 1;
 540                         commands(1, 1);
 541                         inexrc = 0;
 542                 } else {
 543                         globp = 0;
 544                         if ((cp = (unsigned char *) getenv("HOME")) !=
 545                             0 && *cp) {
 546                                 strncpy(scratch, cp, sizeof (scratch) - 1);
 547                                 strncat(scratch, "/.exrc",
 548                                     sizeof (scratch) - 1 - strlen(scratch));
 549                                 if (ivis)
 550                                         inexrc = 1;
 551                                 if ((vret = validate_exrc(scratch)) == 0) {
 552                                         source(scratch, 1);
 553                                 } else {
 554                                         if (vret == -1) {
 555                                                 error(gettext(
 556                                                     "Not owner of .exrc "
 557                                                     "or .exrc is group or "
 558                                                     "world writable"));
 559                                         }
 560                                 }
 561                                 inexrc = 0;
 562                         }
 563                 }
 564 
 565                 /*
 566                  * Allow local .exrc if the "exrc" option was set. This
 567                  * loses if . is $HOME, but nobody should notice unless
 568                  * they do stupid things like putting a version command
 569                  * in .exrc.
 570                  * Besides, they should be using EXINIT, not .exrc, right?
 571                  */
 572 
 573                 if (value(vi_EXRC)) {
 574                         if (ivis)
 575                                 inexrc = 1;
 576                         if ((cp = (unsigned char *) getenv("PWD")) != 0 &&
 577                             *cp) {
 578                                 strncpy(exrcpath, cp, sizeof (exrcpath) - 1);
 579                                 strncat(exrcpath, "/.exrc",
 580                                     sizeof (exrcpath) - 1 - strlen(exrcpath));
 581                                 if (strcmp(scratch, exrcpath) != 0) {
 582                                         if ((vret =
 583                                             validate_exrc(exrcpath)) == 0) {
 584                                                 source(exrcpath, 1);
 585                                         } else {
 586                                                 if (vret == -1) {
 587                                                         error(gettext(
 588                                                             "Not owner of "
 589                                                             ".exrc or .exrc "
 590                                                             "is group or world "
 591                                                             "writable"));
 592                                                 }
 593                                         }
 594                                 }
 595                         }
 596                         inexrc = 0;
 597                 }
 598         }
 599 
 600         init(); /* moved after prev 2 chunks to fix directory option */
 601 
 602         /*
 603          * Initial processing.  Handle tag, recover, and file argument
 604          * implied next commands.  If going in as 'vi', then don't do
 605          * anything, just set initev so we will do it later (from within
 606          * visual).
 607          */
 608         if (setexit() == 0) {
 609                 if (recov)
 610                         globp = (unsigned char *)"recover";
 611                 else if (itag) {
 612                         globp = ivis ? (unsigned char *)"tag" :
 613                             (unsigned char *)"tag|p";
 614 #ifdef  XPG4
 615                         if (firstpat != NULL) {
 616                                 /*
 617                                  * if the user specified the -t and -c
 618                                  * flags together, then we service these
 619                                  * commands here. -t is handled first.
 620                                  */
 621                                 savepat = firstpat;
 622                                 firstpat = NULL;
 623                                 inglobal = 1;
 624                                 commands(1, 1);
 625 
 626                                 /* now handle the -c argument: */
 627                                 globp = savepat;
 628                                 commands(1, 1);
 629                                 inglobal = 0;
 630                                 globp = savepat = NULL;
 631 
 632                                 /* the above isn't sufficient for ex mode: */
 633                                 if (!ivis) {
 634                                         setdot();
 635                                         nonzero();
 636                                         plines(addr1, addr2, 1);
 637                                 }
 638                         }
 639 #endif /* XPG4 */
 640                 } else if (argc)
 641                         globp = (unsigned char *)"next";
 642                 if (ivis)
 643                         initev = globp;
 644                 else if (globp) {
 645                         inglobal = 1;
 646                         commands(1, 1);
 647                         inglobal = 0;
 648                 }
 649         }
 650 
 651         /*
 652          * Vi command... go into visual.
 653          */
 654         if (ivis) {
 655                 /*
 656                  * Don't have to be upward compatible
 657                  * by starting editing at line $.
 658                  */
 659 #ifdef  XPG4
 660                 if (!itag && (dol > zero))
 661 #else /* XPG4 */
 662                 if (dol > zero)
 663 #endif /* XPG4 */
 664                         dot = one;
 665                 globp = (unsigned char *)"visual";
 666                 if (setexit() == 0)
 667                         commands(1, 1);
 668         }
 669 
 670         /*
 671          * Clear out trash in state accumulated by startup,
 672          * and then do the main command loop for a normal edit.
 673          * If you quit out of a 'vi' command by doing Q or ^\,
 674          * you also fall through to here.
 675          */
 676         seenprompt = 1;
 677         ungetchar(0);
 678         globp = 0;
 679         initev = 0;
 680         setlastchar('\n');
 681         setexit();
 682         commands(0, 0);
 683         cleanup(1);
 684         return (errcnt);
 685 }
 686 
 687 /*
 688  * Initialization, before editing a new file.
 689  * Main thing here is to get a new buffer (in fileinit),
 690  * rest is peripheral state resetting.
 691  */
 692 void
 693 init(void)
 694 {
 695         int i;
 696         void (*pstat)();
 697         fileinit();
 698         dot = zero = truedol = unddol = dol = fendcore;
 699         one = zero+1;
 700         undkind = UNDNONE;
 701         chng = 0;
 702         edited = 0;
 703         for (i = 0; i <= 'z'-'a'+1; i++)
 704                 names[i] = 1;
 705         anymarks = 0;
 706         if (xflag) {
 707                 xtflag = 1;
 708                 /* ignore SIGINT before crypt process */
 709                 pstat = signal(SIGINT, SIG_IGN);
 710                 if (tpermflag)
 711                         (void) crypt_close(tperm);
 712                 tpermflag = 1;
 713                 if (makekey(tperm) != 0) {
 714                         xtflag = 0;
 715                         smerror(gettext(
 716                             "Warning--Cannot encrypt temporary buffer\n"));
 717                 }
 718                 signal(SIGINT, pstat);
 719         }
 720 }
 721 
 722 /*
 723  * Return last component of unix path name p.
 724  */
 725 unsigned char *
 726 tailpath(p)
 727 unsigned char *p;
 728 {
 729         unsigned char *r;
 730 
 731         for (r = p; *p; p++)
 732                 if (*p == '/')
 733                         r = p+1;
 734         return (r);
 735 }
 736 
 737 
 738 /*
 739  * validate_exrc - verify .exrc as belonging to the user.
 740  * The file uid should match the process ruid,
 741  * and the file should be writable only by the owner.
 742  */
 743 static int
 744 validate_exrc(unsigned char *exrc_path)
 745 {
 746         struct stat64 exrc_stat;
 747         int process_uid;
 748 
 749         if (stat64((char *)exrc_path, &exrc_stat) == -1)
 750                 return (0); /* ignore if .exrec is not found */
 751         process_uid = geteuid();
 752         /* if not root, uid must match file owner */
 753         if (process_uid && process_uid != exrc_stat.st_uid)
 754                 return (-1);
 755         if ((exrc_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0)
 756                 return (-1);
 757         return (0);
 758 }
 759 
 760 /*
 761  * print usage message to stdout
 762  */
 763 static void
 764 usage(unsigned char *name)
 765 {
 766         char buf[160];
 767 
 768 #ifdef TRACE
 769         (void) snprintf(buf, sizeof (buf), gettext(
 770             "Usage: %s [- | -s] [-l] [-L] [-wn] "
 771             "[-R] [-S] [-r [file]] [-t tag] [-T] [-U tracefile]\n"
 772             "[-v] [-V] [-x] [-C] [+cmd | -c cmd] file...\n"), name);
 773 #else
 774         (void) snprintf(buf, sizeof (buf), gettext(
 775             "Usage: %s [- | -s] [-l] [-L] [-wn] "
 776             "[-R] [-S] [-r [file]] [-t tag]\n"
 777             "[-v] [-V] [-x] [-C] [+cmd | -c cmd] file...\n"), name);
 778 #endif
 779         (void) write(2, buf, strlen(buf));
 780 }