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