xref: /netbsd-src/usr.sbin/iostat/iostat.c (revision aaf4ece63a859a04e37cf3a7229b5fab0157cc06)
1 /*	$NetBSD: iostat.c,v 1.46 2006/01/08 08:47:16 yamt Exp $	*/
2 
3 /*
4  * Copyright (c) 1996 John M. Vinopal
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed for the NetBSD Project
18  *      by John M. Vinopal.
19  * 4. The name of the author may not be used to endorse or promote products
20  *    derived from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
23  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 /*-
36  * Copyright (c) 1986, 1991, 1993
37  *      The Regents of the University of California.  All rights reserved.
38  *
39  * Redistribution and use in source and binary forms, with or without
40  * modification, are permitted provided that the following conditions
41  * are met:
42  * 1. Redistributions of source code must retain the above copyright
43  *    notice, this list of conditions and the following disclaimer.
44  * 2. Redistributions in binary form must reproduce the above copyright
45  *    notice, this list of conditions and the following disclaimer in the
46  *    documentation and/or other materials provided with the distribution.
47  * 3. Neither the name of the University nor the names of its contributors
48  *    may be used to endorse or promote products derived from this software
49  *    without specific prior written permission.
50  *
51  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
52  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
55  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
56  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
57  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
59  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
60  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
61  * SUCH DAMAGE.
62  */
63 
64 #include <sys/cdefs.h>
65 #ifndef lint
66 __COPYRIGHT("@(#) Copyright (c) 1986, 1991, 1993\n\
67         The Regents of the University of California.  All rights reserved.\n");
68 #endif /* not lint */
69 
70 #ifndef lint
71 #if 0
72 static char sccsid[] = "@(#)iostat.c	8.3 (Berkeley) 4/28/95";
73 #else
74 __RCSID("$NetBSD: iostat.c,v 1.46 2006/01/08 08:47:16 yamt Exp $");
75 #endif
76 #endif /* not lint */
77 
78 #include <sys/types.h>
79 #include <sys/ioctl.h>
80 #include <sys/sched.h>
81 #include <sys/time.h>
82 
83 #include <err.h>
84 #include <ctype.h>
85 #include <signal.h>
86 #include <stdio.h>
87 #include <stdlib.h>
88 #include <string.h>
89 #include <unistd.h>
90 
91 #include "dkstats.h"
92 #include "tpstats.h"
93 
94 /* Namelist and memory files. */
95 char	*nlistf, *memf;
96 
97 int		hz, reps, interval;
98 static int	todo = 0;
99 static int	defdrives;
100 static int	winlines = 20;
101 static int	wincols = 80;
102 
103 #define	ISSET(x, a)	((x) & (a))
104 #define	SHOW_CPU	1<<0
105 #define	SHOW_TTY	1<<1
106 #define	SHOW_STATS_1	1<<2
107 #define	SHOW_STATS_2	1<<3
108 #define	SHOW_STATS_X	1<<4
109 #define	SHOW_TOTALS	1<<7
110 #define	SHOW_STATS_ALL	(SHOW_STATS_1 | SHOW_STATS_2 | SHOW_STATS_X)
111 
112 static void cpustats(void);
113 static void disk_stats(double);
114 static void disk_stats2(double);
115 static void disk_statsx(double);
116 static void sig_header(int);
117 static volatile int do_header;
118 static void header(void);
119 static void usage(void);
120 static void display(void);
121 static int selectdrives(int, char *[]);
122 
123 int main(int, char *[]);
124 
125 int
126 main(int argc, char *argv[])
127 {
128 	int ch, hdrcnt, ndrives, lines;
129 	struct timespec	tv;
130 	struct ttysize ts;
131 
132 	while ((ch = getopt(argc, argv, "Cc:dDIM:N:Tw:x")) != -1)
133 		switch (ch) {
134 		case 'c':
135 			if ((reps = atoi(optarg)) <= 0)
136 				errx(1, "repetition count <= 0.");
137 			break;
138 		case 'C':
139 			todo |= SHOW_CPU;
140 			break;
141 		case 'd':
142 			todo &= ~SHOW_STATS_ALL;
143 			todo |= SHOW_STATS_1;
144 			break;
145 		case 'D':
146 			todo &= ~SHOW_STATS_ALL;
147 			todo |= SHOW_STATS_2;
148 			break;
149 		case 'I':
150 			todo |= SHOW_TOTALS;
151 			break;
152 		case 'M':
153 			memf = optarg;
154 			break;
155 		case 'N':
156 			nlistf = optarg;
157 			break;
158 		case 'T':
159 			todo |= SHOW_TTY;
160 			break;
161 		case 'w':
162 			if ((interval = atoi(optarg)) <= 0)
163 				errx(1, "interval <= 0.");
164 			break;
165 		case 'x':
166 			todo &= ~SHOW_STATS_ALL;
167 			todo |= SHOW_STATS_X;
168 			break;
169 		case '?':
170 		default:
171 			usage();
172 		}
173 	argc -= optind;
174 	argv += optind;
175 
176 	if (!ISSET(todo, SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL))
177 		todo |= SHOW_CPU | SHOW_TTY | SHOW_STATS_1;
178 	if (ISSET(todo, SHOW_STATS_X)) {
179 		todo &= ~(SHOW_CPU | SHOW_TTY | SHOW_STATS_ALL);
180 		todo |= SHOW_STATS_X;
181 	}
182 
183 	if (ioctl(STDOUT_FILENO, TIOCGSIZE, &ts) != -1) {
184 		if (ts.ts_lines)
185 			winlines = ts.ts_lines;
186 		if (ts.ts_cols)
187 			wincols = ts.ts_cols;
188 	}
189 
190 	defdrives = wincols;
191 	if (ISSET(todo, SHOW_CPU))
192 		defdrives -= 16;	/* XXX magic number */
193 	if (ISSET(todo, SHOW_TTY))
194 		defdrives -= 9;		/* XXX magic number */
195 	defdrives /= 18;		/* XXX magic number */
196 
197 	dkinit(0);
198 	tpinit(0);
199 	dkreadstats();
200 	tpreadstats();
201 	ndrives = selectdrives(argc, argv);
202 	if (ndrives == 0) {
203 		/* No drives are selected.  No need to show disk stats. */
204 		todo &= ~SHOW_STATS_ALL;
205 		if (todo == 0)
206 			errx(1, "no drives");
207 	}
208 	if (ISSET(todo, SHOW_STATS_X))
209 		lines = ndrives;
210 	else
211 		lines = 1;
212 
213 	tv.tv_sec = interval;
214 	tv.tv_nsec = 0;
215 
216 	/* print a new header on sigcont */
217 	(void)signal(SIGCONT, sig_header);
218 
219 	for (hdrcnt = 1;;) {
220 		if (do_header ||
221 		    ((hdrcnt -= lines) <= 0) ||
222 		    ISSET(todo, SHOW_STATS_X)) {
223 			do_header = 0;
224 			header();
225 			hdrcnt = winlines - 4;
226 		}
227 
228 		if (!ISSET(todo, SHOW_TOTALS)) {
229 			dkswap();
230 			tpswap();
231 		}
232 
233 		display();
234 
235 		if (reps >= 0 && --reps <= 0)
236 			break;
237 		nanosleep(&tv, NULL);
238 		dkreadstats();
239 		tpreadstats();
240 	}
241 	exit(0);
242 }
243 
244 static void
245 sig_header(int signo)
246 {
247 	do_header = 1;
248 }
249 
250 static void
251 header()
252 {
253 	int i;
254 
255 					/* Main Headers. */
256 	if (ISSET(todo, SHOW_STATS_X)) {
257 		if (ISSET(todo, SHOW_TOTALS)) {
258 			(void)printf(
259 			    "device  read KB/t    xfr   time     MB  ");
260 			(void)printf(" write KB/t    xfr   time     MB\n");
261 		} else {
262 			(void)printf(
263 			    "device  read KB/t    r/s   time     MB/s");
264 			(void)printf(" write KB/t    w/s   time     MB/s\n");
265 		}
266 		return;
267 	}
268 
269 	if (ISSET(todo, SHOW_TTY))
270 		(void)printf("      tty");
271 
272 	if (ISSET(todo, SHOW_STATS_1)) {
273 		for (i = 0; i < dk_ndrive; i++)
274 			if (cur.dk_select[i])
275 				(void)printf("        %9.9s ", cur.dk_name[i]);
276 		for (i = 0; i < tp_ndrive; i++)
277 			if (cur_tape.select[i])
278 				(void)printf("        %9.9s ",
279 					     cur_tape.name[i]);
280 	}
281 
282 	if (ISSET(todo, SHOW_STATS_2)) {
283 		for (i = 0; i < dk_ndrive; i++)
284 			if (cur.dk_select[i])
285 				(void)printf("        %9.9s ", cur.dk_name[i]);
286 		for (i = 0; i < tp_ndrive; i++)
287 			if (cur_tape.select[i])
288 				(void)printf("        %9.9s ",
289 					     cur_tape.name[i]);
290 	}
291 
292 	if (ISSET(todo, SHOW_CPU))
293 		(void)printf("            CPU");
294 
295 	printf("\n");
296 
297 					/* Sub-Headers. */
298 	if (ISSET(todo, SHOW_TTY))
299 		printf(" tin tout");
300 
301 	if (ISSET(todo, SHOW_STATS_1)) {
302 		for (i = 0; i < dk_ndrive; i++)
303 			if (cur.dk_select[i]) {
304 				if (ISSET(todo, SHOW_TOTALS))
305 					(void)printf("  KB/t  xfr  MB   ");
306 				else
307 					(void)printf("  KB/t  t/s  MB/s ");
308 			}
309 		for (i = 0; i < tp_ndrive; i++)
310 			if (cur_tape.select[i]) {
311 				if (ISSET(todo, SHOW_TOTALS))
312 					(void)printf("  KB/t  xfr  MB   ");
313 				else
314 					(void)printf("  KB/t  t/s  MB/s ");
315 			}
316 	}
317 
318 	if (ISSET(todo, SHOW_STATS_2)) {
319 		for (i = 0; i < dk_ndrive; i++)
320 			if (cur.dk_select[i])
321 				(void)printf("    KB   xfr time ");
322 		for (i = 0; i < tp_ndrive; i++)
323 			if (cur_tape.select[i])
324 				(void)printf("    KB   xfr time ");
325 	}
326 
327 	if (ISSET(todo, SHOW_CPU))
328 		(void)printf(" us ni sy in id");
329 	printf("\n");
330 }
331 
332 static void
333 disk_stats(double etime)
334 {
335 	int dn;
336 	double atime, mbps;
337 
338 	for (dn = 0; dn < dk_ndrive; ++dn) {
339 		if (!cur.dk_select[dn])
340 			continue;
341 					/* average Kbytes per transfer. */
342 		if (cur.dk_rxfer[dn] + cur.dk_wxfer[dn])
343 			mbps = ((cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) /
344 			    1024.0) / (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]);
345 		else
346 			mbps = 0.0;
347 		(void)printf(" %5.2f", mbps);
348 
349 					/* average transfers per second. */
350 		(void)printf(" %4.0f",
351 		    (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime);
352 
353 					/* time busy in disk activity */
354 		atime = (double)cur.dk_time[dn].tv_sec +
355 		    ((double)cur.dk_time[dn].tv_usec / (double)1000000);
356 
357 					/* Megabytes per second. */
358 		if (atime != 0.0)
359 			mbps = (cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) /
360 			    (double)(1024 * 1024);
361 		else
362 			mbps = 0;
363 		(void)printf(" %5.2f ", mbps / etime);
364 	}
365 }
366 
367 static void
368 disk_stats2(double etime)
369 {
370 	int dn;
371 	double atime;
372 
373 	for (dn = 0; dn < dk_ndrive; ++dn) {
374 		if (!cur.dk_select[dn])
375 			continue;
376 
377 					/* average kbytes per second. */
378 		(void)printf(" %5.0f",
379 		    (cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) / 1024.0 / etime);
380 
381 					/* average transfers per second. */
382 		(void)printf(" %5.0f",
383 		    (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime);
384 
385 					/* average time busy in disk activity */
386 		atime = (double)cur.dk_time[dn].tv_sec +
387 		    ((double)cur.dk_time[dn].tv_usec / (double)1000000);
388 		(void)printf(" %4.2f ", atime / etime);
389 	}
390 }
391 
392 static void
393 disk_statsx(double etime)
394 {
395 	int dn;
396 	double atime, kbps;
397 
398 	for (dn = 0; dn < dk_ndrive; ++dn) {
399 		if (!cur.dk_select[dn])
400 			continue;
401 
402 		(void)printf("%-8.8s", cur.dk_name[dn]);
403 
404 					/* average read Kbytes per transfer */
405 		if (cur.dk_rxfer[dn])
406 			kbps = (cur.dk_rbytes[dn] / 1024.0) / cur.dk_rxfer[dn];
407 		else
408 			kbps = 0.0;
409 		(void)printf(" %8.2f", kbps);
410 
411 					/* average read transfers
412 					   (per second) */
413 		(void)printf(" %6.0f", cur.dk_rxfer[dn] / etime);
414 
415 					/* time read busy in disk activity */
416 		atime = (double)cur.dk_time[dn].tv_sec +
417 		    ((double)cur.dk_time[dn].tv_usec / (double)1000000);
418 		(void)printf(" %6.2f", atime / etime);
419 
420 					/* average read megabytes
421 					   (per second) */
422 		(void)printf(" %8.2f",
423 		    cur.dk_rbytes[dn] / (1024.0 * 1024) / etime);
424 
425 
426 					/* average write Kbytes per transfer */
427 		if (cur.dk_wxfer[dn])
428 			kbps = (cur.dk_wbytes[dn] / 1024.0) / cur.dk_wxfer[dn];
429 		else
430 			kbps = 0.0;
431 		(void)printf("   %8.2f", kbps);
432 
433 					/* average write transfers
434 					   (per second) */
435 		(void)printf(" %6.0f", cur.dk_wxfer[dn] / etime);
436 
437 					/* time write busy in disk activity */
438 		atime = (double)cur.dk_time[dn].tv_sec +
439 		    ((double)cur.dk_time[dn].tv_usec / (double)1000000);
440 		(void)printf(" %6.2f", atime / etime);
441 
442 					/* average write megabytes
443 					   (per second) */
444 		(void)printf(" %8.2f\n",
445 		    cur.dk_wbytes[dn] / (1024.0 * 1024) / etime);
446 	}
447 }
448 
449 static void
450 tape_stats(double etime)
451 {
452 	int dn;
453 	double atime, mbps;
454 
455 	for (dn = 0; dn < tp_ndrive; ++dn) {
456 		if (!cur_tape.select[dn])
457 			continue;
458 					/* average Kbytes per transfer. */
459 		if (cur_tape.rxfer[dn] + cur_tape.wxfer[dn])
460 			mbps = ((cur_tape.rbytes[dn] + cur_tape.wbytes[dn]) /
461 			    1024.0) / (cur_tape.rxfer[dn] + cur_tape.wxfer[dn]);
462 		else
463 			mbps = 0.0;
464 		(void)printf(" %5.2f", mbps);
465 
466 					/* average transfers per second. */
467 		(void)printf(" %4.0f",
468 		    (cur_tape.rxfer[dn] + cur_tape.wxfer[dn]) / etime);
469 
470 					/* time busy in disk activity */
471 		atime = (double)cur_tape.time[dn].tv_sec +
472 		    ((double)cur_tape.time[dn].tv_usec / (double)1000000);
473 
474 					/* Megabytes per second. */
475 		if (atime != 0.0)
476 			mbps = (cur_tape.rbytes[dn] + cur_tape.wbytes[dn]) /
477 			    (double)(1024 * 1024);
478 		else
479 			mbps = 0;
480 		(void)printf(" %5.2f ", mbps / etime);
481 	}
482 }
483 
484 static void
485 tape_stats2(double etime)
486 {
487 	int dn;
488 	double atime;
489 
490 	for (dn = 0; dn < tp_ndrive; ++dn) {
491 		if (!cur_tape.select[dn])
492 			continue;
493 
494 					/* average kbytes per second. */
495 		(void)printf(" %5.0f",
496 		    (cur_tape.rbytes[dn] + cur_tape.wbytes[dn]) / 1024.0 / etime);
497 
498 					/* average transfers per second. */
499 		(void)printf(" %5.0f",
500 		    (cur_tape.rxfer[dn] + cur_tape.wxfer[dn]) / etime);
501 
502 					/* average time busy in disk activity */
503 		atime = (double)cur_tape.time[dn].tv_sec +
504 		    ((double)cur_tape.time[dn].tv_usec / (double)1000000);
505 		(void)printf(" %4.2f ", atime / etime);
506 	}
507 }
508 
509 static void
510 tape_statsx(double etime)
511 {
512 	int dn;
513 	double atime, kbps;
514 
515 	for (dn = 0; dn < tp_ndrive; ++dn) {
516 		if (!cur_tape.select[dn])
517 			continue;
518 
519 		(void)printf("%-8.8s", cur_tape.name[dn]);
520 
521 					/* average read Kbytes per transfer */
522 		if (cur.dk_rxfer[dn])
523 			kbps = (cur_tape.rbytes[dn] / 1024.0) / cur_tape.rxfer[dn];
524 		else
525 			kbps = 0.0;
526 		(void)printf(" %8.2f", kbps);
527 
528 					/* average read transfers
529 					   (per second) */
530 		(void)printf(" %6.0f", cur_tape.rxfer[dn] / etime);
531 
532 					/* time read busy in disk activity */
533 		atime = (double)cur_tape.time[dn].tv_sec +
534 		    ((double)cur_tape.time[dn].tv_usec / (double)1000000);
535 		(void)printf(" %6.2f", atime / etime);
536 
537 					/* average read megabytes
538 					   (per second) */
539 		(void)printf(" %8.2f",
540 		    cur_tape.rbytes[dn] / (1024.0 * 1024) / etime);
541 
542 
543 					/* average write Kbytes per transfer */
544 		if (cur_tape.wxfer[dn])
545 			kbps = (cur_tape.wbytes[dn] / 1024.0) / cur_tape.wxfer[dn];
546 		else
547 			kbps = 0.0;
548 		(void)printf("   %8.2f", kbps);
549 
550 					/* average write transfers
551 					   (per second) */
552 		(void)printf(" %6.0f", cur_tape.wxfer[dn] / etime);
553 
554 					/* time write busy in disk activity */
555 		atime = (double)cur_tape.time[dn].tv_sec +
556 		    ((double)cur_tape.time[dn].tv_usec / (double)1000000);
557 		(void)printf(" %6.2f", atime / etime);
558 
559 					/* average write megabytes
560 					   (per second) */
561 		(void)printf(" %8.2f\n",
562 		    cur_tape.wbytes[dn] / (1024.0 * 1024) / etime);
563 	}
564 }
565 
566 static void
567 cpustats(void)
568 {
569 	int state;
570 	double time;
571 
572 	time = 0;
573 	for (state = 0; state < CPUSTATES; ++state)
574 		time += cur.cp_time[state];
575 	if (!time)
576 		time = 1.0;
577 			/* States are generally never 100% and can use %3.0f. */
578 	for (state = 0; state < CPUSTATES; ++state)
579 		printf(" %2.0f", 100. * cur.cp_time[state] / time);
580 }
581 
582 static void
583 usage(void)
584 {
585 
586 	(void)fprintf(stderr, "usage: iostat [-CdDITx] [-c count] [-M core] "
587 	    "[-N system] [-w wait] [drives]\n");
588 	exit(1);
589 }
590 
591 static void
592 display(void)
593 {
594 	double	etime;
595 
596 	/* Sum up the elapsed ticks. */
597 	etime = cur.cp_etime;
598 
599 	/*
600 	 * If we're showing totals only, then don't divide by the
601 	 * system time.
602 	 */
603 	if (ISSET(todo, SHOW_TOTALS))
604 		etime = 1.0;
605 
606 	if (ISSET(todo, SHOW_STATS_X)) {
607 		disk_statsx(etime);
608 		tape_statsx(etime);
609 		goto out;
610 	}
611 
612 	if (ISSET(todo, SHOW_TTY))
613 		printf("%4.0f %4.0f", cur.tk_nin / etime, cur.tk_nout / etime);
614 
615 	if (ISSET(todo, SHOW_STATS_1)) {
616 		disk_stats(etime);
617 		tape_stats(etime);
618 	}
619 
620 
621 	if (ISSET(todo, SHOW_STATS_2)) {
622 		disk_stats2(etime);
623 		tape_stats2(etime);
624 	}
625 
626 
627 	if (ISSET(todo, SHOW_CPU))
628 		cpustats();
629 
630 	(void)printf("\n");
631 
632 out:
633 	(void)fflush(stdout);
634 }
635 
636 static int
637 selectdrives(int argc, char *argv[])
638 {
639 	int	i, maxdrives, ndrives, tried;
640 
641 	/*
642 	 * Choose drives to be displayed.  Priority goes to (in order) drives
643 	 * supplied as arguments and default drives.  If everything isn't
644 	 * filled in and there are drives not taken care of, display the first
645 	 * few that fit.
646 	 *
647 	 * The backward compatibility #ifdefs permit the syntax:
648 	 *	iostat [ drives ] [ interval [ count ] ]
649 	 */
650 
651 #define	BACKWARD_COMPATIBILITY
652 	for (tried = ndrives = 0; *argv; ++argv) {
653 #ifdef BACKWARD_COMPATIBILITY
654 		if (isdigit((unsigned char)**argv))
655 			break;
656 #endif
657 		tried++;
658 		for (i = 0; i < dk_ndrive; i++) {
659 			if (strcmp(cur.dk_name[i], *argv))
660 				continue;
661 			cur.dk_select[i] = 1;
662 			++ndrives;
663 		}
664 
665 		for (i = 0; i < tp_ndrive; i++) {
666 			if (strcmp(cur_tape.name[i], *argv))
667 				continue;
668 			cur_tape.select[i] = 1;
669 			++ndrives;
670 		}
671 	}
672 
673 	if (ndrives == 0 && tried == 0) {
674 		/*
675 		 * Pick up to defdrives (or all if -x is given) drives
676 		 * if none specified.
677 		 */
678 		maxdrives = (ISSET(todo, SHOW_STATS_X) ||
679 			     (dk_ndrive + tp_ndrive) < defdrives)
680 			? (dk_ndrive + tp_ndrive) : defdrives;
681 		for (i = 0; i < maxdrives; i++) {
682 			if (i >= dk_ndrive) {
683 				cur_tape.select[i - dk_ndrive] = 1;
684 			} else {
685 				cur.dk_select[i] = 1;
686 			}
687 
688 			++ndrives;
689 			if (!ISSET(todo, SHOW_STATS_X) && ndrives == defdrives)
690 				break;
691 		}
692 	}
693 
694 #ifdef BACKWARD_COMPATIBILITY
695 	if (*argv) {
696 		interval = atoi(*argv);
697 		if (*++argv)
698 			reps = atoi(*argv);
699 	}
700 #endif
701 
702 	if (interval) {
703 		if (!reps)
704 			reps = -1;
705 	} else
706 		if (reps)
707 			interval = 1;
708 
709 	return (ndrives);
710 }
711