xref: /netbsd-src/usr.sbin/iostat/iostat.c (revision eb961d0e02b7a46a9acfa877b02df48df6637278)
1 /*	$NetBSD: iostat.c,v 1.48 2006/02/12 22:11:54 dsl 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.48 2006/02/12 22:11:54 dsl 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 	cpureadstats();
200 	dkreadstats();
201 	tpreadstats();
202 	ndrives = selectdrives(argc, argv);
203 	if (ndrives == 0) {
204 		/* No drives are selected.  No need to show disk stats. */
205 		todo &= ~SHOW_STATS_ALL;
206 		if (todo == 0)
207 			errx(1, "no drives");
208 	}
209 	if (ISSET(todo, SHOW_STATS_X))
210 		lines = ndrives;
211 	else
212 		lines = 1;
213 
214 	tv.tv_sec = interval;
215 	tv.tv_nsec = 0;
216 
217 	/* print a new header on sigcont */
218 	(void)signal(SIGCONT, sig_header);
219 
220 	for (hdrcnt = 1;;) {
221 		if (do_header || lines > 1 || (hdrcnt -= lines) <= 0) {
222 			do_header = 0;
223 			header();
224 			hdrcnt = winlines - 4;
225 		}
226 
227 		if (!ISSET(todo, SHOW_TOTALS)) {
228 			cpuswap();
229 			dkswap();
230 			tpswap();
231 		}
232 
233 		display();
234 
235 		if (reps >= 0 && --reps <= 0)
236 			break;
237 		nanosleep(&tv, NULL);
238 		cpureadstats();
239 		dkreadstats();
240 		tpreadstats();
241 	}
242 	exit(0);
243 }
244 
245 static void
246 sig_header(int signo)
247 {
248 	do_header = 1;
249 }
250 
251 static void
252 header()
253 {
254 	int i;
255 
256 					/* Main Headers. */
257 	if (ISSET(todo, SHOW_STATS_X)) {
258 		if (ISSET(todo, SHOW_TOTALS)) {
259 			(void)printf(
260 			    "device  read KB/t    xfr   time     MB  ");
261 			(void)printf(" write KB/t    xfr   time     MB\n");
262 		} else {
263 			(void)printf(
264 			    "device  read KB/t    r/s   time     MB/s");
265 			(void)printf(" write KB/t    w/s   time     MB/s\n");
266 		}
267 		return;
268 	}
269 
270 	if (ISSET(todo, SHOW_TTY))
271 		(void)printf("      tty");
272 
273 	if (ISSET(todo, SHOW_STATS_1)) {
274 		for (i = 0; i < dk_ndrive; i++)
275 			if (cur.dk_select[i])
276 				(void)printf("        %9.9s ", cur.dk_name[i]);
277 		for (i = 0; i < tp_ndrive; i++)
278 			if (cur_tape.select[i])
279 				(void)printf("        %9.9s ",
280 					     cur_tape.name[i]);
281 	}
282 
283 	if (ISSET(todo, SHOW_STATS_2)) {
284 		for (i = 0; i < dk_ndrive; i++)
285 			if (cur.dk_select[i])
286 				(void)printf("        %9.9s ", cur.dk_name[i]);
287 		for (i = 0; i < tp_ndrive; i++)
288 			if (cur_tape.select[i])
289 				(void)printf("        %9.9s ",
290 					     cur_tape.name[i]);
291 	}
292 
293 	if (ISSET(todo, SHOW_CPU))
294 		(void)printf("            CPU");
295 
296 	printf("\n");
297 
298 					/* Sub-Headers. */
299 	if (ISSET(todo, SHOW_TTY))
300 		printf(" tin tout");
301 
302 	if (ISSET(todo, SHOW_STATS_1)) {
303 		for (i = 0; i < dk_ndrive; i++)
304 			if (cur.dk_select[i]) {
305 				if (ISSET(todo, SHOW_TOTALS))
306 					(void)printf("  KB/t  xfr  MB   ");
307 				else
308 					(void)printf("  KB/t  t/s  MB/s ");
309 			}
310 		for (i = 0; i < tp_ndrive; i++)
311 			if (cur_tape.select[i]) {
312 				if (ISSET(todo, SHOW_TOTALS))
313 					(void)printf("  KB/t  xfr  MB   ");
314 				else
315 					(void)printf("  KB/t  t/s  MB/s ");
316 			}
317 	}
318 
319 	if (ISSET(todo, SHOW_STATS_2)) {
320 		for (i = 0; i < dk_ndrive; i++)
321 			if (cur.dk_select[i])
322 				(void)printf("    KB   xfr time ");
323 		for (i = 0; i < tp_ndrive; i++)
324 			if (cur_tape.select[i])
325 				(void)printf("    KB   xfr time ");
326 	}
327 
328 	if (ISSET(todo, SHOW_CPU))
329 		(void)printf(" us ni sy in id");
330 	printf("\n");
331 }
332 
333 static void
334 disk_stats(double etime)
335 {
336 	int dn;
337 	double atime, mbps;
338 
339 	for (dn = 0; dn < dk_ndrive; ++dn) {
340 		if (!cur.dk_select[dn])
341 			continue;
342 					/* average Kbytes per transfer. */
343 		if (cur.dk_rxfer[dn] + cur.dk_wxfer[dn])
344 			mbps = ((cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) /
345 			    1024.0) / (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]);
346 		else
347 			mbps = 0.0;
348 		(void)printf(" %5.2f", mbps);
349 
350 					/* average transfers per second. */
351 		(void)printf(" %4.0f",
352 		    (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime);
353 
354 					/* time busy in disk activity */
355 		atime = (double)cur.dk_time[dn].tv_sec +
356 		    ((double)cur.dk_time[dn].tv_usec / (double)1000000);
357 
358 					/* Megabytes per second. */
359 		if (atime != 0.0)
360 			mbps = (cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) /
361 			    (double)(1024 * 1024);
362 		else
363 			mbps = 0;
364 		(void)printf(" %5.2f ", mbps / etime);
365 	}
366 }
367 
368 static void
369 disk_stats2(double etime)
370 {
371 	int dn;
372 	double atime;
373 
374 	for (dn = 0; dn < dk_ndrive; ++dn) {
375 		if (!cur.dk_select[dn])
376 			continue;
377 
378 					/* average kbytes per second. */
379 		(void)printf(" %5.0f",
380 		    (cur.dk_rbytes[dn] + cur.dk_wbytes[dn]) / 1024.0 / etime);
381 
382 					/* average transfers per second. */
383 		(void)printf(" %5.0f",
384 		    (cur.dk_rxfer[dn] + cur.dk_wxfer[dn]) / etime);
385 
386 					/* average time busy in disk activity */
387 		atime = (double)cur.dk_time[dn].tv_sec +
388 		    ((double)cur.dk_time[dn].tv_usec / (double)1000000);
389 		(void)printf(" %4.2f ", atime / etime);
390 	}
391 }
392 
393 static void
394 disk_statsx(double etime)
395 {
396 	int dn;
397 	double atime, kbps;
398 
399 	for (dn = 0; dn < dk_ndrive; ++dn) {
400 		if (!cur.dk_select[dn])
401 			continue;
402 
403 		(void)printf("%-8.8s", cur.dk_name[dn]);
404 
405 					/* average read Kbytes per transfer */
406 		if (cur.dk_rxfer[dn])
407 			kbps = (cur.dk_rbytes[dn] / 1024.0) / cur.dk_rxfer[dn];
408 		else
409 			kbps = 0.0;
410 		(void)printf(" %8.2f", kbps);
411 
412 					/* average read transfers
413 					   (per second) */
414 		(void)printf(" %6.0f", cur.dk_rxfer[dn] / etime);
415 
416 					/* time read busy in disk activity */
417 		atime = (double)cur.dk_time[dn].tv_sec +
418 		    ((double)cur.dk_time[dn].tv_usec / (double)1000000);
419 		(void)printf(" %6.2f", atime / etime);
420 
421 					/* average read megabytes
422 					   (per second) */
423 		(void)printf(" %8.2f",
424 		    cur.dk_rbytes[dn] / (1024.0 * 1024) / etime);
425 
426 
427 					/* average write Kbytes per transfer */
428 		if (cur.dk_wxfer[dn])
429 			kbps = (cur.dk_wbytes[dn] / 1024.0) / cur.dk_wxfer[dn];
430 		else
431 			kbps = 0.0;
432 		(void)printf("   %8.2f", kbps);
433 
434 					/* average write transfers
435 					   (per second) */
436 		(void)printf(" %6.0f", cur.dk_wxfer[dn] / etime);
437 
438 					/* time write busy in disk activity */
439 		atime = (double)cur.dk_time[dn].tv_sec +
440 		    ((double)cur.dk_time[dn].tv_usec / (double)1000000);
441 		(void)printf(" %6.2f", atime / etime);
442 
443 					/* average write megabytes
444 					   (per second) */
445 		(void)printf(" %8.2f\n",
446 		    cur.dk_wbytes[dn] / (1024.0 * 1024) / etime);
447 	}
448 }
449 
450 static void
451 tape_stats(double etime)
452 {
453 	int dn;
454 	double atime, mbps;
455 
456 	for (dn = 0; dn < tp_ndrive; ++dn) {
457 		if (!cur_tape.select[dn])
458 			continue;
459 					/* average Kbytes per transfer. */
460 		if (cur_tape.rxfer[dn] + cur_tape.wxfer[dn])
461 			mbps = ((cur_tape.rbytes[dn] + cur_tape.wbytes[dn]) /
462 			    1024.0) / (cur_tape.rxfer[dn] + cur_tape.wxfer[dn]);
463 		else
464 			mbps = 0.0;
465 		(void)printf(" %5.2f", mbps);
466 
467 					/* average transfers per second. */
468 		(void)printf(" %4.0f",
469 		    (cur_tape.rxfer[dn] + cur_tape.wxfer[dn]) / etime);
470 
471 					/* time busy in disk activity */
472 		atime = (double)cur_tape.time[dn].tv_sec +
473 		    ((double)cur_tape.time[dn].tv_usec / (double)1000000);
474 
475 					/* Megabytes per second. */
476 		if (atime != 0.0)
477 			mbps = (cur_tape.rbytes[dn] + cur_tape.wbytes[dn]) /
478 			    (double)(1024 * 1024);
479 		else
480 			mbps = 0;
481 		(void)printf(" %5.2f ", mbps / etime);
482 	}
483 }
484 
485 static void
486 tape_stats2(double etime)
487 {
488 	int dn;
489 	double atime;
490 
491 	for (dn = 0; dn < tp_ndrive; ++dn) {
492 		if (!cur_tape.select[dn])
493 			continue;
494 
495 					/* average kbytes per second. */
496 		(void)printf(" %5.0f",
497 		    (cur_tape.rbytes[dn] + cur_tape.wbytes[dn]) / 1024.0 / etime);
498 
499 					/* average transfers per second. */
500 		(void)printf(" %5.0f",
501 		    (cur_tape.rxfer[dn] + cur_tape.wxfer[dn]) / etime);
502 
503 					/* average time busy in disk activity */
504 		atime = (double)cur_tape.time[dn].tv_sec +
505 		    ((double)cur_tape.time[dn].tv_usec / (double)1000000);
506 		(void)printf(" %4.2f ", atime / etime);
507 	}
508 }
509 
510 static void
511 tape_statsx(double etime)
512 {
513 	int dn;
514 	double atime, kbps;
515 
516 	for (dn = 0; dn < tp_ndrive; ++dn) {
517 		if (!cur_tape.select[dn])
518 			continue;
519 
520 		(void)printf("%-8.8s", cur_tape.name[dn]);
521 
522 					/* average read Kbytes per transfer */
523 		if (cur.dk_rxfer[dn])
524 			kbps = (cur_tape.rbytes[dn] / 1024.0) / cur_tape.rxfer[dn];
525 		else
526 			kbps = 0.0;
527 		(void)printf(" %8.2f", kbps);
528 
529 					/* average read transfers
530 					   (per second) */
531 		(void)printf(" %6.0f", cur_tape.rxfer[dn] / etime);
532 
533 					/* time read busy in disk activity */
534 		atime = (double)cur_tape.time[dn].tv_sec +
535 		    ((double)cur_tape.time[dn].tv_usec / (double)1000000);
536 		(void)printf(" %6.2f", atime / etime);
537 
538 					/* average read megabytes
539 					   (per second) */
540 		(void)printf(" %8.2f",
541 		    cur_tape.rbytes[dn] / (1024.0 * 1024) / etime);
542 
543 
544 					/* average write Kbytes per transfer */
545 		if (cur_tape.wxfer[dn])
546 			kbps = (cur_tape.wbytes[dn] / 1024.0) / cur_tape.wxfer[dn];
547 		else
548 			kbps = 0.0;
549 		(void)printf("   %8.2f", kbps);
550 
551 					/* average write transfers
552 					   (per second) */
553 		(void)printf(" %6.0f", cur_tape.wxfer[dn] / etime);
554 
555 					/* time write busy in disk activity */
556 		atime = (double)cur_tape.time[dn].tv_sec +
557 		    ((double)cur_tape.time[dn].tv_usec / (double)1000000);
558 		(void)printf(" %6.2f", atime / etime);
559 
560 					/* average write megabytes
561 					   (per second) */
562 		(void)printf(" %8.2f\n",
563 		    cur_tape.wbytes[dn] / (1024.0 * 1024) / etime);
564 	}
565 }
566 
567 static void
568 cpustats(void)
569 {
570 	int state;
571 	double time;
572 
573 	time = 0;
574 	for (state = 0; state < CPUSTATES; ++state)
575 		time += cur.cp_time[state];
576 	if (!time)
577 		time = 1.0;
578 			/* States are generally never 100% and can use %3.0f. */
579 	for (state = 0; state < CPUSTATES; ++state)
580 		printf(" %2.0f", 100. * cur.cp_time[state] / time);
581 }
582 
583 static void
584 usage(void)
585 {
586 
587 	(void)fprintf(stderr, "usage: iostat [-CdDITx] [-c count] [-M core] "
588 	    "[-N system] [-w wait] [drives]\n");
589 	exit(1);
590 }
591 
592 static void
593 display(void)
594 {
595 	double	etime;
596 
597 	/* Sum up the elapsed ticks. */
598 	etime = cur.cp_etime;
599 
600 	/*
601 	 * If we're showing totals only, then don't divide by the
602 	 * system time.
603 	 */
604 	if (ISSET(todo, SHOW_TOTALS))
605 		etime = 1.0;
606 
607 	if (ISSET(todo, SHOW_STATS_X)) {
608 		disk_statsx(etime);
609 		tape_statsx(etime);
610 		goto out;
611 	}
612 
613 	if (ISSET(todo, SHOW_TTY))
614 		printf("%4.0f %4.0f", cur.tk_nin / etime, cur.tk_nout / etime);
615 
616 	if (ISSET(todo, SHOW_STATS_1)) {
617 		disk_stats(etime);
618 		tape_stats(etime);
619 	}
620 
621 
622 	if (ISSET(todo, SHOW_STATS_2)) {
623 		disk_stats2(etime);
624 		tape_stats2(etime);
625 	}
626 
627 
628 	if (ISSET(todo, SHOW_CPU))
629 		cpustats();
630 
631 	(void)printf("\n");
632 
633 out:
634 	(void)fflush(stdout);
635 }
636 
637 static int
638 selectdrives(int argc, char *argv[])
639 {
640 	int	i, maxdrives, ndrives, tried;
641 
642 	/*
643 	 * Choose drives to be displayed.  Priority goes to (in order) drives
644 	 * supplied as arguments and default drives.  If everything isn't
645 	 * filled in and there are drives not taken care of, display the first
646 	 * few that fit.
647 	 *
648 	 * The backward compatibility #ifdefs permit the syntax:
649 	 *	iostat [ drives ] [ interval [ count ] ]
650 	 */
651 
652 #define	BACKWARD_COMPATIBILITY
653 	for (tried = ndrives = 0; *argv; ++argv) {
654 #ifdef BACKWARD_COMPATIBILITY
655 		if (isdigit((unsigned char)**argv))
656 			break;
657 #endif
658 		tried++;
659 		for (i = 0; i < dk_ndrive; i++) {
660 			if (strcmp(cur.dk_name[i], *argv))
661 				continue;
662 			cur.dk_select[i] = 1;
663 			++ndrives;
664 		}
665 
666 		for (i = 0; i < tp_ndrive; i++) {
667 			if (strcmp(cur_tape.name[i], *argv))
668 				continue;
669 			cur_tape.select[i] = 1;
670 			++ndrives;
671 		}
672 	}
673 
674 	if (ndrives == 0 && tried == 0) {
675 		/*
676 		 * Pick up to defdrives (or all if -x is given) drives
677 		 * if none specified.
678 		 */
679 		maxdrives = (ISSET(todo, SHOW_STATS_X) ||
680 			     (dk_ndrive + tp_ndrive) < defdrives)
681 			? (dk_ndrive + tp_ndrive) : defdrives;
682 		for (i = 0; i < maxdrives; i++) {
683 			if (i >= dk_ndrive) {
684 				cur_tape.select[i - dk_ndrive] = 1;
685 			} else {
686 				cur.dk_select[i] = 1;
687 			}
688 
689 			++ndrives;
690 			if (!ISSET(todo, SHOW_STATS_X) && ndrives == defdrives)
691 				break;
692 		}
693 	}
694 
695 #ifdef BACKWARD_COMPATIBILITY
696 	if (*argv) {
697 		interval = atoi(*argv);
698 		if (*++argv)
699 			reps = atoi(*argv);
700 	}
701 #endif
702 
703 	if (interval) {
704 		if (!reps)
705 			reps = -1;
706 	} else
707 		if (reps)
708 			interval = 1;
709 
710 	return (ndrives);
711 }
712