xref: /openbsd-src/sbin/scsi/scsi.c (revision d7259957e8a5d4370d76bfccd4a30d5d1fe80f38)
1*d7259957Scheloha /*	$OpenBSD: scsi.c,v 1.32 2022/12/04 23:50:47 cheloha Exp $	*/
2ce8aa036Sderaadt /*	$FreeBSD: scsi.c,v 1.11 1996/04/06 11:00:28 joerg Exp $	*/
3ce8aa036Sderaadt 
4ce8aa036Sderaadt /*
5ce8aa036Sderaadt  * Written By Julian ELischer
6ce8aa036Sderaadt  * Copyright julian Elischer 1993.
7ce8aa036Sderaadt  * Permission is granted to use or redistribute this file in any way as long
8ce8aa036Sderaadt  * as this notice remains. Julian Elischer does not guarantee that this file
9ce8aa036Sderaadt  * is totally correct for any given task and users of this file must
10ce8aa036Sderaadt  * accept responsibility for any damage that occurs from the application of this
11ce8aa036Sderaadt  * file.
12ce8aa036Sderaadt  *
13ce8aa036Sderaadt  * (julian@tfs.com julian@dialix.oz.au)
14ce8aa036Sderaadt  *
15ce8aa036Sderaadt  * User SCSI hooks added by Peter Dufault:
16ce8aa036Sderaadt  *
17ce8aa036Sderaadt  * Copyright (c) 1994 HD Associates
18ce8aa036Sderaadt  * (contact: dufault@hda.com)
19ce8aa036Sderaadt  * All rights reserved.
20ce8aa036Sderaadt  *
21ce8aa036Sderaadt  * Redistribution and use in source and binary forms, with or without
22ce8aa036Sderaadt  * modification, are permitted provided that the following conditions
23ce8aa036Sderaadt  * are met:
24ce8aa036Sderaadt  * 1. Redistributions of source code must retain the above copyright
25ce8aa036Sderaadt  *    notice, this list of conditions and the following disclaimer.
26ce8aa036Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
27ce8aa036Sderaadt  *    notice, this list of conditions and the following disclaimer in the
28ce8aa036Sderaadt  *    documentation and/or other materials provided with the distribution.
29ce8aa036Sderaadt  * 3. The name of HD Associates
30ce8aa036Sderaadt  *    may not be used to endorse or promote products derived from this software
31ce8aa036Sderaadt  *    without specific prior written permission.
32ce8aa036Sderaadt  *
33ce8aa036Sderaadt  * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES ``AS IS'' AND
34ce8aa036Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
35ce8aa036Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36ce8aa036Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL HD ASSOCIATES BE LIABLE
37ce8aa036Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
38ce8aa036Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
39ce8aa036Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40ce8aa036Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
41ce8aa036Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
42ce8aa036Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43ce8aa036Sderaadt  * SUCH DAMAGE.
44ce8aa036Sderaadt  */
45ce8aa036Sderaadt 
4614789241Sray #include <sys/types.h>
4714789241Sray #include <sys/wait.h>
4814789241Sray 
498e1dad3fStedu #include <fcntl.h>
50ce8aa036Sderaadt #include <stdio.h>
51ce8aa036Sderaadt #include <string.h>
52ce8aa036Sderaadt #include <stdlib.h>
53ce8aa036Sderaadt #include <unistd.h>
54ce8aa036Sderaadt #include <errno.h>
55ce8aa036Sderaadt #include <sys/scsiio.h>
56ce8aa036Sderaadt #include <ctype.h>
57ce8aa036Sderaadt #include <signal.h>
58e763ff64Smickey #include <err.h>
59cf00d3a7Scloder #include <paths.h>
60ce8aa036Sderaadt 
61a6669909Sderaadt #include "libscsi.h"
62a6669909Sderaadt 
63ce8aa036Sderaadt int	fd;
64ce8aa036Sderaadt int	debuglevel;
65ce8aa036Sderaadt int	debugflag;
66ce8aa036Sderaadt int commandflag;
67ce8aa036Sderaadt int verbose = 0;
68ce8aa036Sderaadt 
69ce8aa036Sderaadt int modeflag;
70ce8aa036Sderaadt int editflag;
71ce8aa036Sderaadt int modepage = 0; /* Read this mode page */
72ce8aa036Sderaadt int pagectl = 0;  /* Mode sense page control */
73ce8aa036Sderaadt int seconds = 2;
74ce8aa036Sderaadt 
7545e804e6Sderaadt void	procargs(int *argc_p, char ***argv_p);
7645e804e6Sderaadt int	iget(void *hook, char *name);
7745e804e6Sderaadt char	*cget(void *hook, char *name);
7845e804e6Sderaadt void	arg_put(void *hook, int letter, void *arg, int count, char *name);
7945e804e6Sderaadt void	mode_sense(int fd, u_char *data, int len, int pc, int page);
8045e804e6Sderaadt void	mode_select(int fd, u_char *data, int len, int perm);
81e07939ebSderaadt int	editit(const char *pathname);
8245e804e6Sderaadt 
8345e804e6Sderaadt static void
usage(void)848809fabbSderaadt usage(void)
85ce8aa036Sderaadt {
86e763ff64Smickey 	fprintf(stderr,
87c6528975Sjmc "usage: scsi -f device -d debug_level\n"
88c6528975Sjmc "       scsi -f device -m page [-e] [-P pc]\n"
89c6528975Sjmc "       scsi -f device [-v] [-s seconds] -c cmd_fmt [arg ...]"
90c6528975Sjmc " -o count out_fmt\n"
91c6528975Sjmc "            [arg ...] -i count in_fmt [arg ...]\n");
92ce8aa036Sderaadt 
93ce8aa036Sderaadt 	exit (1);
94ce8aa036Sderaadt }
95ce8aa036Sderaadt 
968809fabbSderaadt void
procargs(int * argc_p,char *** argv_p)978809fabbSderaadt procargs(int *argc_p, char ***argv_p)
98ce8aa036Sderaadt {
99ce8aa036Sderaadt 	int argc = *argc_p;
100ce8aa036Sderaadt 	char **argv = *argv_p;
1018809fabbSderaadt 	int fflag, ch;
102ce8aa036Sderaadt 
103ce8aa036Sderaadt 	fflag = 0;
104ce8aa036Sderaadt 	commandflag = 0;
105ce8aa036Sderaadt 	debugflag = 0;
10681d5a439Sdlg 	while ((ch = getopt(argc, argv, "cef:d:m:P:s:v")) != -1) {
107ce8aa036Sderaadt 		switch (ch) {
108ce8aa036Sderaadt 		case 'c':
109ce8aa036Sderaadt 			commandflag = 1;
110ce8aa036Sderaadt 			break;
111ce8aa036Sderaadt 		case 'e':
112ce8aa036Sderaadt 			editflag = 1;
113ce8aa036Sderaadt 			break;
114ce8aa036Sderaadt 		case 'f':
115e763ff64Smickey 			if ((fd = scsi_open(optarg, O_RDWR)) < 0)
1168ddb3e99Sokan 				err(1, "unable to open device %s", optarg);
117ce8aa036Sderaadt 			fflag = 1;
118ce8aa036Sderaadt 			break;
119ce8aa036Sderaadt 		case 'd':
120ce8aa036Sderaadt 			debuglevel = strtol(optarg, 0, 0);
121ce8aa036Sderaadt 			debugflag = 1;
122ce8aa036Sderaadt 			break;
12381d5a439Sdlg 		case 'm':
12481d5a439Sdlg 			modeflag = 1;
12581d5a439Sdlg 			modepage = strtol(optarg, 0, 0);
126ce8aa036Sderaadt 			break;
127ce8aa036Sderaadt 		case 'P':
128ce8aa036Sderaadt 			pagectl = strtol(optarg, 0, 0);
129ce8aa036Sderaadt 			break;
130ce8aa036Sderaadt 		case 's':
131ce8aa036Sderaadt 			seconds = strtol(optarg, 0, 0);
132ce8aa036Sderaadt 			break;
13381d5a439Sdlg 		case 'v':
13481d5a439Sdlg 			verbose = 1;
135ce8aa036Sderaadt 			break;
136ce8aa036Sderaadt 		default:
137ce8aa036Sderaadt 			usage();
138ce8aa036Sderaadt 		}
139ce8aa036Sderaadt 	}
140ce8aa036Sderaadt 	*argc_p = argc - optind;
141ce8aa036Sderaadt 	*argv_p = argv + optind;
142ce8aa036Sderaadt 
143ce8aa036Sderaadt 	if (!fflag) usage();
144ce8aa036Sderaadt }
145ce8aa036Sderaadt 
146ce8aa036Sderaadt /* get_hook: Structure for evaluating args in a callback.
147ce8aa036Sderaadt  */
148ce8aa036Sderaadt struct get_hook
149ce8aa036Sderaadt {
150ce8aa036Sderaadt 	int argc;
151ce8aa036Sderaadt 	char **argv;
152ce8aa036Sderaadt 	int got;
153ce8aa036Sderaadt };
154ce8aa036Sderaadt 
155ce8aa036Sderaadt /* iget: Integer argument callback
156ce8aa036Sderaadt  */
1578809fabbSderaadt int
iget(void * hook,char * name)1588809fabbSderaadt iget(void *hook, char *name)
159ce8aa036Sderaadt {
160ce8aa036Sderaadt 	struct get_hook *h = (struct get_hook *)hook;
161ce8aa036Sderaadt 	int arg;
162ce8aa036Sderaadt 
163ce8aa036Sderaadt 	if (h->got >= h->argc)
164ce8aa036Sderaadt 	{
165ce8aa036Sderaadt 		fprintf(stderr, "Expecting an integer argument.\n");
166ce8aa036Sderaadt 		usage();
167ce8aa036Sderaadt 	}
168ce8aa036Sderaadt 	arg = strtol(h->argv[h->got], 0, 0);
169ce8aa036Sderaadt 	h->got++;
170ce8aa036Sderaadt 
171ce8aa036Sderaadt 	if (verbose && name && *name)
172ce8aa036Sderaadt 		printf("%s: %d\n", name, arg);
173ce8aa036Sderaadt 
174ce8aa036Sderaadt 	return arg;
175ce8aa036Sderaadt }
176ce8aa036Sderaadt 
177ce8aa036Sderaadt /* cget: char * argument callback
178ce8aa036Sderaadt  */
1798809fabbSderaadt char *
cget(void * hook,char * name)1808809fabbSderaadt cget(void *hook, char *name)
181ce8aa036Sderaadt {
182ce8aa036Sderaadt 	struct get_hook *h = (struct get_hook *)hook;
183ce8aa036Sderaadt 	char *arg;
184ce8aa036Sderaadt 
185ce8aa036Sderaadt 	if (h->got >= h->argc)
186ce8aa036Sderaadt 	{
187ce8aa036Sderaadt 		fprintf(stderr, "Expecting a character pointer argument.\n");
188ce8aa036Sderaadt 		usage();
189ce8aa036Sderaadt 	}
190ce8aa036Sderaadt 	arg = h->argv[h->got];
191ce8aa036Sderaadt 	h->got++;
192ce8aa036Sderaadt 
193ce8aa036Sderaadt 	if (verbose && name)
194ce8aa036Sderaadt 		printf("cget: %s: %s", name, arg);
195ce8aa036Sderaadt 
196ce8aa036Sderaadt 	return arg;
197ce8aa036Sderaadt }
198ce8aa036Sderaadt 
199ce8aa036Sderaadt /* arg_put: "put argument" callback
200ce8aa036Sderaadt  */
arg_put(void * hook,int letter,void * arg,int count,char * name)201ce8aa036Sderaadt void arg_put(void *hook, int letter, void *arg, int count, char *name)
202ce8aa036Sderaadt {
203ce8aa036Sderaadt 	if (verbose && name && *name)
204ce8aa036Sderaadt 		printf("%s:  ", name);
205ce8aa036Sderaadt 
206ce8aa036Sderaadt 	switch(letter)
207ce8aa036Sderaadt 	{
208ce8aa036Sderaadt 		case 'i':
209ce8aa036Sderaadt 		case 'b':
210c1f5f64dSdgregor 		printf("%ld ", (long)arg);
211ce8aa036Sderaadt 		break;
212ce8aa036Sderaadt 
213ce8aa036Sderaadt 		case 'c':
214ce8aa036Sderaadt 		case 'z':
215ce8aa036Sderaadt 		{
216ce8aa036Sderaadt 			char *p = malloc(count + 1);
2178ddb3e99Sokan 			if (p == NULL)
2188ddb3e99Sokan 				err(1, NULL);
2198ddb3e99Sokan 
220ce8aa036Sderaadt 			p[count] = 0;
221ce8aa036Sderaadt 			strncpy(p, (char *)arg, count);
222ce8aa036Sderaadt 			if (letter == 'z')
223ce8aa036Sderaadt 			{
224ce8aa036Sderaadt 				int i;
225ce8aa036Sderaadt 				for (i = count - 1; i >= 0; i--)
226ce8aa036Sderaadt 					if (p[i] == ' ')
227ce8aa036Sderaadt 						p[i] = 0;
228ce8aa036Sderaadt 					else
229ce8aa036Sderaadt 						break;
230ce8aa036Sderaadt 			}
231ce8aa036Sderaadt 			printf("%s ", p);
232d5b07100Sjsg 			free(p);
233ce8aa036Sderaadt 		}
234ce8aa036Sderaadt 
235ce8aa036Sderaadt 		break;
236ce8aa036Sderaadt 
237ce8aa036Sderaadt 		default:
238ce8aa036Sderaadt 		printf("Unknown format letter: '%c'\n", letter);
239ce8aa036Sderaadt 	}
240ce8aa036Sderaadt 	if (verbose)
241ce8aa036Sderaadt 		putchar('\n');
242ce8aa036Sderaadt }
243ce8aa036Sderaadt 
244ce8aa036Sderaadt /* data_phase: SCSI bus data phase: DATA IN, DATA OUT, or no data transfer.
245ce8aa036Sderaadt  */
246ce8aa036Sderaadt enum data_phase {none = 0, in, out};
247ce8aa036Sderaadt 
248ce8aa036Sderaadt /* do_cmd: Send a command to a SCSI device
249ce8aa036Sderaadt  */
250ce8aa036Sderaadt static void
do_cmd(int fd,char * fmt,int argc,char ** argv)251ce8aa036Sderaadt do_cmd(int fd, char *fmt, int argc, char **argv)
252ce8aa036Sderaadt {
253ce8aa036Sderaadt 	struct get_hook h;
254ce8aa036Sderaadt 	scsireq_t *scsireq = scsireq_new();
255ce8aa036Sderaadt 	enum data_phase data_phase;
256ce8aa036Sderaadt 	int count, amount;
257ce8aa036Sderaadt 	char *data_fmt, *bp;
258ce8aa036Sderaadt 
259ce8aa036Sderaadt 	h.argc = argc;
260ce8aa036Sderaadt 	h.argv = argv;
261ce8aa036Sderaadt 	h.got = 0;
262ce8aa036Sderaadt 
263ce8aa036Sderaadt 	scsireq_reset(scsireq);
264ce8aa036Sderaadt 
265ce8aa036Sderaadt 	scsireq_build_visit(scsireq, 0, 0, 0, fmt, iget, (void *)&h);
266ce8aa036Sderaadt 
267ce8aa036Sderaadt 	/* Three choices here:
268ce8aa036Sderaadt 	 * 1. We've used up all the args and have no data phase.
269ce8aa036Sderaadt 	 * 2. We have input data ("-i")
270ce8aa036Sderaadt 	 * 3. We have output data ("-o")
271ce8aa036Sderaadt 	 */
272ce8aa036Sderaadt 
273ce8aa036Sderaadt 	if (h.got >= h.argc)
274ce8aa036Sderaadt 	{
275ce8aa036Sderaadt 		data_phase = none;
276ce8aa036Sderaadt 		count = scsireq->datalen = 0;
277ce8aa036Sderaadt 	}
278ce8aa036Sderaadt 	else
279ce8aa036Sderaadt 	{
280ce8aa036Sderaadt 		char *flag = cget(&h, 0);
281ce8aa036Sderaadt 
282ce8aa036Sderaadt 		if (strcmp(flag, "-o") == 0)
283ce8aa036Sderaadt 		{
284ce8aa036Sderaadt 			data_phase = out;
285ce8aa036Sderaadt 			scsireq->flags = SCCMD_WRITE;
286ce8aa036Sderaadt 		}
287ce8aa036Sderaadt 		else if (strcmp(flag, "-i") == 0)
288ce8aa036Sderaadt 		{
289ce8aa036Sderaadt 			data_phase = in;
290ce8aa036Sderaadt 			scsireq->flags = SCCMD_READ;
291ce8aa036Sderaadt 		}
292ce8aa036Sderaadt 		else
293ce8aa036Sderaadt 		{
294ce8aa036Sderaadt 			fprintf(stderr,
295ce8aa036Sderaadt 			"Need either \"-i\" or \"-o\" for data phase; not \"%s\".\n", flag);
296ce8aa036Sderaadt 			usage();
297ce8aa036Sderaadt 		}
298ce8aa036Sderaadt 
299ce8aa036Sderaadt 		count = scsireq->datalen = iget(&h, 0);
30054dea640Sderaadt 		if (count) {
301ce8aa036Sderaadt 			data_fmt = cget(&h, 0);
302ce8aa036Sderaadt 
303ce8aa036Sderaadt 			scsireq->databuf = malloc(count);
3048ddb3e99Sokan 			if (scsireq->databuf == NULL)
3058ddb3e99Sokan 				err(1, NULL);
306ce8aa036Sderaadt 
30754dea640Sderaadt 			if (data_phase == out) {
30854dea640Sderaadt 				if (strcmp(data_fmt, "-") == 0)	{
309ce8aa036Sderaadt 					bp = (char *)scsireq->databuf;
31054dea640Sderaadt 					while (count > 0 &&
31154dea640Sderaadt 					    (amount = read(STDIN_FILENO,
31254dea640Sderaadt 					    bp, count)) > 0) {
313ce8aa036Sderaadt 						count -= amount;
314ce8aa036Sderaadt 						bp += amount;
315ce8aa036Sderaadt 					}
316ce8aa036Sderaadt 					if (amount == -1)
3178ddb3e99Sokan 						err(1, "read");
31854dea640Sderaadt 					else if (amount == 0) {
319ce8aa036Sderaadt 						/* early EOF */
320ce8aa036Sderaadt 						fprintf(stderr,
321ce8aa036Sderaadt 							"Warning: only read %lu bytes out of %lu.\n",
322ce8aa036Sderaadt 							scsireq->datalen - (u_long)count,
323ce8aa036Sderaadt 							scsireq->datalen);
324ce8aa036Sderaadt 						scsireq->datalen -= (u_long)count;
325ce8aa036Sderaadt 					}
326ce8aa036Sderaadt 				}
327ce8aa036Sderaadt 				else
328ce8aa036Sderaadt 				{
329ce8aa036Sderaadt 					bzero(scsireq->databuf, count);
330ce8aa036Sderaadt 					scsireq_encode_visit(scsireq, data_fmt, iget, (void *)&h);
331ce8aa036Sderaadt 				}
332ce8aa036Sderaadt 			}
333ce8aa036Sderaadt 		}
334ce8aa036Sderaadt 	}
335ce8aa036Sderaadt 
336ce8aa036Sderaadt 
337ce8aa036Sderaadt 	scsireq->timeout = seconds * 1000;
338ce8aa036Sderaadt 
339ce8aa036Sderaadt 	if (scsireq_enter(fd, scsireq) == -1)
340ce8aa036Sderaadt 	{
341ce8aa036Sderaadt 		scsi_debug(stderr, -1, scsireq);
3428ddb3e99Sokan 		exit(1);
343ce8aa036Sderaadt 	}
344ce8aa036Sderaadt 
345ce8aa036Sderaadt 	if (SCSIREQ_ERROR(scsireq))
346ce8aa036Sderaadt 		scsi_debug(stderr, 0, scsireq);
347ce8aa036Sderaadt 
348ce8aa036Sderaadt 	if (count && data_phase == in)
349ce8aa036Sderaadt 	{
350ce8aa036Sderaadt 		if (strcmp(data_fmt, "-") == 0)	/* stdout */
351ce8aa036Sderaadt 		{
352ce8aa036Sderaadt 			bp = (char *)scsireq->databuf;
35337889ee4Smillert 			while (count > 0 && (amount = write(STDOUT_FILENO, bp, count)) > 0)
354ce8aa036Sderaadt 			{
355ce8aa036Sderaadt 				count -= amount;
356ce8aa036Sderaadt 				bp += amount;
357ce8aa036Sderaadt 			}
358ce8aa036Sderaadt 			if (amount < 0)
3598ddb3e99Sokan 				err(1, "write");
360ce8aa036Sderaadt 			else if (amount == 0)
3616c9fc84dSotto 				fprintf(stderr, "Warning: wrote only %lu bytes out of %lu.\n",
362ce8aa036Sderaadt 					scsireq->datalen - count,
363ce8aa036Sderaadt 					scsireq->datalen);
364ce8aa036Sderaadt 
365ce8aa036Sderaadt 		}
366ce8aa036Sderaadt 		else
367ce8aa036Sderaadt 		{
368ce8aa036Sderaadt 			scsireq_decode_visit(scsireq, data_fmt, arg_put, 0);
369ce8aa036Sderaadt 			putchar('\n');
370ce8aa036Sderaadt 		}
371ce8aa036Sderaadt 	}
372ce8aa036Sderaadt }
373ce8aa036Sderaadt 
mode_sense(int fd,u_char * data,int len,int pc,int page)374ce8aa036Sderaadt void mode_sense(int fd, u_char *data, int len, int pc, int page)
375ce8aa036Sderaadt {
376ce8aa036Sderaadt 	scsireq_t *scsireq;
377ce8aa036Sderaadt 
378ce8aa036Sderaadt 	bzero(data, len);
379ce8aa036Sderaadt 
380ce8aa036Sderaadt 	scsireq = scsireq_new();
381ce8aa036Sderaadt 
382ce8aa036Sderaadt 	if (scsireq_enter(fd, scsireq_build(scsireq,
383ce8aa036Sderaadt 	 len, data, SCCMD_READ,
384ce8aa036Sderaadt 	 "1A 0 v:2 {Page Control} v:6 {Page Code} 0 v:i1 {Allocation Length} 0",
385ce8aa036Sderaadt 	 pc, page, len)) == -1)	/* Mode sense */
386ce8aa036Sderaadt 	{
387ce8aa036Sderaadt 		scsi_debug(stderr, -1, scsireq);
3888ddb3e99Sokan 		exit(1);
389ce8aa036Sderaadt 	}
390ce8aa036Sderaadt 
391ce8aa036Sderaadt 	if (SCSIREQ_ERROR(scsireq))
392ce8aa036Sderaadt 	{
393ce8aa036Sderaadt 		scsi_debug(stderr, 0, scsireq);
3944636c832Sderaadt 		exit(1);
395ce8aa036Sderaadt 	}
396ce8aa036Sderaadt 
397ce8aa036Sderaadt 	free(scsireq);
398ce8aa036Sderaadt }
399ce8aa036Sderaadt 
mode_select(int fd,u_char * data,int len,int perm)400ce8aa036Sderaadt void mode_select(int fd, u_char *data, int len, int perm)
401ce8aa036Sderaadt {
402ce8aa036Sderaadt 	scsireq_t *scsireq;
403ce8aa036Sderaadt 
404ce8aa036Sderaadt 	scsireq = scsireq_new();
405ce8aa036Sderaadt 
406ce8aa036Sderaadt 	if (scsireq_enter(fd, scsireq_build(scsireq,
407ce8aa036Sderaadt 	 len, data, SCCMD_WRITE,
408ce8aa036Sderaadt 	 "15 0:7 v:1 {SP} 0 0 v:i1 {Allocation Length} 0", perm, len)) == -1)	/* Mode select */
409ce8aa036Sderaadt 	{
410ce8aa036Sderaadt 		scsi_debug(stderr, -1, scsireq);
4118ddb3e99Sokan 		exit(1);
412ce8aa036Sderaadt 	}
413ce8aa036Sderaadt 
414ce8aa036Sderaadt 	if (SCSIREQ_ERROR(scsireq))
415ce8aa036Sderaadt 	{
416ce8aa036Sderaadt 		scsi_debug(stderr, 0, scsireq);
4174636c832Sderaadt 		exit(1);
418ce8aa036Sderaadt 	}
419ce8aa036Sderaadt 
420ce8aa036Sderaadt 	free(scsireq);
421ce8aa036Sderaadt }
422ce8aa036Sderaadt 
423ce8aa036Sderaadt 
424ce8aa036Sderaadt #define START_ENTRY '{'
425ce8aa036Sderaadt #define END_ENTRY '}'
426ce8aa036Sderaadt 
427ce8aa036Sderaadt static void
skipwhite(FILE * f)428ce8aa036Sderaadt skipwhite(FILE *f)
429ce8aa036Sderaadt {
430ce8aa036Sderaadt 	int c;
431ce8aa036Sderaadt 
432ce8aa036Sderaadt skip_again:
433ce8aa036Sderaadt 
434ce8aa036Sderaadt 	while (isspace(c = getc(f)))
435252fb110Stedu 		continue;
436ce8aa036Sderaadt 
437ce8aa036Sderaadt 	if (c == '#') {
438ce8aa036Sderaadt 		while ((c = getc(f)) != '\n' && c != EOF)
439252fb110Stedu 			continue;
440ce8aa036Sderaadt 		goto skip_again;
441ce8aa036Sderaadt 	}
442ce8aa036Sderaadt 
443ce8aa036Sderaadt 	ungetc(c, f);
444ce8aa036Sderaadt }
445ce8aa036Sderaadt 
446ce8aa036Sderaadt /* mode_lookup: Lookup a format description for a given page.
447ce8aa036Sderaadt  */
448ce8aa036Sderaadt char *mode_db = "/usr/share/misc/scsi_modes";
mode_lookup(int page)449ce8aa036Sderaadt static char *mode_lookup(int page)
450ce8aa036Sderaadt {
451ce8aa036Sderaadt 	char *new_db;
452ce8aa036Sderaadt 	FILE *modes;
453ce8aa036Sderaadt 	int match, next, found, c;
454ce8aa036Sderaadt 	static char fmt[1024];	/* XXX This should be with strealloc */
455ce8aa036Sderaadt 	int page_desc;
456ce8aa036Sderaadt 	new_db = getenv("SCSI_MODES");
457ce8aa036Sderaadt 
458ce8aa036Sderaadt 	if (new_db)
459ce8aa036Sderaadt 		mode_db = new_db;
460ce8aa036Sderaadt 
461ce8aa036Sderaadt 	modes = fopen(mode_db, "r");
462888dd059Sderaadt 	if (modes == NULL)
463ce8aa036Sderaadt 		return 0;
464ce8aa036Sderaadt 
465ce8aa036Sderaadt 	next = 0;
466ce8aa036Sderaadt 	found = 0;
467ce8aa036Sderaadt 
468ce8aa036Sderaadt 	while (!found) {
469ce8aa036Sderaadt 
470ce8aa036Sderaadt 		skipwhite(modes);
471ce8aa036Sderaadt 
472ce8aa036Sderaadt 		if (fscanf(modes, "%i", &page_desc) != 1)
473ce8aa036Sderaadt 			break;
474ce8aa036Sderaadt 
475ce8aa036Sderaadt 		if (page_desc == page)
476ce8aa036Sderaadt 			found = 1;
477ce8aa036Sderaadt 
478ce8aa036Sderaadt 		skipwhite(modes);
479ce8aa036Sderaadt 		if (getc(modes) != START_ENTRY) {
4808ddb3e99Sokan 			errx(1, "Expected %c", START_ENTRY);
481ce8aa036Sderaadt 		}
482ce8aa036Sderaadt 
483ce8aa036Sderaadt 		match = 1;
484ce8aa036Sderaadt 		while (match != 0) {
485ce8aa036Sderaadt 			c = getc(modes);
486e763ff64Smickey 			if (c == EOF)
487ce8aa036Sderaadt 				fprintf(stderr, "Expected %c.\n", END_ENTRY);
488ce8aa036Sderaadt 
489ce8aa036Sderaadt 			if (c == START_ENTRY) {
490ce8aa036Sderaadt 				match++;
491ce8aa036Sderaadt 			}
492ce8aa036Sderaadt 			if (c == END_ENTRY) {
493ce8aa036Sderaadt 				match--;
494ce8aa036Sderaadt 				if (match == 0)
495ce8aa036Sderaadt 					break;
496ce8aa036Sderaadt 			}
497ce8aa036Sderaadt 			if (found && c != '\n') {
498ce8aa036Sderaadt 				if (next >= sizeof(fmt)) {
4998ddb3e99Sokan 					errx(1, "Stupid program: Buffer overflow.\n");
500ce8aa036Sderaadt 				}
501ce8aa036Sderaadt 
502ce8aa036Sderaadt 				fmt[next++] = (u_char)c;
503ce8aa036Sderaadt 			}
504ce8aa036Sderaadt 		}
505ce8aa036Sderaadt 	}
506888dd059Sderaadt 	fclose(modes);
507ce8aa036Sderaadt 	fmt[next] = 0;
508ce8aa036Sderaadt 
509ce8aa036Sderaadt 	return (found) ? fmt : 0;
510ce8aa036Sderaadt }
511ce8aa036Sderaadt 
512ce8aa036Sderaadt /* -------- edit: Mode Select Editor ---------
513ce8aa036Sderaadt  */
514ce8aa036Sderaadt struct editinfo
515ce8aa036Sderaadt {
516c1f5f64dSdgregor 	long can_edit;
517c1f5f64dSdgregor 	long default_value;
518ce8aa036Sderaadt } editinfo[64];	/* XXX Bogus fixed size */
519ce8aa036Sderaadt 
520ce8aa036Sderaadt static int editind;
521ce8aa036Sderaadt volatile int edit_opened;
522ce8aa036Sderaadt static FILE *edit_file;
523ce8aa036Sderaadt static char edit_name[L_tmpnam];
524ce8aa036Sderaadt 
525290ab849Scloder static void
edit_rewind(void)526ce8aa036Sderaadt edit_rewind(void)
527ce8aa036Sderaadt {
528ce8aa036Sderaadt 	editind = 0;
529ce8aa036Sderaadt }
530ce8aa036Sderaadt 
531ce8aa036Sderaadt static void
edit_done(void)532ce8aa036Sderaadt edit_done(void)
533ce8aa036Sderaadt {
534ce8aa036Sderaadt 	int opened;
535ce8aa036Sderaadt 
536ce8aa036Sderaadt 	sigset_t all, prev;
537ce8aa036Sderaadt 	sigfillset(&all);
538ce8aa036Sderaadt 
539ce8aa036Sderaadt 	(void)sigprocmask(SIG_SETMASK, &all, &prev);
540ce8aa036Sderaadt 
541ce8aa036Sderaadt 	opened = (int)edit_opened;
542ce8aa036Sderaadt 	edit_opened = 0;
543ce8aa036Sderaadt 
544ce8aa036Sderaadt 	(void)sigprocmask(SIG_SETMASK, &prev, 0);
545ce8aa036Sderaadt 
546ce8aa036Sderaadt 	if (opened)
547ce8aa036Sderaadt 	{
548ce8aa036Sderaadt 		if (fclose(edit_file))
549ce8aa036Sderaadt 			perror(edit_name);
550ce8aa036Sderaadt 		if (unlink(edit_name))
551ce8aa036Sderaadt 			perror(edit_name);
552ce8aa036Sderaadt 	}
553ce8aa036Sderaadt }
554ce8aa036Sderaadt 
555ce8aa036Sderaadt static void
edit_init(void)556ce8aa036Sderaadt edit_init(void)
557ce8aa036Sderaadt {
558340812adSangelos 	int fd;
559340812adSangelos 
560ce8aa036Sderaadt 	edit_rewind();
56115f407e7Sderaadt 	strlcpy(edit_name, "/var/tmp/scXXXXXXXX", sizeof edit_name);
562e763ff64Smickey 	if ((fd = mkstemp(edit_name)) == -1)
5638ddb3e99Sokan 		err(1, "mkstemp");
564e763ff64Smickey 	if ( (edit_file = fdopen(fd, "w+")) == 0)
5658ddb3e99Sokan 		err(1, "fdopen");
566ce8aa036Sderaadt 	edit_opened = 1;
567ce8aa036Sderaadt 
568ce8aa036Sderaadt 	atexit(edit_done);
569ce8aa036Sderaadt }
570ce8aa036Sderaadt 
571ce8aa036Sderaadt static void
edit_check(void * hook,int letter,void * arg,int count,char * name)572ce8aa036Sderaadt edit_check(void *hook, int letter, void *arg, int count, char *name)
573ce8aa036Sderaadt {
574ce8aa036Sderaadt 	if (letter != 'i' && letter != 'b') {
5758ddb3e99Sokan 		errx(1, "Can't edit format %c.\n", letter);
576ce8aa036Sderaadt 	}
577ce8aa036Sderaadt 
578ce8aa036Sderaadt 	if (editind >= sizeof(editinfo) / sizeof(editinfo[0])) {
5798ddb3e99Sokan 		errx(1, "edit table overflow");
580ce8aa036Sderaadt 	}
581c1f5f64dSdgregor 	editinfo[editind].can_edit = ((long)arg != 0);
582ce8aa036Sderaadt 	editind++;
583ce8aa036Sderaadt }
584ce8aa036Sderaadt 
585ce8aa036Sderaadt static void
edit_defaults(void * hook,int letter,void * arg,int count,char * name)586ce8aa036Sderaadt edit_defaults(void *hook, int letter, void *arg, int count, char *name)
587ce8aa036Sderaadt {
588ce8aa036Sderaadt 	if (letter != 'i' && letter != 'b') {
5898ddb3e99Sokan 		errx(1, "Can't edit format %c.\n", letter);
590ce8aa036Sderaadt 	}
591ce8aa036Sderaadt 
592c1f5f64dSdgregor 	editinfo[editind].default_value = ((long)arg);
593ce8aa036Sderaadt 	editind++;
594ce8aa036Sderaadt }
595ce8aa036Sderaadt 
596ce8aa036Sderaadt static void
edit_report(void * hook,int letter,void * arg,int count,char * name)597ce8aa036Sderaadt edit_report(void *hook, int letter, void *arg, int count, char *name)
598ce8aa036Sderaadt {
599ce8aa036Sderaadt 	if (editinfo[editind].can_edit) {
600ce8aa036Sderaadt 		if (letter != 'i' && letter != 'b') {
6018ddb3e99Sokan 			errx(1, "Can't report format %c.\n", letter);
602ce8aa036Sderaadt 		}
603ce8aa036Sderaadt 
604c1f5f64dSdgregor 		fprintf(edit_file, "%s:  %ld\n", name, (long)arg);
605ce8aa036Sderaadt 	}
606ce8aa036Sderaadt 
607ce8aa036Sderaadt 	editind++;
608ce8aa036Sderaadt }
609ce8aa036Sderaadt 
610ce8aa036Sderaadt static int
edit_get(void * hook,char * name)611ce8aa036Sderaadt edit_get(void *hook, char *name)
612ce8aa036Sderaadt {
613ce8aa036Sderaadt 	int arg = editinfo[editind].default_value;
614ce8aa036Sderaadt 
615ce8aa036Sderaadt 	if (editinfo[editind].can_edit) {
616ce8aa036Sderaadt 		char line[80];
6176eb3959cScloder 		size_t len;
618719c0cc6Scloder 		if (fgets(line, sizeof(line), edit_file) == NULL)
6198ddb3e99Sokan 			err(1, "fgets");
620ce8aa036Sderaadt 
6216eb3959cScloder 		len = strlen(line);
6226eb3959cScloder 		if (len && line[len - 1] == '\n')
6236eb3959cScloder 			line[len - 1] = '\0';
624ce8aa036Sderaadt 
625ce8aa036Sderaadt 		if (strncmp(name, line, strlen(name)) != 0) {
6268ddb3e99Sokan 			errx(1, "Expected \"%s\" and read \"%s\"\n",
627ce8aa036Sderaadt 			    name, line);
628ce8aa036Sderaadt 		}
629ce8aa036Sderaadt 
630ce8aa036Sderaadt 		arg = strtoul(line + strlen(name) + 2, 0, 0);
631ce8aa036Sderaadt 	}
632ce8aa036Sderaadt 
633ce8aa036Sderaadt 	editind++;
634ce8aa036Sderaadt 	return arg;
635ce8aa036Sderaadt }
636ce8aa036Sderaadt 
63714789241Sray int
editit(const char * pathname)63814789241Sray editit(const char *pathname)
639ce8aa036Sderaadt {
64014789241Sray 	char *argp[] = {"sh", "-c", NULL, NULL}, *ed, *p;
64114789241Sray 	sig_t sighup, sigint, sigquit;
64214789241Sray 	pid_t pid;
64314789241Sray 	int st;
644ce8aa036Sderaadt 
64514789241Sray 	ed = getenv("VISUAL");
64614789241Sray 	if (ed == NULL || ed[0] == '\0')
64714789241Sray 		ed = getenv("EDITOR");
64814789241Sray 	if (ed == NULL || ed[0] == '\0')
64914789241Sray 		ed = _PATH_VI;
65014789241Sray 	if (asprintf(&p, "%s %s", ed, pathname) == -1)
65114789241Sray 		return (-1);
65214789241Sray 	argp[2] = p;
653ce8aa036Sderaadt 
65414789241Sray  top:
65514789241Sray 	sighup = signal(SIGHUP, SIG_IGN);
65614789241Sray 	sigint = signal(SIGINT, SIG_IGN);
65714789241Sray 	sigquit = signal(SIGQUIT, SIG_IGN);
65814789241Sray 	if ((pid = fork()) == -1) {
65914789241Sray 		int saved_errno = errno;
66089815c80Scloder 
66114789241Sray 		(void)signal(SIGHUP, sighup);
66214789241Sray 		(void)signal(SIGINT, sigint);
66314789241Sray 		(void)signal(SIGQUIT, sigquit);
66414789241Sray 		if (saved_errno == EAGAIN) {
66514789241Sray 			sleep(1);
66614789241Sray 			goto top;
66714789241Sray 		}
66814789241Sray 		free(p);
66914789241Sray 		errno = saved_errno;
67014789241Sray 		return (-1);
67114789241Sray 	}
67214789241Sray 	if (pid == 0) {
67314789241Sray 		execv(_PATH_BSHELL, argp);
67414789241Sray 		_exit(127);
67514789241Sray 	}
67614789241Sray 	free(p);
67714789241Sray 	for (;;) {
67814789241Sray 		if (waitpid(pid, &st, 0) == -1) {
67914789241Sray 			if (errno != EINTR)
68014789241Sray 				return (-1);
68114789241Sray 		} else
68214789241Sray 			break;
68314789241Sray 	}
68414789241Sray 	(void)signal(SIGHUP, sighup);
68514789241Sray 	(void)signal(SIGINT, sigint);
68614789241Sray 	(void)signal(SIGQUIT, sigquit);
68714789241Sray 	if (!WIFEXITED(st) || WEXITSTATUS(st) != 0) {
68814789241Sray 		errno = ECHILD;
68914789241Sray 		return (-1);
69014789241Sray 	}
69114789241Sray 	return (0);
692ce8aa036Sderaadt }
693ce8aa036Sderaadt 
694ce8aa036Sderaadt static void
mode_edit(int fd,int page,int edit,int argc,char * argv[])695ce8aa036Sderaadt mode_edit(int fd, int page, int edit, int argc, char *argv[])
696ce8aa036Sderaadt {
697ce8aa036Sderaadt 	int i;
698ce8aa036Sderaadt 	u_char data[255];
699ce8aa036Sderaadt 	u_char *mode_pars;
700ce8aa036Sderaadt 	struct mode_header
701ce8aa036Sderaadt 	{
702ce8aa036Sderaadt 		u_char mdl;	/* Mode data length */
703ce8aa036Sderaadt 		u_char medium_type;
704ce8aa036Sderaadt 		u_char dev_spec_par;
705ce8aa036Sderaadt 		u_char bdl;	/* Block descriptor length */
706ce8aa036Sderaadt 	};
707ce8aa036Sderaadt 
708ce8aa036Sderaadt 	struct mode_page_header
709ce8aa036Sderaadt 	{
710ce8aa036Sderaadt 		u_char page_code;
711ce8aa036Sderaadt 		u_char page_length;
712ce8aa036Sderaadt 	};
713ce8aa036Sderaadt 
714ce8aa036Sderaadt 	struct mode_header *mh;
715ce8aa036Sderaadt 	struct mode_page_header *mph;
716ce8aa036Sderaadt 
717ce8aa036Sderaadt 	char *fmt = mode_lookup(page);
718ce8aa036Sderaadt 	if (!fmt && verbose) {
719ce8aa036Sderaadt 		fprintf(stderr,
720ce8aa036Sderaadt 		"No mode data base entry in \"%s\" for page %d;  binary %s only.\n",
721ce8aa036Sderaadt 		mode_db, page, (edit ? "edit" : "display"));
722ce8aa036Sderaadt 	}
723ce8aa036Sderaadt 
724ce8aa036Sderaadt 	if (edit) {
725ce8aa036Sderaadt 		if (!fmt) {
7268ddb3e99Sokan 			errx(1, "Sorry: can't edit without a format.\n");
727ce8aa036Sderaadt 		}
728ce8aa036Sderaadt 
729ce8aa036Sderaadt 		if (pagectl != 0 && pagectl != 3) {
7308ddb3e99Sokan 			errx(1,
731ce8aa036Sderaadt "It only makes sense to edit page 0 (current) or page 3 (saved values)\n");
732ce8aa036Sderaadt 		}
733ce8aa036Sderaadt 
734ce8aa036Sderaadt 		verbose = 1;
735ce8aa036Sderaadt 
736ce8aa036Sderaadt 		mode_sense(fd, data, sizeof(data), 1, page);
737ce8aa036Sderaadt 
738ce8aa036Sderaadt 		mh = (struct mode_header *)data;
739ce8aa036Sderaadt 		mph = (struct mode_page_header *)
740ce8aa036Sderaadt 		(((char *)mh) + sizeof(*mh) + mh->bdl);
741ce8aa036Sderaadt 
742ce8aa036Sderaadt 		mode_pars = (char *)mph + sizeof(*mph);
743ce8aa036Sderaadt 
744ce8aa036Sderaadt 		edit_init();
745ce8aa036Sderaadt 		scsireq_buff_decode_visit(mode_pars, mh->mdl,
746ce8aa036Sderaadt 		fmt, edit_check, 0);
747ce8aa036Sderaadt 
748ce8aa036Sderaadt 		mode_sense(fd, data, sizeof(data), 0, page);
749ce8aa036Sderaadt 
750ce8aa036Sderaadt 		edit_rewind();
751ce8aa036Sderaadt 		scsireq_buff_decode_visit(mode_pars, mh->mdl,
752ce8aa036Sderaadt 		fmt, edit_defaults, 0);
753ce8aa036Sderaadt 
754ce8aa036Sderaadt 		edit_rewind();
755ce8aa036Sderaadt 		scsireq_buff_decode_visit(mode_pars, mh->mdl,
756ce8aa036Sderaadt 		fmt, edit_report, 0);
757ce8aa036Sderaadt 
75814789241Sray 		fclose(edit_file);
7598b791dc9Sray 		if (editit(edit_name) == -1 && errno != ECHILD)
76014789241Sray 			err(1, "edit %s", edit_name);
76114789241Sray 		if ((edit_file = fopen(edit_name, "r")) == NULL)
76214789241Sray 			err(1, "open %s", edit_name);
763ce8aa036Sderaadt 
764ce8aa036Sderaadt 		edit_rewind();
765ce8aa036Sderaadt 		scsireq_buff_encode_visit(mode_pars, mh->mdl,
766ce8aa036Sderaadt 		fmt, edit_get, 0);
767ce8aa036Sderaadt 
768ce8aa036Sderaadt 		/* Eliminate block descriptors:
769ce8aa036Sderaadt 		 */
770ce8aa036Sderaadt 		bcopy((char *)mph, ((char *)mh) + sizeof(*mh),
771ce8aa036Sderaadt 		sizeof(*mph) + mph->page_length);
772ce8aa036Sderaadt 
773ce8aa036Sderaadt 		mh->bdl = 0;
774ce8aa036Sderaadt 		mph = (struct mode_page_header *) (((char *)mh) + sizeof(*mh));
775ce8aa036Sderaadt 		mode_pars = ((char *)mph) + 2;
776ce8aa036Sderaadt 
777ce8aa036Sderaadt #if 0
778ce8aa036Sderaadt 		/* Turn this on to see what you're sending to the
779ce8aa036Sderaadt 		 * device:
780ce8aa036Sderaadt 		 */
781ce8aa036Sderaadt 		edit_rewind();
782ce8aa036Sderaadt 		scsireq_buff_decode_visit(mode_pars,
783ce8aa036Sderaadt 		mh->mdl, fmt, arg_put, 0);
784ce8aa036Sderaadt #endif
785ce8aa036Sderaadt 
786ce8aa036Sderaadt 		edit_done();
787ce8aa036Sderaadt 
788ce8aa036Sderaadt 		/* Make it permanent if pageselect is three.
789ce8aa036Sderaadt 		 */
790ce8aa036Sderaadt 
791ce8aa036Sderaadt 		mph->page_code &= ~0xC0;	/* Clear PS and RESERVED */
792ce8aa036Sderaadt 		mh->mdl = 0;				/* Reserved for mode select */
793ce8aa036Sderaadt 
794ce8aa036Sderaadt 		mode_select(fd, (char *)mh,
795ce8aa036Sderaadt 		sizeof(*mh) + mh->bdl + sizeof(*mph) + mph->page_length,
796ce8aa036Sderaadt 		(pagectl == 3));
797ce8aa036Sderaadt 
798ce8aa036Sderaadt 		exit(0);
799ce8aa036Sderaadt 	}
800ce8aa036Sderaadt 
801ce8aa036Sderaadt 	mode_sense(fd, data, sizeof(data), pagectl, page);
802ce8aa036Sderaadt 
803ce8aa036Sderaadt 	/* Skip over the block descriptors.
804ce8aa036Sderaadt 	 */
805ce8aa036Sderaadt 	mh = (struct mode_header *)data;
806ce8aa036Sderaadt 	mph = (struct mode_page_header *)(((char *)mh) + sizeof(*mh) + mh->bdl);
807ce8aa036Sderaadt 	mode_pars = (char *)mph + sizeof(*mph);
808ce8aa036Sderaadt 
809ce8aa036Sderaadt 	if (!fmt) {
810ce8aa036Sderaadt 		for (i = 0; i < mh->mdl; i++) {
811ce8aa036Sderaadt 			printf("%02x%c",mode_pars[i],
812ce8aa036Sderaadt 			(((i + 1) % 8) == 0) ? '\n' : ' ');
813ce8aa036Sderaadt 		}
814ce8aa036Sderaadt 		putc('\n', stdout);
815ce8aa036Sderaadt 	} else {
816ce8aa036Sderaadt 			verbose = 1;
817ce8aa036Sderaadt 			scsireq_buff_decode_visit(mode_pars,
818ce8aa036Sderaadt 			mh->mdl, fmt, arg_put, 0);
819ce8aa036Sderaadt 	}
820ce8aa036Sderaadt }
821ce8aa036Sderaadt 
8224d6c3a14Sniklas int
main(int argc,char ** argv)8234d6c3a14Sniklas main(int argc, char **argv)
824ce8aa036Sderaadt {
825ce8aa036Sderaadt 	procargs(&argc,&argv);
826ce8aa036Sderaadt 
827ce8aa036Sderaadt 	/* XXX This has grown to the point that it should be cleaned up.
828ce8aa036Sderaadt 	 */
82981d5a439Sdlg 	if (debugflag) {
830ce8aa036Sderaadt 		if (ioctl(fd,SCIOCDEBUG,&debuglevel) == -1)
8318ddb3e99Sokan 			err(1, "SCIOCDEBUG");
832ce8aa036Sderaadt 	} else if (commandflag) {
833ce8aa036Sderaadt 		char *fmt;
834ce8aa036Sderaadt 
835ce8aa036Sderaadt 		if (argc < 1) {
836ce8aa036Sderaadt 			fprintf(stderr, "Need the command format string.\n");
837ce8aa036Sderaadt 			usage();
838ce8aa036Sderaadt 		}
839ce8aa036Sderaadt 
840ce8aa036Sderaadt 
841ce8aa036Sderaadt 		fmt = argv[0];
842ce8aa036Sderaadt 
843ce8aa036Sderaadt 		argc -= 1;
844ce8aa036Sderaadt 		argv += 1;
845ce8aa036Sderaadt 
846ce8aa036Sderaadt 		do_cmd(fd, fmt, argc, argv);
847e763ff64Smickey 	} else if (modeflag)
848ce8aa036Sderaadt 		mode_edit(fd, modepage, editflag, argc, argv);
849e763ff64Smickey 
850ce8aa036Sderaadt 	exit(0);
851ce8aa036Sderaadt }
852