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.
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.
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]
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
29 /* Copyright (c) 1981 Regents of the University of California */
39 unsigned char tttrace
[BUFSIZ
];
42 #define EQ(a, b) (strcmp(a, b) == 0)
48 * The code for ex is divided as follows:
50 * ex.c Entry point and routines handling interrupt, hangup
51 * signals; initialization code.
53 * ex_addr.c Address parsing routines for command mode decoding.
54 * Routines to set and check address ranges on commands.
56 * ex_cmds.c Command mode command decoding.
58 * ex_cmds2.c Subroutines for command decoding and processing of
59 * file names in the argument list. Routines to print
60 * messages and reset state when errors occur.
62 * ex_cmdsub.c Subroutines which implement command mode functions
63 * such as append, delete, join.
65 * ex_data.c Initialization of options.
67 * ex_get.c Command mode input routines.
69 * ex_io.c General input/output processing: file i/o, unix
70 * escapes, filtering, source commands, preserving
73 * ex_put.c Terminal driving and optimizing routines for low-level
74 * output (cursor-positioning); output line formatting
77 * ex_re.c Global commands, substitute, regular expression
78 * compilation and execution.
80 * ex_set.c The set command.
82 * ex_subr.c Loads of miscellaneous subroutines.
84 * ex_temp.c Editor buffer routines for main buffer and also
85 * for named buffers (Q registers if you will.)
87 * ex_tty.c Terminal dependent initializations from termcap
88 * data base, grabbing of tty modes (at beginning
91 * ex_unix.c Routines for the ! command and its variations.
93 * ex_v*.c Visual/open mode routines... see ex_v.c for a
94 * guide to the overall organization.
98 * This sets the Version of ex/vi for both the exstrings file and
99 * the version command (":ver").
102 /* variable used by ":ver" command */
103 unsigned char *Version
= (unsigned char *)"Version SVR4.0, Solaris 2.5.0";
106 * NOTE: when changing the Version number, it must be changed in the
115 unsigned char *savepat
= (unsigned char *) NULL
; /* firstpat storage */
119 * Main procedure. Process arguments and then
120 * transfer control to the main command processing loop
121 * in the routine commands. We are entered as either "ex", "edit", "vi"
122 * or "view" and the distinction is made here. For edit we just diddle options;
123 * for vi we actually force an early visual command.
125 static unsigned char cryptkey
[19]; /* contents of encryption key */
127 static void usage(unsigned char *);
129 static int validate_exrc(unsigned char *);
134 main(int ac
, char *av
[])
138 unsigned char *rcvname
= 0;
141 unsigned char *cmdnam
;
148 extern int tags_flag
; /* Set if tag file is not sorted (-S flag) */
149 unsigned char scratch
[PATH_MAX
+1]; /* temp for sourcing rc file(s) */
151 unsigned char exrcpath
[PATH_MAX
+1]; /* temp for sourcing rc file(s) */
154 unsigned char *tracef
;
157 (void) setlocale(LC_ALL
, "");
158 #if !defined(TEXT_DOMAIN)
159 #define TEXT_DOMAIN "SYS_TEST"
161 (void) textdomain(TEXT_DOMAIN
);
164 * Immediately grab the tty modes so that we won't
165 * get messed up if an interrupt comes in quickly.
170 /* Note - this will core dump if you didn't -DSINGLE in CFLAGS */
172 columns
= 80; /* until defined right by setupterm */
174 * Defend against d's, v's, w's, and a's in directories of
175 * path leading to our true name.
177 if ((cmdnam
= (unsigned char *)strrchr(av
[0], '/')) != 0)
180 cmdnam
= (unsigned char *)av
[0];
182 if (EQ((char *)cmdnam
, "vi"))
184 else if (EQ(cmdnam
, "view")) {
186 value(vi_READONLY
) = 1;
187 } else if (EQ(cmdnam
, "vedit")) {
189 value(vi_NOVICE
) = 1;
190 value(vi_REPORT
) = 1;
192 value(vi_SHOWMODE
) = 1;
193 } else if (EQ(cmdnam
, "edit")) {
194 value(vi_NOVICE
) = 1;
195 value(vi_REPORT
) = 1;
197 value(vi_SHOWMODE
) = 1;
205 envlines
= envcolumns
= -1;
206 oldlines
= oldcolumns
= -1;
208 if (ioctl(0, TIOCGWINSZ
, &jwin
) != -1) {
209 oldlines
= jwin
.ws_row
;
210 oldcolumns
= jwin
.ws_col
;
213 if ((envptr
= getenv("LINES")) != NULL
&&
214 *envptr
!= '\0' && isdigit(*envptr
)) {
215 if ((envlines
= atoi(envptr
)) <= 0) {
220 if ((envptr
= getenv("COLUMNS")) != NULL
&&
221 *envptr
!= '\0' && isdigit(*envptr
)) {
222 if ((envcolumns
= atoi(envptr
)) <= 0) {
233 * Initialize interrupt handling.
235 oldhup
= signal(SIGHUP
, SIG_IGN
);
236 if (oldhup
== SIG_DFL
)
237 signal(SIGHUP
, onhup
);
238 oldquit
= signal(SIGQUIT
, SIG_IGN
);
239 ruptible
= signal(SIGINT
, SIG_IGN
) == SIG_DFL
;
240 if (signal(SIGTERM
, SIG_IGN
) == SIG_DFL
)
241 signal(SIGTERM
, onhup
);
242 signal(SIGILL
, oncore
);
243 signal(SIGTRAP
, oncore
);
244 signal(SIGIOT
, oncore
);
245 signal(SIGFPE
, oncore
);
246 signal(SIGBUS
, oncore
);
247 signal(SIGSEGV
, oncore
);
248 signal(SIGPIPE
, oncore
);
252 while ((c
= getopt(ac
, (char **)av
, "VU:Lc:Tvt:rlw:xRCsS")) !=
255 while ((c
= getopt(ac
, (char **)av
,
256 "VLc:vt:rlw:xRCsS")) != EOF
)
261 value(vi_AUTOPRINT
) = 0;
266 value(vi_READONLY
) = 1;
273 tracef
= (unsigned char *)"trace";
278 strcpy(tracef
, optarg
);
280 trace
= fopen((char *)tracef
, "w");
283 viprintf("Trace create error\n");
285 setbuf(trace
, (char *)tracbuf
);
290 firstpat
= (unsigned char *)optarg
;
292 firstpat
= (unsigned char *)"";
297 value(vi_SHOWMATCH
) = 1;
301 if (av
[optind
] && (c
= av
[optind
][0]) &&
303 if ((strlen(av
[optind
])) >=
304 sizeof (savedfile
)) {
305 (void) fprintf(stderr
, gettext(
306 "Recovered file name"
311 rcvname
= (unsigned char *)av
[optind
];
330 itag
= tagflg
= 1; /* -t option */
331 if (strlcpy(lasttag
, optarg
,
332 sizeof (lasttag
)) >= sizeof (lasttag
)) {
333 (void) fprintf(stderr
, gettext("Tag"
334 " file name too long\n"));
341 if (optarg
[0] == NULL
)
343 else for (cp
= (unsigned char *)optarg
;
345 defwind
= 10*defwind
+ *cp
- '0';
369 if (av
[optind
] && av
[optind
][0] == '+' &&
370 av
[optind
-1] && strcmp(av
[optind
-1], "--")) {
371 firstpat
= (unsigned char *)&av
[optind
][1];
374 } else if (av
[optind
] && av
[optind
][0] == '-' &&
375 av
[optind
-1] && strcmp(av
[optind
-1], "--")) {
377 value(vi_AUTOPRINT
) = 0;
385 if (isatty(0) == 0) {
387 * If -V option is set and input is coming in via
388 * stdin then vi behavior should be ignored. The vi
389 * command should act like ex and only process ex commands
390 * and echo the input ex commands to stderr
397 * If the standard input is not a terminal device,
398 * it is as if the -s option has been specified.
402 value(vi_AUTOPRINT
) = 0;
410 for (argcounter
= 0; argcounter
< ac
; argcounter
++) {
411 if ((strlen(av
[argcounter
])) >= sizeof (savedfile
)) {
412 (void) fprintf(stderr
, gettext("File argument"
419 if (!hush
&& signal(SIGTSTP
, SIG_IGN
) == SIG_DFL
)
420 signal(SIGTSTP
, onsusp
), dosusp
++;
425 if ((kflag
= run_setkey(perm
,
426 (key
= (unsigned char *)getpass(
427 gettext("Enter key:"))))) == -1) {
430 smerror(gettext("Encryption facility not available\n"));
435 strcpy(cryptkey
, "CrYpTkEy=XXXXXXXXX");
436 strcpy(cryptkey
+ 9, key
);
437 if (putenv((char *)cryptkey
) != 0)
438 smerror(gettext(" Cannot copy key to environment"));
444 * Perform locale-specific initialization
447 #endif /* PRESUNEUC */
450 * Initialize end of core pointers.
451 * Normally we avoid breaking back to fendcore after each
452 * file since this can be expensive (much core-core copying).
453 * If your system can scatter load processes you could do
454 * this as ed does, saving a little core, but it will probably
455 * not often make much difference.
457 fendcore
= (line
*) sbrk(0);
458 endcore
= fendcore
- 2;
461 * If we are doing a recover and no filename
462 * was given, then execute an exrecover command with
463 * the -r option to type out the list of saved file names.
464 * Otherwise set the remembered file name to the first argument
465 * file name so the "recover" initial command will find it.
468 if (ac
== 0 && (rcvname
== NULL
|| *rcvname
== NULL
)) {
471 execlp(EXRECOVER
, "exrecover", "-r", (char *)0);
475 if (rcvname
&& *rcvname
)
476 (void) strlcpy(savedfile
, rcvname
, sizeof (savedfile
));
478 (void) strlcpy(savedfile
, *av
++, sizeof (savedfile
));
484 * Initialize the argument list.
486 argv0
= (unsigned char **)av
;
488 args0
= (unsigned char *)av
[0];
492 * Initialize a temporary file (buffer) and
493 * set up terminal environment. Read user startup commands.
495 if (setexit() == 0) {
498 value(vi_PROMPT
) = intty
;
499 if (((cp
= (unsigned char *)getenv("SHELL")) != NULL
) &&
501 if (strlen(cp
) < sizeof (shell
)) {
502 (void) strlcpy(shell
, cp
, sizeof (shell
));
506 setterm((unsigned char *)"dumb");
509 cp
= (unsigned char *)getenv("TERM");
510 if (cp
== NULL
|| *cp
== '\0')
511 cp
= (unsigned char *)"unknown";
517 * Bring up some code from init()
518 * This is still done in init later. This
519 * avoids null pointer problems
522 dot
= zero
= truedol
= unddol
= dol
= fendcore
;
527 for (i
= 0; i
<= 'z'-'a'+1; i
++)
531 if (setexit() == 0 && !fast
) {
533 (unsigned char *) getenv("EXINIT")) && *globp
) {
540 if ((cp
= (unsigned char *) getenv("HOME")) !=
542 strncpy(scratch
, cp
, sizeof (scratch
) - 1);
543 strncat(scratch
, "/.exrc",
544 sizeof (scratch
) - 1 - strlen(scratch
));
547 if ((vret
= validate_exrc(scratch
)) == 0) {
552 "Not owner of .exrc "
553 "or .exrc is group or "
562 * Allow local .exrc if the "exrc" option was set. This
563 * loses if . is $HOME, but nobody should notice unless
564 * they do stupid things like putting a version command
566 * Besides, they should be using EXINIT, not .exrc, right?
569 if (value(vi_EXRC
)) {
572 if ((cp
= (unsigned char *) getenv("PWD")) != 0 &&
574 strncpy(exrcpath
, cp
, sizeof (exrcpath
) - 1);
575 strncat(exrcpath
, "/.exrc",
576 sizeof (exrcpath
) - 1 - strlen(exrcpath
));
577 if (strcmp(scratch
, exrcpath
) != 0) {
579 validate_exrc(exrcpath
)) == 0) {
596 init(); /* moved after prev 2 chunks to fix directory option */
599 * Initial processing. Handle tag, recover, and file argument
600 * implied next commands. If going in as 'vi', then don't do
601 * anything, just set initev so we will do it later (from within
604 if (setexit() == 0) {
606 globp
= (unsigned char *)"recover";
608 globp
= ivis
? (unsigned char *)"tag" :
609 (unsigned char *)"tag|p";
611 if (firstpat
!= NULL
) {
613 * if the user specified the -t and -c
614 * flags together, then we service these
615 * commands here. -t is handled first.
622 /* now handle the -c argument: */
626 globp
= savepat
= NULL
;
628 /* the above isn't sufficient for ex mode: */
632 plines(addr1
, addr2
, 1);
637 globp
= (unsigned char *)"next";
648 * Vi command... go into visual.
652 * Don't have to be upward compatible
653 * by starting editing at line $.
656 if (!itag
&& (dol
> zero
))
661 globp
= (unsigned char *)"visual";
667 * Clear out trash in state accumulated by startup,
668 * and then do the main command loop for a normal edit.
669 * If you quit out of a 'vi' command by doing Q or ^\,
670 * you also fall through to here.
684 * Initialization, before editing a new file.
685 * Main thing here is to get a new buffer (in fileinit),
686 * rest is peripheral state resetting.
694 dot
= zero
= truedol
= unddol
= dol
= fendcore
;
699 for (i
= 0; i
<= 'z'-'a'+1; i
++)
704 /* ignore SIGINT before crypt process */
705 pstat
= signal(SIGINT
, SIG_IGN
);
707 (void) crypt_close(tperm
);
709 if (makekey(tperm
) != 0) {
712 "Warning--Cannot encrypt temporary buffer\n"));
714 signal(SIGINT
, pstat
);
719 * Return last component of unix path name p.
735 * validate_exrc - verify .exrc as belonging to the user.
736 * The file uid should match the process ruid,
737 * and the file should be writable only by the owner.
740 validate_exrc(unsigned char *exrc_path
)
742 struct stat64 exrc_stat
;
745 if (stat64((char *)exrc_path
, &exrc_stat
) == -1)
746 return (0); /* ignore if .exrec is not found */
747 process_uid
= geteuid();
748 /* if not root, uid must match file owner */
749 if (process_uid
&& process_uid
!= exrc_stat
.st_uid
)
751 if ((exrc_stat
.st_mode
& (S_IWGRP
| S_IWOTH
)) != 0)
757 * print usage message to stdout
760 usage(unsigned char *name
)
765 (void) snprintf(buf
, sizeof (buf
), gettext(
766 "Usage: %s [- | -s] [-l] [-L] [-wn] "
767 "[-R] [-S] [-r [file]] [-t tag] [-T] [-U tracefile]\n"
768 "[-v] [-V] [-x] [-C] [+cmd | -c cmd] file...\n"), name
);
770 (void) snprintf(buf
, sizeof (buf
), gettext(
771 "Usage: %s [- | -s] [-l] [-L] [-wn] "
772 "[-R] [-S] [-r [file]] [-t tag]\n"
773 "[-v] [-V] [-x] [-C] [+cmd | -c cmd] file...\n"), name
);
775 (void) write(2, buf
, strlen(buf
));