ifmedia.c revision 25450
125450Speter/*	$NetBSD: ifconfig.c,v 1.34 1997/04/21 01:17:58 lukem Exp $	*/
225450Speter/*	$Id$ */
325450Speter
425450Speter/*
525450Speter * Copyright (c) 1997 Jason R. Thorpe.
625450Speter * All rights reserved.
725450Speter *
825450Speter * Redistribution and use in source and binary forms, with or without
925450Speter * modification, are permitted provided that the following conditions
1025450Speter * are met:
1125450Speter * 1. Redistributions of source code must retain the above copyright
1225450Speter *    notice, this list of conditions and the following disclaimer.
1325450Speter * 2. Redistributions in binary form must reproduce the above copyright
1425450Speter *    notice, this list of conditions and the following disclaimer in the
1525450Speter *    documentation and/or other materials provided with the distribution.
1625450Speter * 3. All advertising materials mentioning features or use of this software
1725450Speter *    must display the following acknowledgement:
1825450Speter *      This product includes software developed for the NetBSD Project
1925450Speter *	by Jason R. Thorpe.
2025450Speter * 4. The name of the author may not be used to endorse or promote products
2125450Speter *    derived from this software without specific prior written permission.
2225450Speter *
2325450Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2425450Speter * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2525450Speter * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2625450Speter * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2725450Speter * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
2825450Speter * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2925450Speter * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
3025450Speter * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
3125450Speter * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3225450Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3325450Speter * SUCH DAMAGE.
3425450Speter */
3525450Speter
3625450Speter/*
3725450Speter * Copyright (c) 1983, 1993
3825450Speter *	The Regents of the University of California.  All rights reserved.
3925450Speter *
4025450Speter * Redistribution and use in source and binary forms, with or without
4125450Speter * modification, are permitted provided that the following conditions
4225450Speter * are met:
4325450Speter * 1. Redistributions of source code must retain the above copyright
4425450Speter *    notice, this list of conditions and the following disclaimer.
4525450Speter * 2. Redistributions in binary form must reproduce the above copyright
4625450Speter *    notice, this list of conditions and the following disclaimer in the
4725450Speter *    documentation and/or other materials provided with the distribution.
4825450Speter * 3. All advertising materials mentioning features or use of this software
4925450Speter *    must display the following acknowledgement:
5025450Speter *	This product includes software developed by the University of
5125450Speter *	California, Berkeley and its contributors.
5225450Speter * 4. Neither the name of the University nor the names of its contributors
5325450Speter *    may be used to endorse or promote products derived from this software
5425450Speter *    without specific prior written permission.
5525450Speter *
5625450Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
5725450Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
5825450Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
5925450Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
6025450Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
6125450Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
6225450Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
6325450Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
6425450Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
6525450Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
6625450Speter * SUCH DAMAGE.
6725450Speter */
6825450Speter
6925450Speter#include <sys/param.h>
7025450Speter#include <sys/ioctl.h>
7125450Speter#include <sys/socket.h>
7225450Speter#include <sys/sysctl.h>
7325450Speter#include <sys/time.h>
7425450Speter
7525450Speter#include <net/if.h>
7625450Speter#include <net/if_var.h>
7725450Speter#include <net/if_dl.h>
7825450Speter#include <net/if_types.h>
7925450Speter#include <net/if_media.h>
8025450Speter#include <net/route.h>
8125450Speter
8225450Speter#include <ctype.h>
8325450Speter#include <err.h>
8425450Speter#include <errno.h>
8525450Speter#include <fcntl.h>
8625450Speter#include <stdio.h>
8725450Speter#include <stdlib.h>
8825450Speter#include <string.h>
8925450Speter#include <unistd.h>
9025450Speter
9125450Speter#include "ifconfig.h"
9225450Speter
9325450Speterstatic void	domediaopt __P((const char *, int, int));
9425450Speterstatic int	get_media_subtype __P((int, const char *));
9525450Speterstatic int	get_media_options __P((int, const char *));
9625450Speterstatic int	lookup_media_word __P((struct ifmedia_description *, const char *));
9725450Speterstatic void	print_media_word __P((int));
9825450Speter
9925450Spetervoid
10025450Spetermedia_status(s)
10125450Speter	int s;
10225450Speter{
10325450Speter	struct ifmediareq ifmr;
10425450Speter	int *media_list, i;
10525450Speter
10625450Speter	(void) memset(&ifmr, 0, sizeof(ifmr));
10725450Speter	(void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
10825450Speter
10925450Speter	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
11025450Speter		/*
11125450Speter		 * Interface doesn't support SIOC{G,S}IFMEDIA.
11225450Speter		 */
11325450Speter		return;
11425450Speter	}
11525450Speter
11625450Speter	if (ifmr.ifm_count == 0) {
11725450Speter		warnx("%s: no media types?", name);
11825450Speter		return;
11925450Speter	}
12025450Speter
12125450Speter	media_list = (int *)malloc(ifmr.ifm_count * sizeof(int));
12225450Speter	if (media_list == NULL)
12325450Speter		err(1, "malloc");
12425450Speter	ifmr.ifm_ulist = media_list;
12525450Speter
12625450Speter	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0)
12725450Speter		err(1, "SIOCGIFMEDIA");
12825450Speter
12925450Speter	printf("\tmedia: ");
13025450Speter	print_media_word(ifmr.ifm_current);
13125450Speter	if (ifmr.ifm_active != ifmr.ifm_current) {
13225450Speter		putchar(' ');
13325450Speter		putchar('(');
13425450Speter		print_media_word(ifmr.ifm_active);
13525450Speter		putchar(')');
13625450Speter	}
13725450Speter
13825450Speter	if (ifmr.ifm_status & IFM_AVALID) {
13925450Speter		printf(" status: ");
14025450Speter		switch (IFM_TYPE(ifmr.ifm_active)) {
14125450Speter		case IFM_ETHER:
14225450Speter			if (ifmr.ifm_status & IFM_ACTIVE)
14325450Speter				printf("active");
14425450Speter			else
14525450Speter				printf("no carrier");
14625450Speter			break;
14725450Speter
14825450Speter		case IFM_FDDI:
14925450Speter		case IFM_TOKEN:
15025450Speter			if (ifmr.ifm_status & IFM_ACTIVE)
15125450Speter				printf("inserted");
15225450Speter			else
15325450Speter				printf("no ring");
15425450Speter			break;
15525450Speter		}
15625450Speter	}
15725450Speter
15825450Speter	putchar('\n');
15925450Speter
16025450Speter	if (allmedia) {
16125450Speter		printf("\tsupported media:");
16225450Speter		for (i = 0; i < ifmr.ifm_count; i++) {
16325450Speter			putchar(' ');
16425450Speter			print_media_word(media_list[i]);
16525450Speter		}
16625450Speter		putchar('\n');
16725450Speter	}
16825450Speter
16925450Speter	free(media_list);
17025450Speter}
17125450Speter
17225450Spetervoid
17325450Spetersetmedia(val, d, s)
17425450Speter	const char *val;
17525450Speter	int d;
17625450Speter	int s;
17725450Speter{
17825450Speter	struct ifmediareq ifmr;
17925450Speter	int first_type, subtype;
18025450Speter
18125450Speter	(void) memset(&ifmr, 0, sizeof(ifmr));
18225450Speter	(void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
18325450Speter
18425450Speter	ifmr.ifm_count = 1;
18525450Speter	ifmr.ifm_ulist = &first_type;
18625450Speter	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
18725450Speter		/*
18825450Speter		 * If we get E2BIG, the kernel is telling us
18925450Speter		 * that there are more, so we can ignore it.
19025450Speter		 */
19125450Speter		if (errno != E2BIG)
19225450Speter			err(1, "SIOCGIFMEDIA");
19325450Speter	}
19425450Speter
19525450Speter	if (ifmr.ifm_count == 0)
19625450Speter		errx(1, "%s: no media types?", name);
19725450Speter
19825450Speter	/*
19925450Speter	 * We are primarily concerned with the top-level type.
20025450Speter	 * However, "current" may be only IFM_NONE, so we just look
20125450Speter	 * for the top-level type in the first "supported type"
20225450Speter	 * entry.
20325450Speter	 *
20425450Speter	 * (I'm assuming that all supported media types for a given
20525450Speter	 * interface will be the same top-level type..)
20625450Speter	 */
20725450Speter	subtype = get_media_subtype(IFM_TYPE(first_type), val);
20825450Speter
20925450Speter	strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
21025450Speter	ifr.ifr_media = (ifmr.ifm_current & ~(IFM_NMASK|IFM_TMASK)) |
21125450Speter	    IFM_TYPE(first_type) | subtype;
21225450Speter
21325450Speter	if (ioctl(s, SIOCSIFMEDIA, (caddr_t)&ifr) < 0)
21425450Speter		err(1, "SIOCSIFMEDIA");
21525450Speter}
21625450Speter
21725450Spetervoid
21825450Spetersetmediaopt(val, d, s)
21925450Speter	const char *val;
22025450Speter	int d;
22125450Speter	int s;
22225450Speter{
22325450Speter
22425450Speter	domediaopt(val, 0, s);
22525450Speter}
22625450Speter
22725450Spetervoid
22825450Speterunsetmediaopt(val, d, s)
22925450Speter	const char *val;
23025450Speter	int d;
23125450Speter	int s;
23225450Speter{
23325450Speter
23425450Speter	domediaopt(val, 1, s);
23525450Speter}
23625450Speter
23725450Speterstatic void
23825450Speterdomediaopt(val, clear, s)
23925450Speter	const char *val;
24025450Speter	int clear;
24125450Speter	int s;
24225450Speter{
24325450Speter	struct ifmediareq ifmr;
24425450Speter	int *mwords, options;
24525450Speter
24625450Speter	(void) memset(&ifmr, 0, sizeof(ifmr));
24725450Speter	(void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
24825450Speter
24925450Speter	/*
25025450Speter	 * We must go through the motions of reading all
25125450Speter	 * supported media because we need to know both
25225450Speter	 * the current media type and the top-level type.
25325450Speter	 */
25425450Speter
25525450Speter	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0)
25625450Speter		err(1, "SIOCGIFMEDIA");
25725450Speter
25825450Speter	if (ifmr.ifm_count == 0)
25925450Speter		errx(1, "%s: no media types?", name);
26025450Speter
26125450Speter	mwords = (int *)malloc(ifmr.ifm_count * sizeof(int));
26225450Speter	if (mwords == NULL)
26325450Speter		err(1, "malloc");
26425450Speter
26525450Speter	ifmr.ifm_ulist = mwords;
26625450Speter	if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0)
26725450Speter		err(1, "SIOCGIFMEDIA");
26825450Speter
26925450Speter	options = get_media_options(IFM_TYPE(mwords[0]), val);
27025450Speter
27125450Speter	free(mwords);
27225450Speter
27325450Speter	strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
27425450Speter	ifr.ifr_media = ifmr.ifm_current;
27525450Speter	if (clear)
27625450Speter		ifr.ifr_media &= ~options;
27725450Speter	else
27825450Speter		ifr.ifr_media |= options;
27925450Speter
28025450Speter	if (ioctl(s, SIOCSIFMEDIA, (caddr_t)&ifr) < 0)
28125450Speter		err(1, "SIOCSIFMEDIA");
28225450Speter}
28325450Speter
28425450Speter/**********************************************************************
28525450Speter * A good chunk of this is duplicated from sys/net/ifmedia.c
28625450Speter **********************************************************************/
28725450Speter
28825450Speterstatic struct ifmedia_description ifm_type_descriptions[] =
28925450Speter    IFM_TYPE_DESCRIPTIONS;
29025450Speter
29125450Speterstatic struct ifmedia_description ifm_subtype_ethernet_descriptions[] =
29225450Speter    IFM_SUBTYPE_ETHERNET_DESCRIPTIONS;
29325450Speter
29425450Speterstatic struct ifmedia_description ifm_subtype_ethernet_aliases[] =
29525450Speter    IFM_SUBTYPE_ETHERNET_ALIASES;
29625450Speter
29725450Speterstatic struct ifmedia_description ifm_subtype_ethernet_option_descriptions[] =
29825450Speter    IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS;
29925450Speter
30025450Speterstatic struct ifmedia_description ifm_subtype_tokenring_descriptions[] =
30125450Speter    IFM_SUBTYPE_TOKENRING_DESCRIPTIONS;
30225450Speter
30325450Speterstatic struct ifmedia_description ifm_subtype_tokenring_aliases[] =
30425450Speter    IFM_SUBTYPE_TOKENRING_ALIASES;
30525450Speter
30625450Speterstatic struct ifmedia_description ifm_subtype_tokenring_option_descriptions[] =
30725450Speter    IFM_SUBTYPE_TOKENRING_OPTION_DESCRIPTIONS;
30825450Speter
30925450Speterstatic struct ifmedia_description ifm_subtype_fddi_descriptions[] =
31025450Speter    IFM_SUBTYPE_FDDI_DESCRIPTIONS;
31125450Speter
31225450Speterstatic struct ifmedia_description ifm_subtype_fddi_aliases[] =
31325450Speter    IFM_SUBTYPE_FDDI_ALIASES;
31425450Speter
31525450Speterstatic struct ifmedia_description ifm_subtype_fddi_option_descriptions[] =
31625450Speter    IFM_SUBTYPE_FDDI_OPTION_DESCRIPTIONS;
31725450Speter
31825450Speterstatic struct ifmedia_description ifm_subtype_shared_descriptions[] =
31925450Speter    IFM_SUBTYPE_SHARED_DESCRIPTIONS;
32025450Speter
32125450Speterstatic struct ifmedia_description ifm_subtype_shared_aliases[] =
32225450Speter    IFM_SUBTYPE_SHARED_ALIASES;
32325450Speter
32425450Speterstatic struct ifmedia_description ifm_shared_option_descriptions[] =
32525450Speter    IFM_SHARED_OPTION_DESCRIPTIONS;
32625450Speter
32725450Speterstruct ifmedia_type_to_subtype {
32825450Speter	struct {
32925450Speter		struct ifmedia_description *desc;
33025450Speter		int alias;
33125450Speter	} subtypes[5];
33225450Speter	struct {
33325450Speter		struct ifmedia_description *desc;
33425450Speter		int alias;
33525450Speter	} options[3];
33625450Speter};
33725450Speter
33825450Speter/* must be in the same order as IFM_TYPE_DESCRIPTIONS */
33925450Speterstatic struct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = {
34025450Speter	{
34125450Speter		{
34225450Speter			{ &ifm_subtype_shared_descriptions[0], 0 },
34325450Speter			{ &ifm_subtype_shared_aliases[0], 1 },
34425450Speter			{ &ifm_subtype_ethernet_descriptions[0], 0 },
34525450Speter			{ &ifm_subtype_ethernet_aliases[0], 1 },
34625450Speter			{ NULL, 0 },
34725450Speter		},
34825450Speter		{
34925450Speter			{ &ifm_shared_option_descriptions[0], 0 },
35025450Speter			{ &ifm_subtype_ethernet_option_descriptions[0], 1 },
35125450Speter			{ NULL, 0 },
35225450Speter		},
35325450Speter	},
35425450Speter	{
35525450Speter		{
35625450Speter			{ &ifm_subtype_shared_descriptions[0], 0 },
35725450Speter			{ &ifm_subtype_shared_aliases[0], 1 },
35825450Speter			{ &ifm_subtype_tokenring_descriptions[0], 0 },
35925450Speter			{ &ifm_subtype_tokenring_aliases[0], 1 },
36025450Speter			{ NULL, 0 },
36125450Speter		},
36225450Speter		{
36325450Speter			{ &ifm_shared_option_descriptions[0], 0 },
36425450Speter			{ &ifm_subtype_tokenring_option_descriptions[0], 1 },
36525450Speter			{ NULL, 0 },
36625450Speter		},
36725450Speter	},
36825450Speter	{
36925450Speter		{
37025450Speter			{ &ifm_subtype_shared_descriptions[0], 0 },
37125450Speter			{ &ifm_subtype_shared_aliases[0], 1 },
37225450Speter			{ &ifm_subtype_fddi_descriptions[0], 0 },
37325450Speter			{ &ifm_subtype_fddi_aliases[0], 1 },
37425450Speter			{ NULL, 0 },
37525450Speter		},
37625450Speter		{
37725450Speter			{ &ifm_shared_option_descriptions[0], 0 },
37825450Speter			{ &ifm_subtype_fddi_option_descriptions[0], 1 },
37925450Speter			{ NULL, 0 },
38025450Speter		},
38125450Speter	},
38225450Speter};
38325450Speter
38425450Speterstatic int
38525450Speterget_media_subtype(type, val)
38625450Speter	int type;
38725450Speter	const char *val;
38825450Speter{
38925450Speter	struct ifmedia_description *desc;
39025450Speter	struct ifmedia_type_to_subtype *ttos;
39125450Speter	int rval, i;
39225450Speter
39325450Speter	/* Find the top-level interface type. */
39425450Speter	for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes;
39525450Speter	    desc->ifmt_string != NULL; desc++, ttos++)
39625450Speter		if (type == desc->ifmt_word)
39725450Speter			break;
39825450Speter	if (desc->ifmt_string == NULL)
39925450Speter		errx(1, "unknown media type 0x%x", type);
40025450Speter
40125450Speter	for (i = 0; ttos->subtypes[i].desc != NULL; i++) {
40225450Speter		rval = lookup_media_word(ttos->subtypes[i].desc, val);
40325450Speter		if (rval != -1)
40425450Speter			return (rval);
40525450Speter	}
40625450Speter	errx(1, "unknown media subtype: %s", val);
40725450Speter	/* NOTREACHED */
40825450Speter}
40925450Speter
41025450Speterstatic int
41125450Speterget_media_options(type, val)
41225450Speter	int type;
41325450Speter	const char *val;
41425450Speter{
41525450Speter	struct ifmedia_description *desc;
41625450Speter	struct ifmedia_type_to_subtype *ttos;
41725450Speter	char *optlist, *optptr;
41825450Speter	int option = 0, i, rval = 0;
41925450Speter
42025450Speter	/* We muck with the string, so copy it. */
42125450Speter	optlist = strdup(val);
42225450Speter	if (optlist == NULL)
42325450Speter		err(1, "strdup");
42425450Speter
42525450Speter	/* Find the top-level interface type. */
42625450Speter	for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes;
42725450Speter	    desc->ifmt_string != NULL; desc++, ttos++)
42825450Speter		if (type == desc->ifmt_word)
42925450Speter			break;
43025450Speter	if (desc->ifmt_string == NULL)
43125450Speter		errx(1, "unknown media type 0x%x", type);
43225450Speter
43325450Speter	/*
43425450Speter	 * Look up the options in the user-provided comma-separated
43525450Speter	 * list.
43625450Speter	 */
43725450Speter	optptr = optlist;
43825450Speter	for (; (optptr = strtok(optptr, ",")) != NULL; optptr = NULL) {
43925450Speter		for (i = 0; ttos->options[i].desc != NULL; i++) {
44025450Speter			option = lookup_media_word(ttos->options[i].desc, optptr);
44125450Speter			if (option != -1)
44225450Speter				break;
44325450Speter		}
44425450Speter		if (option == 0)
44525450Speter			errx(1, "unknown option: %s", optptr);
44625450Speter		rval |= option;
44725450Speter	}
44825450Speter
44925450Speter	free(optlist);
45025450Speter	return (rval);
45125450Speter}
45225450Speter
45325450Speterstatic int
45425450Speterlookup_media_word(desc, val)
45525450Speter	struct ifmedia_description *desc;
45625450Speter	const char *val;
45725450Speter{
45825450Speter
45925450Speter	for (; desc->ifmt_string != NULL; desc++)
46025450Speter		if (strcasecmp(desc->ifmt_string, val) == 0)
46125450Speter			return (desc->ifmt_word);
46225450Speter
46325450Speter	return (-1);
46425450Speter}
46525450Speter
46625450Speterstatic void
46725450Speterprint_media_word(ifmw)
46825450Speter	int ifmw;
46925450Speter{
47025450Speter	struct ifmedia_description *desc;
47125450Speter	struct ifmedia_type_to_subtype *ttos;
47225450Speter	int seen_option = 0, i;
47325450Speter
47425450Speter	/* Find the top-level interface type. */
47525450Speter	for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes;
47625450Speter	    desc->ifmt_string != NULL; desc++, ttos++)
47725450Speter		if (IFM_TYPE(ifmw) == desc->ifmt_word)
47825450Speter			break;
47925450Speter	if (desc->ifmt_string == NULL) {
48025450Speter		printf("<unknown type>");
48125450Speter		return;
48225450Speter	}
48325450Speter
48425450Speter	/*
48525450Speter	 * Don't print the top-level type; it's not like we can
48625450Speter	 * change it, or anything.
48725450Speter	 */
48825450Speter
48925450Speter	/* Find subtype. */
49025450Speter	for (i = 0; ttos->subtypes[i].desc != NULL; i++) {
49125450Speter		if (ttos->subtypes[i].alias)
49225450Speter			continue;
49325450Speter		for (desc = ttos->subtypes[i].desc;
49425450Speter		    desc->ifmt_string != NULL; desc++) {
49525450Speter			if (IFM_SUBTYPE(ifmw) == desc->ifmt_word)
49625450Speter				goto got_subtype;
49725450Speter		}
49825450Speter	}
49925450Speter
50025450Speter	/* Falling to here means unknown subtype. */
50125450Speter	printf("<unknown subtype>");
50225450Speter	return;
50325450Speter
50425450Speter got_subtype:
50525450Speter	printf("%s", desc->ifmt_string);
50625450Speter
50725450Speter	/* Find options. */
50825450Speter	for (i = 0; ttos->options[i].desc != NULL; i++) {
50925450Speter		if (ttos->options[i].alias)
51025450Speter			continue;
51125450Speter		for (desc = ttos->options[i].desc;
51225450Speter		    desc->ifmt_string != NULL; desc++) {
51325450Speter			if (ifmw & desc->ifmt_word) {
51425450Speter				if (seen_option == 0)
51525450Speter					printf(" <");
51625450Speter				printf("%s%s", seen_option++ ? "," : "",
51725450Speter				    desc->ifmt_string);
51825450Speter			}
51925450Speter		}
52025450Speter	}
52125450Speter	printf("%s", seen_option ? ">" : "");
52225450Speter}
52325450Speter
52425450Speter/**********************************************************************
52525450Speter * ...until here.
52625450Speter **********************************************************************/
527