Home | History | Annotate | Download | only in cfgadm
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*
     28  * This is the main program file for the configuration administration
     29  * command as set out in manual page cfgadm(1M).  It uses the configuration
     30  * administration library interface, libcfgadm, as set out in manual
     31  * page config_admin(3X).
     32  */
     33 
     34 #include <stdio.h>
     35 #include <stdlib.h>
     36 #include <string.h>
     37 #include <locale.h>
     38 #include <langinfo.h>
     39 #include <time.h>
     40 #include <assert.h>
     41 #include <sys/types.h>
     42 #include <sys/stat.h>
     43 #include <sys/param.h>
     44 #include <sys/sunddi.h>
     45 #include <sys/openpromio.h>
     46 #include <sys/ddi_impldefs.h>
     47 #include <sys/systeminfo.h>
     48 #include <ctype.h>
     49 #include <zone.h>
     50 
     51 #include <config_admin.h>
     52 #include "cfgadm.h"
     53 
     54 #define	S_FREE(x)	(((x) != NULL) ? (free(x), (x) = NULL) : (void *)0)
     55 #define	GET_DYN(a)	(strstr((a), CFGA_DYN_SEP))
     56 /*
     57  * forward declarations
     58  */
     59 static char *basename(char *);
     60 static void cfgadm_error(int, char *);
     61 static int confirm_interactive(void *, const char *);
     62 static int confirm_no(void *, const char *);
     63 static int confirm_yes(void *, const char *);
     64 static void usage(void);
     65 static void usage_field(void);
     66 static int extract_list_suboptions(char *, char **, char **, char **,
     67     int *, char **, char **, char **);
     68 static int message_output(void *appdata_ptr, const char *message);
     69 static void *config_calloc_check(size_t, size_t);
     70 static cfga_ap_types_t find_arg_type(const char *);
     71 static int yesno(char *, char *);
     72 
     73 
     74 static int compare_ap_id(cfga_list_data_t *, cfga_list_data_t *, match_type_t);
     75 static int compare_r_state(cfga_list_data_t *, cfga_list_data_t *,
     76     match_type_t);
     77 static int compare_o_state(cfga_list_data_t *, cfga_list_data_t *,
     78     match_type_t);
     79 static int compare_cond(cfga_list_data_t *, cfga_list_data_t *, match_type_t);
     80 static int compare_time(cfga_list_data_t *, cfga_list_data_t *, match_type_t);
     81 static int compare_info(cfga_list_data_t *, cfga_list_data_t *, match_type_t);
     82 static int compare_type(cfga_list_data_t *, cfga_list_data_t *, match_type_t);
     83 static int compare_busy(cfga_list_data_t *, cfga_list_data_t *, match_type_t);
     84 static int compare_class(cfga_list_data_t *, cfga_list_data_t *, match_type_t);
     85 static int compare_null(cfga_list_data_t *, cfga_list_data_t *, match_type_t);
     86 static void print_log_id(cfga_list_data_t *, int, char *);
     87 static void print_r_state(cfga_list_data_t *, int, char *);
     88 static void print_o_state(cfga_list_data_t *, int, char *);
     89 static void print_cond(cfga_list_data_t *, int, char *);
     90 static void print_time(cfga_list_data_t *, int, char *);
     91 static void print_time_p(cfga_list_data_t *, int, char *);
     92 static void print_info(cfga_list_data_t *, int, char *);
     93 static void print_type(cfga_list_data_t *, int, char *);
     94 static void print_busy(cfga_list_data_t *, int, char *);
     95 static void print_phys_id(cfga_list_data_t *, int, char *);
     96 static void print_class(cfga_list_data_t *, int, char *);
     97 static void print_null(cfga_list_data_t *, int, char *);
     98 static int count_fields(char *, char);
     99 static int process_sort_fields(int, struct sort_el *, char *);
    100 static int process_fields(int, struct print_col *, int, char *);
    101 static cfga_err_t print_fields(int, struct print_col *, int, int, char *,
    102     cfga_list_data_t *, FILE *);
    103 static int ldata_compare(const void *, const void *);
    104 
    105 static void arg_got_resp(ap_arg_t *inp, ap_out_t *out_array, int nouts,
    106     int dyn_exp);
    107 static void out_was_req(ap_out_t *outp, ap_arg_t *in_array, int nargs,
    108     int no_dyn);
    109 static void report_no_response(ap_arg_t *arg_array, int napids_to_list);
    110 
    111 static cfga_err_t set_log_flt(cfga_list_data_t *p, const char *val);
    112 static cfga_err_t set_type_flt(cfga_list_data_t *p, const char *val);
    113 static cfga_err_t set_class_flt(cfga_list_data_t *p, const char *val);
    114 
    115 static char *get_dyn(const char *ap_id);
    116 static void remove_dyn(char *ap_id);
    117 static char *s_strdup(char *str);
    118 
    119 /*
    120  * global data
    121  */
    122 /* command name for messages */
    123 static char *cmdname;
    124 
    125 /*
    126  * control for comparing, printing and filtering cfga_list_data
    127  * NOTE:Field names (i.e. member 0 of field_info struct) may not contain '('.
    128  *	The post filtering code depends on it.
    129  * NOTE:A NULL value for the set_filter member indicates that filtering based
    130  *	on that field is currently not supported.
    131  */
    132 static struct field_info all_fields[] = {
    133 {"ap_id", "Ap_Id", SZ_EL(ap_log_id), compare_ap_id, print_log_id, set_log_flt},
    134 {"r_state", "Receptacle", STATE_WIDTH, compare_r_state, print_r_state, NULL},
    135 {"o_state", "Occupant", STATE_WIDTH, compare_o_state, print_o_state, NULL},
    136 {"condition", "Condition", COND_WIDTH, compare_cond, print_cond, NULL},
    137 {"status_time", "When", TIME_WIDTH, compare_time, print_time, NULL},
    138 {"status_time_p", "When", TIME_P_WIDTH, compare_time, print_time_p, NULL},
    139 {"info", "Information", SZ_EL(ap_info), compare_info, print_info, NULL},
    140 {"type", "Type", SZ_EL(ap_type), compare_type, print_type, set_type_flt},
    141 {"busy", "Busy", 8, compare_busy, print_busy, NULL},
    142 {"physid", "Phys_Id", SZ_EL(ap_phys_id), compare_ap_id, print_phys_id, NULL},
    143 {"class", "Class", SZ_EL(ap_class), compare_class, print_class, set_class_flt}
    144 };
    145 
    146 #define	PREFILT_CLASS_STR	"class="
    147 
    148 typedef struct {
    149 	cfga_list_data_t ldata;			/* Selection criteria */
    150 	match_type_t match_type_p[N_FIELDS];	/* Type of match */
    151 } post_filter_t;
    152 
    153 static struct field_info null_field =
    154 	{"null", "", 0, compare_null, print_null, NULL};
    155 
    156 static struct sort_el *sort_list;	/* Used in ldata_compare() */
    157 static int nsort_list;
    158 static char unk_field[] = "%s: field \"%s\" unknown\n";
    159 
    160 static char aptype_no_dyn[] = "%s: Invalid ap_id: %s\n";
    161 
    162 /* strings that make up the usage message */
    163 static char *usage_tab[] = {
    164 " %s [-f] [-y|-n] [-v] [-o hardware_opts ] -c function ap_id [ap_id...]\n",
    165 " %s [-f] [-y|-n] [-v] [-o hardware_opts ] -x function ap_id [ap_id...]\n",
    166 " %s [-v] [-s listing_options ] [-o hardware_opts ] [-a]\n"
    167 "\t[-l [ap_id|ap_type...]]\n",
    168 " %s [-v] [-o hardware_opts ] -t ap_id [ap_id...]\n",
    169 " %s [-v] [-o hardware_opts ] -h [ap_id|ap_type...]\n",
    170 };
    171 
    172 /* Type of matches currently supported by the select sub-option */
    173 static match_cvt_t match_type_array[] = {
    174 	{"partial", CFGA_MATCH_PARTIAL},
    175 	{"exact", CFGA_MATCH_EXACT}
    176 };
    177 
    178 #define	N_MATCH_TYPES	(sizeof (match_type_array)/sizeof (match_type_array[0]))
    179 
    180 static cfga_err_t setup_filter(const char *selectp, const char *matchp,
    181     post_filter_t *post_filtp, char **prefilt_optpp);
    182 static cfga_err_t parse_select_opt(const char *selectp,
    183     post_filter_t *post_filtp, match_type_t match_type);
    184 static int do_config_list(int, char *[], cfga_list_data_t *, int, char *,
    185     char *, char *, int, char *, post_filter_t *, int);
    186 static void do_post_filter(ap_out_t *outp, post_filter_t *post_filtp, int *jp);
    187 
    188 
    189 /*
    190  * main - the main routine of cfgadm, processes the command line
    191  * and dispatches functions off to libraries.
    192  */
    193 int
    194 main(
    195 	int argc,
    196 	char *argv[])
    197 {
    198 	extern char *optarg;
    199 	extern int optind;
    200 	int c;
    201 	char *subopts;
    202 	char *subvalue;
    203 	char *const *ap_args = NULL;
    204 	cfga_cmd_t sc_opt = NULL;
    205 	struct cfga_confirm confirm;
    206 	struct cfga_msg message;
    207 	int ret = CFGA_ERROR;
    208 	int i;
    209 	char *estrp = NULL;
    210 	cfga_op_t action = CFGA_OP_NONE;
    211 	char *plat_opts = NULL;
    212 	char *act_arg = NULL;
    213 	enum confirm confarg = CONFIRM_DEFAULT;
    214 	char *list_opts = NULL;
    215 	cfga_flags_t flags = 0;
    216 	int arg_error = 0;
    217 	int dyn_exp = 0;
    218 
    219 	estrp = NULL;
    220 	if (argc > 0)
    221 		cmdname = basename(argv[0]);
    222 	else
    223 		cmdname = "cfgadm";
    224 	(void) setlocale(LC_ALL, "");
    225 #if !defined(TEXT_DOMAIN)
    226 #define	TEXT_DOMAIN	"SYS_TEST"
    227 #endif
    228 	(void) textdomain(TEXT_DOMAIN);
    229 
    230 	while ((c = getopt(argc, argv, OPTIONS)) != EOF) {
    231 		static char dup_action[] =
    232 "%s: more than one action specified (-c,-l,-t,-x)\n";
    233 		static char dup_option[] =
    234 "%s: more than one -%c option specified\n";
    235 		switch (c) {
    236 		case 'a':
    237 			if (dyn_exp) {
    238 				arg_error = 1;
    239 				(void) fprintf(stderr, gettext(dup_option),
    240 				    cmdname, c);
    241 			}
    242 			dyn_exp = 1;
    243 			break;
    244 		case 'c':
    245 			if (action != CFGA_OP_NONE) {
    246 				arg_error = 1;
    247 				(void) fprintf(stderr, gettext(dup_action),
    248 				    cmdname);
    249 			}
    250 			action = CFGA_OP_CHANGE_STATE;
    251 			subopts = optarg;
    252 			subvalue = NULL;
    253 			/*
    254 			 * Reject -c suboption if they are unrecognized
    255 			 * or more than one or have a associated value.
    256 			 */
    257 			if ((sc_opt = getsubopt(&subopts, state_opts,
    258 			    &subvalue)) == -1 || *subopts != '\0' ||
    259 			    subvalue != NULL) {
    260 				arg_error = 1;
    261 				break;
    262 			}
    263 			break;
    264 		case 'f':
    265 			if ((flags & CFGA_FLAG_FORCE) != 0) {
    266 				arg_error = 1;
    267 				(void) fprintf(stderr, gettext(dup_option),
    268 				    cmdname, c);
    269 			}
    270 			flags |= CFGA_FLAG_FORCE;
    271 			break;
    272 		case 'h':
    273 			if (action != CFGA_OP_NONE) {
    274 				arg_error = 1;
    275 				(void) fprintf(stderr, gettext(dup_action),
    276 				    cmdname);
    277 			}
    278 			action = CFGA_OP_HELP;
    279 			break;
    280 		case 'l':
    281 			if (action != CFGA_OP_NONE) {
    282 				arg_error = 1;
    283 				(void) fprintf(stderr, gettext(dup_action),
    284 				    cmdname);
    285 			}
    286 			action = CFGA_OP_LIST;
    287 			break;
    288 		case 'n':
    289 			if (confarg != CONFIRM_DEFAULT) {
    290 				arg_error = 1;
    291 				(void) fprintf(stderr, gettext(dup_option),
    292 				    cmdname, c);
    293 			}
    294 			confarg = CONFIRM_NO;
    295 			break;
    296 		case 'o':
    297 			if (plat_opts != NULL) {
    298 				arg_error = 1;
    299 				(void) fprintf(stderr, gettext(dup_option),
    300 				    cmdname, c);
    301 			}
    302 			plat_opts = optarg;
    303 			break;
    304 		case 's':
    305 			if (list_opts != NULL) {
    306 				arg_error = 1;
    307 				(void) fprintf(stderr, gettext(dup_option),
    308 				    cmdname, c);
    309 			}
    310 			list_opts = optarg;
    311 			break;
    312 		case 't':
    313 			if (action != CFGA_OP_NONE) {
    314 				arg_error = 1;
    315 				(void) fprintf(stderr, gettext(dup_action),
    316 				    cmdname);
    317 			}
    318 			action = CFGA_OP_TEST;
    319 			break;
    320 		case 'x':
    321 			if (action != CFGA_OP_NONE) {
    322 				arg_error = 1;
    323 				(void) fprintf(stderr, gettext(dup_action),
    324 				    cmdname);
    325 			}
    326 			action = CFGA_OP_PRIVATE;
    327 			act_arg = optarg;
    328 			break;
    329 		case 'v':
    330 			if ((flags & CFGA_FLAG_VERBOSE) != 0) {
    331 				arg_error = 1;
    332 				(void) fprintf(stderr, gettext(dup_option),
    333 				    cmdname, c);
    334 			}
    335 			flags |= CFGA_FLAG_VERBOSE;
    336 			break;
    337 		case 'y':
    338 			if (confarg != CONFIRM_DEFAULT) {
    339 				arg_error = 1;
    340 				(void) fprintf(stderr, gettext(dup_option),
    341 				    cmdname, c);
    342 			}
    343 			confarg = CONFIRM_YES;
    344 			break;
    345 		case '?':	/* getopts issues message is this case */
    346 		default:	/* catch programming errors */
    347 			arg_error = 1;
    348 			break;
    349 		}
    350 	}
    351 
    352 	/* default action is list */
    353 	if (action == CFGA_OP_NONE)
    354 		action = CFGA_OP_LIST;
    355 
    356 	/* -s and -a option only for list */
    357 	if (action != CFGA_OP_LIST && (list_opts != NULL || dyn_exp)) {
    358 		arg_error = 1;
    359 	}
    360 
    361 	if (arg_error) {
    362 		usage();
    363 		exit(EXIT_ARGERROR);
    364 		/*NOTREACHED*/
    365 	}
    366 
    367 	if (getzoneid() != GLOBAL_ZONEID) {
    368 		cfgadm_error(CFGA_NOTSUPP,
    369 		    gettext("cfgadm can only be run from the global zone"));
    370 		exit(EXIT_NOTSUPP);
    371 	}
    372 
    373 	ap_args = &argv[optind];
    374 
    375 	/*
    376 	 * If neither -n of -y was specified, interactive confirmation
    377 	 * is used.  Check if the program has terminal I/O and
    378 	 * enforce -n if not.
    379 	 */
    380 	(void) memset(&confirm, 0, sizeof (confirm));
    381 	if (action == CFGA_OP_CHANGE_STATE || action == CFGA_OP_PRIVATE) {
    382 		if (confarg == CONFIRM_DEFAULT &&
    383 		    !(isatty(fileno(stdin)) && isatty(fileno(stderr))))
    384 			confarg = CONFIRM_NO;
    385 		switch (confarg) {
    386 		case CONFIRM_DEFAULT:
    387 			confirm.confirm = confirm_interactive;
    388 			break;
    389 		case CONFIRM_NO:
    390 			confirm.confirm = confirm_no;
    391 			break;
    392 		case CONFIRM_YES:
    393 			confirm.confirm = confirm_yes;
    394 			break;
    395 		default:	/* paranoia */
    396 			abort();
    397 			/*NOTREACHED*/
    398 		}
    399 	}
    400 
    401 	/*
    402 	 * set up message output routine
    403 	 */
    404 	message.message_routine = message_output;
    405 
    406 	switch (action) {
    407 	case CFGA_OP_CHANGE_STATE:
    408 		/* Sanity check - requires an argument */
    409 		if ((argc - optind) <= 0) {
    410 			usage();
    411 			break;
    412 		}
    413 		/* Sanity check - args cannot be ap_types */
    414 		for (i = 0; i < (argc - optind); i++) {
    415 			if (find_arg_type(ap_args[i]) == AP_TYPE) {
    416 				usage();
    417 				exit(EXIT_ARGERROR);
    418 				/*NOTREACHED*/
    419 			}
    420 		}
    421 		ret = config_change_state(sc_opt, argc - optind, ap_args,
    422 		    plat_opts, &confirm, &message, &estrp, flags);
    423 		if (ret != CFGA_OK)
    424 			cfgadm_error(ret, estrp);
    425 		break;
    426 	case CFGA_OP_PRIVATE:
    427 		/* Sanity check - requires an argument */
    428 		if ((argc - optind) <= 0) {
    429 			usage();
    430 			break;
    431 		}
    432 		/* Sanity check - args cannot be ap_types */
    433 		for (i = 0; i < (argc - optind); i++) {
    434 			if (find_arg_type(ap_args[i]) == AP_TYPE) {
    435 				usage();
    436 				exit(EXIT_ARGERROR);
    437 				/*NOTREACHED*/
    438 			}
    439 		}
    440 
    441 		ret = config_private_func(act_arg, argc - optind, ap_args,
    442 		    plat_opts, &confirm, &message, &estrp, flags);
    443 
    444 		if (ret != CFGA_OK)
    445 			cfgadm_error(ret, estrp);
    446 		break;
    447 	case CFGA_OP_TEST:
    448 		/* Sanity check - requires an argument */
    449 		if ((argc - optind) <= 0) {
    450 			usage();
    451 			break;
    452 		}
    453 
    454 		if ((flags & ~CFGA_FLAG_VERBOSE) != 0) {
    455 			usage();
    456 			exit(EXIT_ARGERROR);
    457 			/*NOTREACHED*/
    458 		}
    459 
    460 		/* Sanity check - args cannot be ap_types */
    461 		for (i = 0; i < (argc - optind); i++) {
    462 			if (find_arg_type(ap_args[i]) == AP_TYPE) {
    463 				usage();
    464 				exit(EXIT_ARGERROR);
    465 				/*NOTREACHED*/
    466 			}
    467 		}
    468 		ret = config_test(argc - optind, ap_args, plat_opts, &message,
    469 		    &estrp, flags);
    470 		if (ret != CFGA_OK)
    471 			cfgadm_error(ret, estrp);
    472 		break;
    473 	case CFGA_OP_HELP:
    474 
    475 		if ((flags & ~CFGA_FLAG_VERBOSE) != 0) {
    476 			usage();
    477 			exit(EXIT_ARGERROR);
    478 			/*NOTREACHED*/
    479 		}
    480 
    481 		/* always do usage? */
    482 		usage();
    483 		ret = config_help(argc - optind, ap_args, &message, plat_opts,
    484 		    flags);
    485 		if (ret != CFGA_OK)
    486 			cfgadm_error(ret, estrp);
    487 		break;
    488 
    489 	case CFGA_OP_LIST: {
    490 		/*
    491 		 * Note that we leak the strdup strings below (we never free
    492 		 * them). This is ok in this context since cfgadm is
    493 		 * a short lived process that will exit shortly freeing
    494 		 * the memory.
    495 		 */
    496 		cfga_list_data_t *list_array = NULL;
    497 		int nlist = 0;
    498 		char *sort_fields = s_strdup(DEF_SORT_FIELDS);
    499 		char *cols = s_strdup(DEF_COLS);
    500 		char *cols2 = s_strdup(DEF_COLS2);
    501 		int noheadings = 0;
    502 		char *delim = s_strdup(DEF_DELIM);
    503 		int exitcode = EXIT_OK;
    504 		int i;
    505 		int type = 0;
    506 		char *selectp = NULL, *matchp = NULL, *prefilt_optp = NULL;
    507 		post_filter_t *post_filtp = NULL;
    508 
    509 		if ((flags & ~CFGA_FLAG_VERBOSE) != 0) {
    510 			usage();
    511 			exit(EXIT_ARGERROR);
    512 			/*NOTREACHED*/
    513 		}
    514 
    515 		if (flags & CFGA_FLAG_VERBOSE) {
    516 			cols = s_strdup(DEF_COLS_VERBOSE);
    517 			cols2 = s_strdup(DEF_COLS2_VERBOSE);
    518 		}
    519 
    520 		if (list_opts != NULL && !extract_list_suboptions(list_opts,
    521 		    &sort_fields, &cols, &cols2, &noheadings, &delim,
    522 		    &selectp, &matchp)) {
    523 			usage_field();
    524 			exit(EXIT_ARGERROR);
    525 			/*NOTREACHED*/
    526 		}
    527 
    528 		/*
    529 		 * Scan any args and see if there are any ap_types.
    530 		 * If there are we get all attachment point stats and
    531 		 * then filter what gets printed.
    532 		 */
    533 
    534 		type = 0;
    535 		for (i = 0; i < (argc - optind); i++) {
    536 			if (find_arg_type(ap_args[i]) == AP_TYPE) {
    537 				type = 1;
    538 				/* ap_types cannot have dynamic components */
    539 				if (get_dyn(ap_args[i]) != NULL) {
    540 					(void) fprintf(stderr,
    541 					    gettext(aptype_no_dyn),
    542 					    cmdname, ap_args[i]);
    543 					exit(EXIT_ARGERROR);
    544 					/*NOTREACHED*/
    545 				}
    546 				break;
    547 			}
    548 		}
    549 
    550 		/* Setup filter */
    551 		post_filtp = config_calloc_check(1, sizeof (*post_filtp));
    552 		if (post_filtp == NULL) {
    553 			exit(EXIT_OPFAILED);
    554 			/*NOTREACHED*/
    555 		}
    556 		if (setup_filter(selectp, matchp, post_filtp, &prefilt_optp)
    557 		    != CFGA_OK) {
    558 			S_FREE(post_filtp);
    559 			exit(EXIT_ARGERROR);
    560 			/*NOTREACHED*/
    561 		}
    562 
    563 		list_array = NULL;
    564 		exitcode = EXIT_OK;
    565 
    566 		/*
    567 		 * Check for args. No args means find all libs
    568 		 * and call the cfga_list_ext routine with no ap_ids specified.
    569 		 * With args, if any one of the args are ap_types we
    570 		 * again find all attachment points as in the
    571 		 * no-args case above and then select which attachment points
    572 		 * are actually displayed.
    573 		 */
    574 		if (((argc - optind) == 0) || (type == 1)) {
    575 			/*
    576 			 * No args, or atleast 1 ap_type arg
    577 			 */
    578 			ret = config_list_ext(0, NULL, &list_array,
    579 			    &nlist, plat_opts, prefilt_optp, &estrp,
    580 			    dyn_exp ? CFGA_FLAG_LIST_ALL : 0);
    581 		} else {
    582 			/*
    583 			 * If the args are all ap_ids (no ap_types) we call the
    584 			 * cfga_list_ext routine with those specific ap_ids.
    585 			 */
    586 			ret = config_list_ext(argc - optind, ap_args,
    587 			    &list_array, &nlist, plat_opts, prefilt_optp,
    588 			    &estrp, dyn_exp ? CFGA_FLAG_LIST_ALL : 0);
    589 		}
    590 
    591 		S_FREE(prefilt_optp);
    592 
    593 		if (ret == CFGA_OK) {
    594 
    595 			if (do_config_list(
    596 			    (argc - optind), &argv[optind], list_array, nlist,
    597 			    sort_fields, cols, cols2, noheadings, delim,
    598 			    post_filtp, dyn_exp) != CFGA_OK) {
    599 				exitcode = EXIT_ARGERROR;
    600 			} else {
    601 				exitcode = EXIT_OK;
    602 			}
    603 
    604 			S_FREE(list_array);
    605 			S_FREE(post_filtp);
    606 
    607 			if (estrp != NULL && *estrp != '\0')
    608 				cfgadm_error(CFGA_NOTSUPP, estrp);
    609 			if (exitcode != EXIT_OK) {
    610 				exit(exitcode);
    611 				/*NOTREACHED*/
    612 			}
    613 		} else {
    614 
    615 			S_FREE(post_filtp);
    616 			cfgadm_error(ret, estrp);
    617 		}
    618 		break;
    619 	}
    620 	default:	/* paranoia */
    621 		abort();
    622 		/*NOTREACHED*/
    623 	}
    624 
    625 	if (ret == CFGA_NOTSUPP) {
    626 		return (EXIT_NOTSUPP);
    627 	} else if (ret != CFGA_OK) {
    628 		return (EXIT_OPFAILED);
    629 	} else {
    630 		return (EXIT_OK);
    631 	}
    632 	/*NOTREACHED*/
    633 }
    634 
    635 /*
    636  * usage - outputs the usage help message.
    637  */
    638 static void
    639 usage(void)
    640 {
    641 	int i;
    642 
    643 	(void) fprintf(stderr, "%s\n", gettext("Usage:"));
    644 	for (i = 0; i < sizeof (usage_tab)/sizeof (usage_tab[0]); i++) {
    645 		(void) fprintf(stderr, gettext(usage_tab[i]), cmdname);
    646 	}
    647 }
    648 
    649 /*
    650  * Emit an error message.
    651  * As a side-effect the hardware specific error message is deallocated
    652  * as described in config_admin(3X).
    653  */
    654 static void
    655 cfgadm_error(int errnum, char *estrp)
    656 {
    657 	const char *ep;
    658 
    659 	ep = config_strerror(errnum);
    660 	if (ep == NULL)
    661 		ep = gettext("configuration administration unknown error");
    662 	if (estrp != NULL && *estrp != '\0') {
    663 		(void) fprintf(stderr, "%s: %s: %s\n", cmdname, ep, estrp);
    664 	} else {
    665 		(void) fprintf(stderr, "%s: %s\n", cmdname, ep);
    666 	}
    667 	if (estrp != NULL)
    668 		free((void *)estrp);
    669 	if (errnum == CFGA_INVAL)
    670 		usage();
    671 }
    672 
    673 /*
    674  * confirm_interactive - prompt user for confirmation
    675  */
    676 static int
    677 confirm_interactive(
    678 	void *appdata_ptr,
    679 	const char *message)
    680 {
    681 	static char yeschr[YESNO_STR_MAX + 2];
    682 	static char nochr[YESNO_STR_MAX + 2];
    683 	static int inited = 0;
    684 	int isyes;
    685 
    686 #ifdef lint
    687 	appdata_ptr = appdata_ptr;
    688 #endif /* lint */
    689 	/*
    690 	 * First time through initialisation.  In the original
    691 	 * version of this command this function is only called once,
    692 	 * but this function is generalized for the future.
    693 	 */
    694 	if (!inited) {
    695 		(void) strncpy(yeschr, nl_langinfo(YESSTR), YESNO_STR_MAX + 1);
    696 		(void) strncpy(nochr, nl_langinfo(NOSTR), YESNO_STR_MAX + 1);
    697 		inited = 1;
    698 	}
    699 
    700 	do {
    701 		(void) fprintf(stderr, "%s (%s/%s)? ", message, yeschr, nochr);
    702 		isyes = yesno(yeschr, nochr);
    703 	} while (isyes == -1);
    704 	return (isyes);
    705 }
    706 
    707 /*
    708  * If any text is input it must sub-string match either yes or no.
    709  * Failure of this match is indicated by return of -1.
    710  * If an empty line is input, this is taken as no.
    711  */
    712 static int
    713 yesno(
    714 	char *yesp,
    715 	char *nop)
    716 {
    717 	int	i, b;
    718 	char	ans[YESNO_STR_MAX + 1];
    719 
    720 	i = 0;
    721 
    722 	/*CONSTCOND*/
    723 	while (1) {
    724 		b = getc(stdin);	/* more explicit that rm.c version */
    725 		if (b == '\n' || b == '\0' || b == EOF) {
    726 			if (i < YESNO_STR_MAX)	/* bug fix to rm.c version */
    727 				ans[i] = 0;
    728 			break;
    729 		}
    730 		if (i < YESNO_STR_MAX)
    731 			ans[i] = b;
    732 		i++;
    733 	}
    734 	if (i >= YESNO_STR_MAX) {
    735 		i = YESNO_STR_MAX;
    736 		ans[YESNO_STR_MAX] = 0;
    737 	}
    738 	/* changes to rm.c version follow */
    739 	if (i == 0)
    740 		return (0);
    741 	if (strncmp(nop, ans, i) == 0)
    742 		return (0);
    743 	if (strncmp(yesp, ans, i) == 0)
    744 		return (1);
    745 	return (-1);
    746 }
    747 
    748 /*ARGSUSED*/
    749 static int
    750 confirm_no(
    751 	void *appdata_ptr,
    752 	const char *message)
    753 {
    754 	return (0);
    755 }
    756 
    757 /*ARGSUSED*/
    758 static int
    759 confirm_yes(
    760 	void *appdata_ptr,
    761 	const char *message)
    762 {
    763 	return (1);
    764 }
    765 
    766 /*
    767  * Find base name of filename.
    768  */
    769 static char *
    770 basename(
    771 	char *cp)
    772 {
    773 	char *sp;
    774 
    775 	if ((sp = strrchr(cp, '/')) != NULL)
    776 		return (sp + 1);
    777 	return (cp);
    778 }
    779 
    780 /*ARGSUSED*/
    781 static int
    782 message_output(
    783 	void *appdata_ptr,
    784 	const char *message)
    785 {
    786 	(void) fprintf(stderr, "%s", message);
    787 	return (CFGA_OK);
    788 
    789 }
    790 
    791 /*
    792  * extract_list_suboptions - process list option string
    793  */
    794 static int
    795 extract_list_suboptions(
    796 	char *arg,
    797 	char **sortpp,
    798 	char **colspp,
    799 	char **cols2pp,
    800 	int *noheadingsp,
    801 	char **delimpp,
    802 	char **selectpp,
    803 	char **matchpp)
    804 {
    805 	char *value = NULL;
    806 	int subopt = 0;
    807 	int err = 0;
    808 
    809 	while (*arg != '\0') {
    810 		static char need_value[] =
    811 "%s: sub-option \"%s\" requires a value\n";
    812 		static char no_value[] =
    813 "%s: sub-option \"%s\" does not take a value\n";
    814 		static char unk_subopt[] =
    815 "%s: sub-option \"%s\" unknown\n";
    816 		char **pptr;
    817 
    818 		subopt = getsubopt(&arg, list_options, &value);
    819 		switch (subopt) {
    820 		case LIST_SORT:
    821 			pptr = sortpp;
    822 			goto valcom;
    823 		case LIST_COLS:
    824 			pptr = colspp;
    825 			goto valcom;
    826 		case LIST_COLS2:
    827 			pptr = cols2pp;
    828 			goto valcom;
    829 		case LIST_SELECT:
    830 			pptr = selectpp;
    831 			goto valcom;
    832 		case LIST_MATCH:
    833 			pptr = matchpp;
    834 			goto valcom;
    835 		case LIST_DELIM:
    836 			pptr = delimpp;
    837 		valcom:
    838 			if (value == NULL) {
    839 				(void) fprintf(stderr, gettext(need_value),
    840 				    cmdname, list_options[subopt]);
    841 				err = 1;
    842 			} else
    843 				*pptr = value;
    844 			break;
    845 		case LIST_NOHEADINGS:
    846 			if (value != NULL) {
    847 				(void) fprintf(stderr, gettext(no_value),
    848 				    cmdname, list_options[subopt]);
    849 				err = 1;
    850 			} else
    851 				*noheadingsp = 1;
    852 			break;
    853 		default:
    854 			(void) fprintf(stderr, gettext(unk_subopt),
    855 			    cmdname, value);
    856 			err = 1;
    857 			break;
    858 		}
    859 	}
    860 	return (err == 0);
    861 }
    862 
    863 static cfga_err_t
    864 setup_prefilter(post_filter_t *post_filtp, char **prefilt_optpp)
    865 {
    866 	size_t len;
    867 	const char *clopt = PREFILT_CLASS_STR;
    868 	int idx;
    869 
    870 
    871 	*prefilt_optpp = NULL;
    872 
    873 	/* Get the index for the "class" field */
    874 	for (idx = 0; idx < N_FIELDS; idx++) {
    875 		if (strcmp(all_fields[idx].name, PREFILT_CLASS_STR) == 0)
    876 			break;
    877 	}
    878 
    879 	/*
    880 	 * Currently pre-filter available only for class fld w/ EXACT match
    881 	 */
    882 	if (idx >= N_FIELDS ||
    883 	    post_filtp->match_type_p[idx] != CFGA_MATCH_EXACT) {
    884 		return (CFGA_OK);
    885 	}
    886 
    887 	len = strlen(clopt) + strlen(post_filtp->ldata.ap_class) + 1;
    888 	if ((*prefilt_optpp = config_calloc_check(1, len)) == NULL) {
    889 		return (CFGA_LIB_ERROR);
    890 	}
    891 
    892 	(void) strcpy(*prefilt_optpp, clopt);
    893 	(void) strcat(*prefilt_optpp, post_filtp->ldata.ap_class);
    894 
    895 	/*
    896 	 * Since it is being pre-filtered, this attribute does not need
    897 	 * post-filtering.
    898 	 */
    899 	post_filtp->match_type_p[idx] = CFGA_MATCH_NOFILTER;
    900 	if (all_fields[idx].set_filter != NULL) {
    901 		(void) all_fields[idx].set_filter(&post_filtp->ldata, "");
    902 	}
    903 
    904 	return (CFGA_OK);
    905 }
    906 
    907 static cfga_err_t
    908 set_attrval(
    909 	const char *attr,
    910 	const char *val,
    911 	post_filter_t *post_filtp,
    912 	match_type_t match_type)
    913 {
    914 	int fld = 0;
    915 	cfga_err_t ret = CFGA_ERROR;
    916 
    917 	for (fld = 0; fld < N_FIELDS; fld++) {
    918 		if (strcmp(attr, all_fields[fld].name) == 0)
    919 			break;
    920 	}
    921 
    922 	/* Valid field or is the select option supported for this field */
    923 	if (fld >= N_FIELDS || all_fields[fld].set_filter == NULL) {
    924 		return (CFGA_ATTR_INVAL);
    925 	}
    926 
    927 	if ((ret = all_fields[fld].set_filter(&post_filtp->ldata, val))
    928 	    == CFGA_OK) {
    929 		post_filtp->match_type_p[fld] = match_type;
    930 	}
    931 
    932 	return (ret);
    933 
    934 }
    935 
    936 static char inval_optarg[] =
    937 	"%s: invalid value \"%s\" for %s suboption.\n";
    938 
    939 /*
    940  * Parses the "select" string and fills in the post_filter structure
    941  */
    942 static cfga_err_t
    943 parse_select_opt(
    944 	const char *selectp,
    945 	post_filter_t *post_filtp,
    946 	match_type_t match_type)
    947 {
    948 	parse_state_t state = CFGA_PSTATE_INIT;
    949 	char *cp = NULL, *optstr = NULL, *attr = NULL, *val = NULL;
    950 	int bal = 0;	/* Tracks balancing */
    951 	char chr;
    952 	cfga_err_t ret;
    953 
    954 
    955 	if (selectp == NULL || post_filtp == NULL) {
    956 		return (CFGA_ERROR);
    957 	}
    958 
    959 	optstr = config_calloc_check(1, strlen(selectp) + 1);
    960 	if (optstr == NULL) {
    961 		return (CFGA_LIB_ERROR);
    962 	}
    963 
    964 	(void) strcpy(optstr, selectp);
    965 
    966 	/* Init */
    967 	ret = CFGA_ATTR_INVAL;
    968 	bal = 0;
    969 	cp = attr = optstr;
    970 	state = CFGA_PSTATE_INIT;
    971 
    972 	for (; *cp != '\0'; cp++) {
    973 		switch (state) {
    974 		case CFGA_PSTATE_INIT:
    975 			if (*cp != LEFT_PAREN)
    976 				break;
    977 			*cp = '\0';
    978 			val = cp + 1;
    979 			bal = 1;
    980 			state = CFGA_PSTATE_ATTR_DONE;
    981 			break;
    982 		case CFGA_PSTATE_ATTR_DONE:
    983 			chr = *cp;
    984 			switch (chr) {
    985 			case LEFT_PAREN:
    986 				bal++;
    987 				break;
    988 			case RIGHT_PAREN:
    989 				bal--;
    990 				if (bal == 0) {
    991 					*cp = '\0';
    992 					state = CFGA_PSTATE_VAL_DONE;
    993 				}
    994 				break;
    995 			}
    996 			break;
    997 		case CFGA_PSTATE_VAL_DONE:
    998 			if (*cp != ':') {
    999 				state = CFGA_PSTATE_ERR;
   1000 				goto out;
   1001 			}
   1002 
   1003 			*cp = '\0';
   1004 			if (set_attrval(attr, val, post_filtp,
   1005 			    match_type) != CFGA_OK) {
   1006 				state = CFGA_PSTATE_ERR;
   1007 				goto out;
   1008 			}
   1009 			state = CFGA_PSTATE_INIT;
   1010 			attr = cp + 1;
   1011 			break;
   1012 		default:
   1013 			state = CFGA_PSTATE_ERR;
   1014 			/* FALLTHROUGH */
   1015 		case CFGA_PSTATE_ERR:
   1016 			goto out;
   1017 		}
   1018 	}
   1019 
   1020 	/*FALLTHRU*/
   1021 out:
   1022 	if (state == CFGA_PSTATE_VAL_DONE) {
   1023 		ret = set_attrval(attr, val, post_filtp, match_type);
   1024 	} else {
   1025 		ret = CFGA_ATTR_INVAL;
   1026 	}
   1027 
   1028 	if (ret != CFGA_OK) {
   1029 		(void) fprintf(stderr, gettext(inval_optarg), cmdname,
   1030 		    selectp, list_options[LIST_SELECT]);
   1031 	}
   1032 
   1033 	S_FREE(optstr);
   1034 	return (ret);
   1035 }
   1036 
   1037 
   1038 
   1039 static cfga_err_t
   1040 setup_filter(
   1041 	const char *selectp,
   1042 	const char *matchp,
   1043 	post_filter_t *post_filtp,
   1044 	char **prefilt_optpp)
   1045 {
   1046 	cfga_err_t ret = CFGA_ERROR;
   1047 	match_type_t match_type = CFGA_MATCH_NOFILTER;
   1048 	int i;
   1049 
   1050 	static char match_needs_select[] =
   1051 	    "%s: %s suboption can only be used with %s suboption.\n";
   1052 
   1053 
   1054 	*prefilt_optpp = NULL;
   1055 
   1056 	/*
   1057 	 * Initial: no filtering.
   1058 	 * CFGA_MATCH_NOFILTER is NOT a valid user input
   1059 	 */
   1060 	for (i = 0; i < N_FIELDS; i++) {
   1061 		post_filtp->match_type_p[i] = CFGA_MATCH_NOFILTER;
   1062 	}
   1063 
   1064 	/* Determine type of match */
   1065 	if (matchp == NULL && selectp == NULL) {
   1066 		/* No filtering */
   1067 		return (CFGA_OK);
   1068 	} else if (matchp == NULL && selectp != NULL) {
   1069 		match_type = CFGA_DEFAULT_MATCH;
   1070 	} else if (matchp != NULL && selectp == NULL) {
   1071 		/* If only match specified, select criteria also needed */
   1072 		(void) fprintf(stderr, gettext(match_needs_select),
   1073 		    cmdname, list_options[LIST_MATCH],
   1074 		    list_options[LIST_SELECT]);
   1075 		return (CFGA_ERROR);
   1076 	} else {
   1077 		for (i = 0; i < N_MATCH_TYPES; i++) {
   1078 			if (strcmp(matchp, match_type_array[i].str) == 0) {
   1079 				match_type = match_type_array[i].type;
   1080 				break;
   1081 			}
   1082 		}
   1083 		if (i >= N_MATCH_TYPES) {
   1084 			(void) fprintf(stderr, gettext(inval_optarg), cmdname,
   1085 			    matchp, list_options[LIST_MATCH]);
   1086 			return (CFGA_ERROR);
   1087 		}
   1088 	}
   1089 
   1090 	if ((ret = parse_select_opt(selectp, post_filtp, match_type))
   1091 	    != CFGA_OK) {
   1092 		return (ret);
   1093 	}
   1094 
   1095 	/* Handle pre-filtering. */
   1096 	if ((ret = setup_prefilter(post_filtp, prefilt_optpp)) != CFGA_OK) {
   1097 		/* Cleanup */
   1098 		for (i = 0; i < N_FIELDS; i++) {
   1099 			post_filtp->match_type_p[i] = CFGA_MATCH_NOFILTER;
   1100 		}
   1101 		return (ret);
   1102 	}
   1103 
   1104 
   1105 	return (CFGA_OK);
   1106 }
   1107 
   1108 /*
   1109  * compare_ap_id - compare two ap_id's
   1110  *
   1111  * For partial matches, argument order is significant. The filtering criterion
   1112  * should be the first argument.
   1113  */
   1114 
   1115 static int
   1116 compare_ap_id(
   1117 	cfga_list_data_t *p1,
   1118 	cfga_list_data_t *p2,
   1119 	match_type_t match_type)
   1120 {
   1121 
   1122 	switch (match_type) {
   1123 		case CFGA_MATCH_NOFILTER:
   1124 			return (0);	/* No filtering. all pass */
   1125 		case CFGA_MATCH_PARTIAL:
   1126 			return (strncmp(p1->ap_log_id, p2->ap_log_id,
   1127 			    strlen(p1->ap_log_id)));
   1128 		case CFGA_MATCH_EXACT:
   1129 			return (strcmp(p1->ap_log_id, p2->ap_log_id));
   1130 		case CFGA_MATCH_ORDER:
   1131 		default:
   1132 			return (config_ap_id_cmp(p1->ap_log_id, p2->ap_log_id));
   1133 	}
   1134 }
   1135 
   1136 /*
   1137  * print_log_id - print logical ap_id
   1138  */
   1139 static void
   1140 print_log_id(
   1141 	cfga_list_data_t *p,
   1142 	int width,
   1143 	char *lp)
   1144 {
   1145 	(void) sprintf(lp, "%-*.*s", width, sizeof (p->ap_log_id),
   1146 	    p->ap_log_id);
   1147 }
   1148 
   1149 /*
   1150  * set_log_flt - Setup filter for logical ap_id
   1151  */
   1152 static cfga_err_t
   1153 set_log_flt(
   1154 	cfga_list_data_t *p,
   1155 	const char *val)
   1156 {
   1157 	if (strlen(val) > sizeof (p->ap_log_id) - 1)
   1158 		return (CFGA_ATTR_INVAL);
   1159 
   1160 	(void) strcpy(p->ap_log_id, val);
   1161 
   1162 	return (CFGA_OK);
   1163 }
   1164 
   1165 /*
   1166  * set_type_flt - Setup filter for type field
   1167  */
   1168 
   1169 static cfga_err_t
   1170 set_type_flt(
   1171 	cfga_list_data_t *p,
   1172 	const char *val)
   1173 {
   1174 	if (strlen(val) > sizeof (p->ap_type) - 1)
   1175 		return (CFGA_ATTR_INVAL);
   1176 
   1177 	(void) strcpy(p->ap_type, val);
   1178 
   1179 	return (CFGA_OK);
   1180 }
   1181 
   1182 /*
   1183  * set_class_flt - Setup filter for class field
   1184  */
   1185 static cfga_err_t
   1186 set_class_flt(
   1187 	cfga_list_data_t *p,
   1188 	const char *val)
   1189 {
   1190 	if (strlen(val) > sizeof (p->ap_class) - 1)
   1191 		return (CFGA_ATTR_INVAL);
   1192 
   1193 	(void) strcpy(p->ap_class, val);
   1194 
   1195 	return (CFGA_OK);
   1196 }
   1197 
   1198 
   1199 /*
   1200  * compare_r_state - compare receptacle state of two ap_id's
   1201  */
   1202 static int
   1203 compare_r_state(
   1204 	cfga_list_data_t *p1,
   1205 	cfga_list_data_t *p2,
   1206 	match_type_t match_type)
   1207 {
   1208 	switch (match_type) {
   1209 	case CFGA_MATCH_NOFILTER:  /* no filtering. pass all */
   1210 		return (0);
   1211 	case CFGA_MATCH_ORDER:
   1212 	default:
   1213 		return (p1->ap_r_state - p2->ap_r_state);
   1214 	}
   1215 }
   1216 
   1217 /*
   1218  * compare_o_state - compare occupant state of two ap_id's
   1219  */
   1220 static int
   1221 compare_o_state(
   1222 	cfga_list_data_t *p1,
   1223 	cfga_list_data_t *p2,
   1224 	match_type_t match_type)
   1225 {
   1226 	switch (match_type) {
   1227 	case CFGA_MATCH_NOFILTER:	/* no filtering. all pass */
   1228 		return (0);
   1229 	case CFGA_MATCH_ORDER:
   1230 	default:
   1231 		return (p1->ap_o_state - p2->ap_o_state);
   1232 	}
   1233 }
   1234 
   1235 /*
   1236  * compare_busy - compare busy field of two ap_id's
   1237  */
   1238 static int
   1239 compare_busy(
   1240 	cfga_list_data_t *p1,
   1241 	cfga_list_data_t *p2,
   1242 	match_type_t match_type)
   1243 {
   1244 
   1245 	switch (match_type) {
   1246 	case CFGA_MATCH_NOFILTER:	/* no filtering. all pass */
   1247 		return (0);
   1248 	case CFGA_MATCH_ORDER:
   1249 	default:
   1250 		return (p1->ap_busy - p2->ap_busy);
   1251 	}
   1252 }
   1253 
   1254 /*
   1255  * print_r_state - print receptacle state
   1256  */
   1257 static void
   1258 print_r_state(
   1259 	cfga_list_data_t *p,
   1260 	int width,
   1261 	char *lp)
   1262 {
   1263 	char *cp;
   1264 
   1265 	switch (p->ap_r_state) {
   1266 	case CFGA_STAT_EMPTY:
   1267 		cp = "empty";
   1268 		break;
   1269 	case CFGA_STAT_CONNECTED:
   1270 		cp = "connected";
   1271 		break;
   1272 	case CFGA_STAT_DISCONNECTED:
   1273 		cp = "disconnected";
   1274 		break;
   1275 	default:
   1276 		cp = "???";
   1277 		break;
   1278 	}
   1279 	(void) sprintf(lp, "%-*s", width, cp);
   1280 }
   1281 
   1282 /*
   1283  * print_o_state - print occupant state
   1284  */
   1285 static void
   1286 print_o_state(
   1287 	cfga_list_data_t *p,
   1288 	int width,
   1289 	char *lp)
   1290 {
   1291 	char *cp;
   1292 
   1293 	switch (p->ap_o_state) {
   1294 	case CFGA_STAT_UNCONFIGURED:
   1295 		cp = "unconfigured";
   1296 		break;
   1297 	case CFGA_STAT_CONFIGURED:
   1298 		cp = "configured";
   1299 		break;
   1300 	default:
   1301 		cp = "???";
   1302 		break;
   1303 	}
   1304 	(void) sprintf(lp, "%-*s", width, cp);
   1305 }
   1306 
   1307 /*
   1308  * compare_cond - compare condition field of two ap_id's
   1309  */
   1310 static int
   1311 compare_cond(
   1312 	cfga_list_data_t *p1,
   1313 	cfga_list_data_t *p2,
   1314 	match_type_t match_type)
   1315 {
   1316 
   1317 	switch (match_type) {
   1318 	case CFGA_MATCH_NOFILTER:
   1319 		return (0);
   1320 	case CFGA_MATCH_ORDER:
   1321 	default:
   1322 		return (p1->ap_cond - p2->ap_cond);
   1323 	}
   1324 }
   1325 
   1326 /*
   1327  * print_cond - print attachment point condition
   1328  */
   1329 static void
   1330 print_cond(
   1331 	cfga_list_data_t *p,
   1332 	int width,
   1333 	char *lp)
   1334 {
   1335 	char *cp;
   1336 
   1337 	switch (p->ap_cond) {
   1338 	case CFGA_COND_UNKNOWN:
   1339 		cp = "unknown";
   1340 		break;
   1341 	case CFGA_COND_UNUSABLE:
   1342 		cp = "unusable";
   1343 		break;
   1344 	case CFGA_COND_FAILING:
   1345 		cp = "failing";
   1346 		break;
   1347 	case CFGA_COND_FAILED:
   1348 		cp = "failed";
   1349 		break;
   1350 	case CFGA_COND_OK:
   1351 		cp = "ok";
   1352 		break;
   1353 	default:
   1354 		cp = "???";
   1355 		break;
   1356 	}
   1357 	(void) sprintf(lp, "%-*s", width, cp);
   1358 }
   1359 
   1360 /*
   1361  * compare_time - compare time field of two ap_id's
   1362  */
   1363 static int
   1364 compare_time(
   1365 	cfga_list_data_t *p1,
   1366 	cfga_list_data_t *p2,
   1367 	match_type_t match_type)
   1368 {
   1369 	switch (match_type) {
   1370 	case CFGA_MATCH_NOFILTER:
   1371 		return (0);
   1372 	case CFGA_MATCH_ORDER:
   1373 	default:
   1374 		return (p1->ap_status_time - p2->ap_status_time);
   1375 	}
   1376 }
   1377 
   1378 
   1379 /*
   1380  * print_time - print time from cfga_list_data.
   1381  * Time print based on ls(1).
   1382  */
   1383 static void
   1384 print_time(
   1385 	cfga_list_data_t *p,
   1386 	int width,
   1387 	char *lp)
   1388 {
   1389 	static time_t   year, now;
   1390 	time_t stime;
   1391 	char	time_buf[50];	/* array to hold day and time */
   1392 
   1393 	if (year == 0) {
   1394 		now = time((long *)NULL);
   1395 		year = now - 6L*30L*24L*60L*60L; /* 6 months ago */
   1396 		now = now + 60;
   1397 	}
   1398 	stime = p->ap_status_time;
   1399 	if (stime == (time_t)-1) {
   1400 		(void) sprintf(lp, "%-*s", width, gettext("unavailable"));
   1401 		return;
   1402 	}
   1403 
   1404 	if ((stime < year) || (stime > now)) {
   1405 		(void) strftime(time_buf, sizeof (time_buf),
   1406 		    dcgettext(NULL, FORMAT1, LC_TIME), localtime(&stime));
   1407 	} else {
   1408 		(void) strftime(time_buf, sizeof (time_buf),
   1409 		    dcgettext(NULL, FORMAT2, LC_TIME), localtime(&stime));
   1410 	}
   1411 	(void) sprintf(lp, "%-*s", width, time_buf);
   1412 }
   1413 
   1414 /*
   1415  * print_time_p - print time from cfga_list_data.
   1416  */
   1417 static void
   1418 print_time_p(
   1419 	cfga_list_data_t *p,
   1420 	int width,
   1421 	char *lp)
   1422 {
   1423 	struct tm *tp;
   1424 	char tstr[TIME_P_WIDTH+1];
   1425 
   1426 	tp = localtime(&p->ap_status_time);
   1427 	(void) sprintf(tstr, "%04d%02d%02d%02d%02d%02d", tp->tm_year + 1900,
   1428 	    tp->tm_mon + 1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec);
   1429 	(void) sprintf(lp, "%-*s", width, tstr);
   1430 }
   1431 
   1432 /*
   1433  * compare_info - compare info from two cfga_list_data structs
   1434  */
   1435 static int
   1436 compare_info(
   1437 	cfga_list_data_t *p1,
   1438 	cfga_list_data_t *p2,
   1439 	match_type_t match_type)
   1440 {
   1441 	switch (match_type) {
   1442 	case CFGA_MATCH_NOFILTER:
   1443 		return (0);
   1444 	case CFGA_MATCH_ORDER:
   1445 	default:
   1446 		return (strncmp(p1->ap_info, p2->ap_info,
   1447 		    sizeof (p2->ap_info)));
   1448 	}
   1449 }
   1450 
   1451 /*
   1452  * print_info - print info from cfga_list_data struct
   1453  */
   1454 static void
   1455 print_info(
   1456 	cfga_list_data_t *p,
   1457 	int width,
   1458 	char *lp)
   1459 {
   1460 	(void) sprintf(lp, "%-*.*s", width, sizeof (p->ap_info), p->ap_info);
   1461 }
   1462 
   1463 /*
   1464  * compare_type - compare type from two cfga_list_data structs
   1465  *
   1466  * For partial matches, argument order is significant. The filtering criterion
   1467  * should be the first argument.
   1468  */
   1469 static int
   1470 compare_type(
   1471 	cfga_list_data_t *p1,
   1472 	cfga_list_data_t *p2,
   1473 	match_type_t match_type)
   1474 {
   1475 	switch (match_type) {
   1476 	case CFGA_MATCH_NOFILTER:
   1477 		return (0);
   1478 	case CFGA_MATCH_PARTIAL:
   1479 		return (strncmp(p1->ap_type, p2->ap_type, strlen(p1->ap_type)));
   1480 	case CFGA_MATCH_EXACT:
   1481 	case CFGA_MATCH_ORDER:
   1482 	default:
   1483 		return (strncmp(p1->ap_type, p2->ap_type,
   1484 		    sizeof (p2->ap_type)));
   1485 	}
   1486 }
   1487 
   1488 /*
   1489  * print_type - print type from cfga_list_data struct
   1490  */
   1491 static void
   1492 print_type(
   1493 	cfga_list_data_t *p,
   1494 	int width,
   1495 	char *lp)
   1496 {
   1497 	(void) sprintf(lp, "%-*.*s", width, sizeof (p->ap_type), p->ap_type);
   1498 }
   1499 
   1500 
   1501 /*
   1502  * compare_class - compare class from two cfga_list_data structs
   1503  *
   1504  * For partial matches, argument order is significant. The filtering criterion
   1505  * should be the first argument.
   1506  */
   1507 static int
   1508 compare_class(
   1509 	cfga_list_data_t *p1,
   1510 	cfga_list_data_t *p2,
   1511 	match_type_t match_type)
   1512 {
   1513 
   1514 	switch (match_type) {
   1515 	case CFGA_MATCH_NOFILTER:
   1516 		return (0);
   1517 	case CFGA_MATCH_PARTIAL:
   1518 		return (strncmp(p1->ap_class, p2->ap_class,
   1519 		    strlen(p1->ap_class)));
   1520 	case CFGA_MATCH_EXACT:
   1521 	case CFGA_MATCH_ORDER:
   1522 	default:
   1523 		return (strncmp(p1->ap_class, p2->ap_class,
   1524 		    sizeof (p2->ap_class)));
   1525 	}
   1526 }
   1527 
   1528 /*
   1529  * print_class - print class from cfga_list_data struct
   1530  */
   1531 static void
   1532 print_class(
   1533 	cfga_list_data_t *p,
   1534 	int width,
   1535 	char *lp)
   1536 {
   1537 	(void) sprintf(lp, "%-*.*s", width, sizeof (p->ap_class), p->ap_class);
   1538 }
   1539 /*
   1540  * print_busy - print busy from cfga_list_data struct
   1541  */
   1542 /* ARGSUSED */
   1543 static void
   1544 print_busy(
   1545 	cfga_list_data_t *p,
   1546 	int width,
   1547 	char *lp)
   1548 {
   1549 	if (p->ap_busy)
   1550 		(void) sprintf(lp, "%-*.*s", width, width, "y");
   1551 	else
   1552 		(void) sprintf(lp, "%-*.*s", width, width, "n");
   1553 }
   1554 
   1555 /*
   1556  * print_phys_id - print physical ap_id
   1557  */
   1558 static void
   1559 print_phys_id(
   1560 	cfga_list_data_t *p,
   1561 	int width,
   1562 	char *lp)
   1563 {
   1564 	(void) sprintf(lp, "%-*.*s", width, sizeof (p->ap_phys_id),
   1565 	    p->ap_phys_id);
   1566 }
   1567 
   1568 
   1569 /*
   1570  * find_field - find the named field
   1571  */
   1572 static struct field_info *
   1573 find_field(char *fname)
   1574 {
   1575 	struct field_info *fldp;
   1576 
   1577 	for (fldp = all_fields; fldp < &all_fields[N_FIELDS]; fldp++)
   1578 		if (strcmp(fname, fldp->name) == 0)
   1579 			return (fldp);
   1580 	return (NULL);
   1581 }
   1582 
   1583 /*
   1584  * usage_field - print field usage
   1585  */
   1586 static void
   1587 usage_field()
   1588 {
   1589 	struct field_info *fldp = NULL;
   1590 	const char *sep;
   1591 	static char field_list[] = "%s: print or sort fields must be one of:";
   1592 
   1593 	(void) fprintf(stderr, gettext(field_list), cmdname);
   1594 	sep = "";
   1595 
   1596 	for (fldp = all_fields; fldp < &all_fields[N_FIELDS]; fldp++) {
   1597 		(void) fprintf(stderr, "%s %s", sep, fldp->name);
   1598 		sep = ",";
   1599 	}
   1600 	(void) fprintf(stderr, "\n");
   1601 }
   1602 
   1603 /*
   1604  * compare_null - null comparison routine
   1605  */
   1606 /*ARGSUSED*/
   1607 static int
   1608 compare_null(
   1609 	cfga_list_data_t *p1,
   1610 	cfga_list_data_t *p2,
   1611 	match_type_t match_type)
   1612 {
   1613 	return (0);
   1614 }
   1615 
   1616 /*
   1617  * print_null - print out a field of spaces
   1618  */
   1619 /*ARGSUSED*/
   1620 static void
   1621 print_null(
   1622 	cfga_list_data_t *p,
   1623 	int width,
   1624 	char *lp)
   1625 {
   1626 	(void) sprintf(lp, "%-*s", width, "");
   1627 }
   1628 
   1629 /*
   1630  * do_config_list - directs the output of the listing functions
   1631  */
   1632 static int
   1633 do_config_list(
   1634 	int l_argc,
   1635 	char *l_argv[],
   1636 	cfga_list_data_t *statlist,
   1637 	int nlist,
   1638 	char *sortp,
   1639 	char *colsp,
   1640 	char *cols2p,
   1641 	int noheadings,
   1642 	char *delimp,
   1643 	post_filter_t *post_filtp,
   1644 	int dyn_exp)
   1645 {
   1646 	int nprcols = 0, ncols2 = 0;
   1647 	struct print_col *prnt_list = NULL;
   1648 	int napids_to_list = 0;
   1649 	FILE *fp = NULL;
   1650 	int f_err;
   1651 	cfga_list_data_t **sel_boards = NULL;
   1652 	int nsel = 0;
   1653 	int i, j;
   1654 	cfga_err_t ret;
   1655 
   1656 	ap_arg_t *arg_array = NULL;
   1657 	ap_out_t *out_array = NULL;
   1658 
   1659 
   1660 	sort_list = NULL;
   1661 	f_err = 0;
   1662 	fp = stdout;
   1663 	nsort_list = count_fields(sortp, FDELIM);
   1664 	if (nsort_list != 0) {
   1665 		sort_list = config_calloc_check(nsort_list,
   1666 		    sizeof (*sort_list));
   1667 		if (sort_list == NULL) {
   1668 			ret = CFGA_LIB_ERROR;
   1669 			goto out;
   1670 		}
   1671 		f_err |= process_sort_fields(nsort_list, sort_list, sortp);
   1672 	} else
   1673 		sort_list = NULL;
   1674 
   1675 	nprcols = count_fields(colsp, FDELIM);
   1676 	if ((ncols2 = count_fields(cols2p, FDELIM)) > nprcols)
   1677 		nprcols = ncols2;
   1678 	if (nprcols != 0) {
   1679 		prnt_list = config_calloc_check(nprcols, sizeof (*prnt_list));
   1680 		if (prnt_list == NULL) {
   1681 			ret = CFGA_LIB_ERROR;
   1682 			goto out;
   1683 		}
   1684 		f_err |= process_fields(nprcols, prnt_list, 0, colsp);
   1685 		if (ncols2 != 0)
   1686 			f_err |= process_fields(nprcols, prnt_list, 1, cols2p);
   1687 	} else
   1688 		prnt_list = NULL;
   1689 
   1690 	if (f_err) {
   1691 		usage_field();
   1692 		ret = CFGA_ERROR;
   1693 		goto out;
   1694 	}
   1695 
   1696 	/* Create an array of all user args (if any) */
   1697 	if (l_argc != 0) {
   1698 		int i, j;
   1699 
   1700 		napids_to_list = 0;
   1701 
   1702 		for (i = 0; i < l_argc; i++) {
   1703 			napids_to_list += count_fields(l_argv[i], ARG_DELIM);
   1704 		}
   1705 
   1706 		arg_array = config_calloc_check(napids_to_list,
   1707 		    sizeof (*arg_array));
   1708 		if (arg_array == NULL) {
   1709 			ret = CFGA_LIB_ERROR;
   1710 			goto out;
   1711 		}
   1712 
   1713 		for (i = 0, j = 0; i < l_argc; i++) {
   1714 			int n;
   1715 
   1716 			n = count_fields(l_argv[i], ARG_DELIM);
   1717 			if (n == 0) {
   1718 				continue;
   1719 			} else if (n == 1) {
   1720 				arg_array[j].arg = l_argv[i];
   1721 				arg_array[j].resp = 0;
   1722 				j++;
   1723 			} else {
   1724 				char *cp, *ncp;
   1725 
   1726 				cp = l_argv[i];
   1727 				for (;;) {
   1728 					arg_array[j].arg = cp;
   1729 					arg_array[j].resp = 0;
   1730 					j++;
   1731 					ncp = strchr(cp, ARG_DELIM);
   1732 					if (ncp == NULL)
   1733 						break;
   1734 					*ncp = '\0';
   1735 					cp = ncp + 1;
   1736 				}
   1737 			}
   1738 		}
   1739 		assert(j == napids_to_list);
   1740 	} else {
   1741 		napids_to_list = 0;
   1742 		arg_array = NULL;
   1743 	}
   1744 
   1745 	assert(nlist != 0);
   1746 
   1747 	out_array = config_calloc_check(nlist, sizeof (*out_array));
   1748 	if (out_array == NULL) {
   1749 		ret = CFGA_LIB_ERROR;
   1750 		goto out;
   1751 	}
   1752 
   1753 
   1754 	/* create a list of output stat data */
   1755 	for (i = 0; i < nlist; i++) {
   1756 		out_array[i].ldatap = &statlist[i];
   1757 		out_array[i].req = 0;
   1758 	}
   1759 
   1760 	/*
   1761 	 * Mark all user input which got atleast 1 stat data in response
   1762 	 */
   1763 	for (i = 0; i < napids_to_list; i++) {
   1764 		arg_got_resp(&arg_array[i], out_array, nlist, dyn_exp);
   1765 	}
   1766 
   1767 	/*
   1768 	 * Process output data
   1769 	 */
   1770 	nsel = 0;
   1771 	for (i = 0; i < nlist; i++) {
   1772 		/*
   1773 		 * Mark all the stats which were actually requested by user
   1774 		 */
   1775 		out_was_req(&out_array[i], arg_array, napids_to_list, 0);
   1776 		if (out_array[i].req == 0 && dyn_exp) {
   1777 			/*
   1778 			 * Try again without the dynamic component for the
   1779 			 * if dynamic expansion was requested.
   1780 			 */
   1781 			out_was_req(&out_array[i], arg_array,
   1782 			    napids_to_list, 1);
   1783 		}
   1784 
   1785 		/*
   1786 		 * post filter data which was actually requested
   1787 		 */
   1788 		if (out_array[i].req == 1) {
   1789 			do_post_filter(&out_array[i], post_filtp, &nsel);
   1790 		}
   1791 	}
   1792 
   1793 	sel_boards = config_calloc_check(nsel, sizeof (*sel_boards));
   1794 	if (sel_boards == NULL) {
   1795 		ret = CFGA_LIB_ERROR;
   1796 		goto out;
   1797 	}
   1798 
   1799 	for (i = 0, j = 0; i < nlist; i++) {
   1800 		if (out_array[i].req == 1) {
   1801 			sel_boards[j] = out_array[i].ldatap;
   1802 			j++;
   1803 		}
   1804 	}
   1805 
   1806 	assert(j == nsel);
   1807 
   1808 	/*
   1809 	 * Print headings even if no list entries - Bug or feature ?
   1810 	 */
   1811 	if (!noheadings && prnt_list != NULL) {
   1812 		if ((ret = print_fields(nprcols, prnt_list, 1, 0,
   1813 		    delimp, NULL, fp)) != CFGA_OK) {
   1814 			goto out;
   1815 		}
   1816 		if (ncols2 != 0) {
   1817 			if ((ret = print_fields(nprcols, prnt_list, 1,
   1818 			    1, delimp, NULL, fp)) != CFGA_OK) {
   1819 				goto out;
   1820 			}
   1821 		}
   1822 	}
   1823 
   1824 	if (nsel != 0) {
   1825 		if (sort_list != NULL && nsel > 1) {
   1826 			qsort(sel_boards, nsel, sizeof (sel_boards[0]),
   1827 			    ldata_compare);
   1828 		}
   1829 
   1830 		if (prnt_list != NULL) {
   1831 			for (i = 0; i < nsel; i++) {
   1832 				if ((ret = print_fields(nprcols,
   1833 				    prnt_list, 0, 0, delimp, sel_boards[i], fp))
   1834 				    != CFGA_OK)
   1835 					goto out;
   1836 				if (ncols2 != 0) {
   1837 					if ((ret = print_fields(
   1838 					    nprcols, prnt_list, 0, 1, delimp,
   1839 					    sel_boards[i], fp)) != CFGA_OK)
   1840 						goto out;
   1841 				}
   1842 			}
   1843 		}
   1844 	}
   1845 	/*
   1846 	 * Go thru the argument list and notify user about args
   1847 	 * which did not have a match
   1848 	 */
   1849 	report_no_response(arg_array, napids_to_list);
   1850 	ret = CFGA_OK;
   1851 	/*FALLTHRU*/
   1852 out:
   1853 	S_FREE(sel_boards);
   1854 	S_FREE(arg_array);
   1855 	S_FREE(out_array);
   1856 
   1857 	S_FREE(sort_list);
   1858 	S_FREE(prnt_list);
   1859 
   1860 	return (ret);
   1861 }
   1862 
   1863 
   1864 /*
   1865  * Mark all user inputs which got a response
   1866  */
   1867 static void
   1868 arg_got_resp(ap_arg_t *inp, ap_out_t *out_array, int nouts, int dyn_exp)
   1869 {
   1870 	int i;
   1871 	cfga_ap_types_t type;
   1872 
   1873 
   1874 	if (nouts == 0) {
   1875 		return;
   1876 	}
   1877 
   1878 	type = find_arg_type(inp->arg);
   1879 
   1880 	/*
   1881 	 * Go through list of output stats and check if argument
   1882 	 * produced that output
   1883 	 */
   1884 	for (i = 0; i < nouts; i++) {
   1885 		if (type == PHYSICAL_AP_ID) {
   1886 			if (config_ap_id_cmp(out_array[i].ldatap->ap_phys_id,
   1887 			    inp->arg) == 0) {
   1888 				break;
   1889 			}
   1890 		} else if (type == LOGICAL_AP_ID) {
   1891 			if (config_ap_id_cmp(out_array[i].ldatap->ap_log_id,
   1892 			    inp->arg) == 0) {
   1893 				break;
   1894 			}
   1895 		} else if (type == AP_TYPE) {
   1896 			/*
   1897 			 * An AP_TYPE argument cannot generate dynamic
   1898 			 * attachment point stats unless dynamic expansion was
   1899 			 * requested by user.
   1900 			 */
   1901 			if (!dyn_exp && get_dyn(out_array[i].ldatap->ap_log_id)
   1902 			    != NULL) {
   1903 				continue;
   1904 			}
   1905 
   1906 			if (strncmp(out_array[i].ldatap->ap_log_id, inp->arg,
   1907 			    strlen(inp->arg)) == 0) {
   1908 				break;
   1909 			}
   1910 		} else {
   1911 			return;
   1912 		}
   1913 	}
   1914 
   1915 	if (i < nouts) {
   1916 		inp->resp = 1;
   1917 	}
   1918 }
   1919 
   1920 /* Mark all stat data which were requested by user */
   1921 static void
   1922 out_was_req(ap_out_t *outp, ap_arg_t *in_array, int nargs, int no_dyn)
   1923 {
   1924 	int i;
   1925 	cfga_ap_types_t type = UNKNOWN_AP;
   1926 	char physid[MAXPATHLEN], logid[MAXPATHLEN];
   1927 
   1928 
   1929 	/* If no user args, all output is acceptable */
   1930 	if (nargs == 0) {
   1931 		outp->req = 1;
   1932 		return;
   1933 	}
   1934 
   1935 
   1936 	(void) snprintf(physid, sizeof (physid), "%s",
   1937 	    outp->ldatap->ap_phys_id);
   1938 	(void) snprintf(logid, sizeof (logid), "%s", outp->ldatap->ap_log_id);
   1939 
   1940 	/*
   1941 	 * Do comparison with or without dynamic component as requested by
   1942 	 * user.
   1943 	 */
   1944 	if (no_dyn) {
   1945 		/* Remove the dynamic component */
   1946 		remove_dyn(physid);
   1947 		remove_dyn(logid);
   1948 	}
   1949 
   1950 	for (i = 0; i < nargs; i++) {
   1951 		type = find_arg_type(in_array[i].arg);
   1952 		if (type == PHYSICAL_AP_ID) {
   1953 
   1954 			if (config_ap_id_cmp(in_array[i].arg, physid) == 0) {
   1955 				break;
   1956 			}
   1957 		} else if (type == LOGICAL_AP_ID) {
   1958 
   1959 			if (config_ap_id_cmp(in_array[i].arg, logid) == 0) {
   1960 				break;
   1961 			}
   1962 		} else if (type == AP_TYPE) {
   1963 			/*
   1964 			 * Aptypes cannot generate dynamic attachment
   1965 			 * points unless dynamic expansion is specified.
   1966 			 * in which case this routine would be called a
   1967 			 * 2nd time with the no_dyn flag set and there
   1968 			 * would be no dynamic ap_ids.
   1969 			 */
   1970 			if (get_dyn(logid) != NULL) {
   1971 				continue;
   1972 			}
   1973 
   1974 			if (strncmp(in_array[i].arg, logid,
   1975 			    strlen(in_array[i].arg)) == 0) {
   1976 				break;
   1977 			}
   1978 		} else {
   1979 			continue;
   1980 		}
   1981 	}
   1982 
   1983 	if (i < nargs) {
   1984 		/* Ok, this output was requested */
   1985 		outp->req = 1;
   1986 	}
   1987 
   1988 }
   1989 
   1990 static void
   1991 do_post_filter(ap_out_t *outp, post_filter_t *post_filtp, int *nselp)
   1992 {
   1993 	int i;
   1994 
   1995 	if (outp->req != 1) {
   1996 		return;
   1997 	}
   1998 
   1999 	/*
   2000 	 * For fields without filtering (CFGA_MATCH_NOFILTER),
   2001 	 * compare always returns 0 (success)
   2002 	 */
   2003 	for (i = 0; i < N_FIELDS; i++) {
   2004 		/*
   2005 		 * Note: Order is important for partial match (via strncmp).
   2006 		 * The first argument for compare must be the filter.
   2007 		 */
   2008 		if (all_fields[i].compare(&post_filtp->ldata, outp->ldatap,
   2009 		    post_filtp->match_type_p[i])) {
   2010 			outp->req = 0;	/* Blocked by filter */
   2011 			return;
   2012 		}
   2013 	}
   2014 
   2015 	/*
   2016 	 * Passed through filter
   2017 	 */
   2018 	(*nselp)++;
   2019 }
   2020 
   2021 static void
   2022 report_no_response(ap_arg_t *arg_array, int nargs)
   2023 {
   2024 	int i;
   2025 
   2026 	if (nargs == 0) {
   2027 		return;
   2028 	}
   2029 
   2030 
   2031 	/*
   2032 	 * nop if no user arguments
   2033 	 */
   2034 	for (i = 0; i < nargs; i++) {
   2035 		if (arg_array[i].resp == 0) {
   2036 			(void) fprintf(stderr,
   2037 			    gettext("%s: No matching library found\n"),
   2038 			    arg_array[i].arg);
   2039 		}
   2040 	}
   2041 }
   2042 
   2043 /*
   2044  * ldata_compare - compare two attachment point list data structures.
   2045  */
   2046 static int
   2047 ldata_compare(
   2048 	const void *vb1,
   2049 	const void *vb2)
   2050 {
   2051 	int i;
   2052 	int res = -1;
   2053 	cfga_list_data_t *b1, *b2;
   2054 
   2055 
   2056 	b1 = *(cfga_list_data_t **)vb1;
   2057 	b2 = *(cfga_list_data_t **)vb2;
   2058 
   2059 	for (i = 0; i < nsort_list; i++) {
   2060 		res = (*(sort_list[i].fld->compare))(b1, b2, CFGA_MATCH_ORDER);
   2061 		if (res != 0) {
   2062 			if (sort_list[i].reverse)
   2063 				res = -res;
   2064 			break;
   2065 		}
   2066 	}
   2067 
   2068 	return (res);
   2069 }
   2070 
   2071 /*
   2072  * count_fields - Count the number of fields, using supplied delimiter.
   2073  */
   2074 static int
   2075 count_fields(char *fspec, char delim)
   2076 {
   2077 	char *cp = NULL;
   2078 	int n;
   2079 
   2080 	if (fspec == 0 || *fspec == '\0')
   2081 		return (0);
   2082 	n = 1;
   2083 	for (cp = fspec; *cp != '\0'; cp++)
   2084 		if (*cp == delim)
   2085 			n++;
   2086 	return (n);
   2087 }
   2088 
   2089 /*
   2090  * get_field
   2091  * This function is not a re-implementation of strtok().
   2092  * There can be null fields - strtok() eats spans of delimiters.
   2093  */
   2094 static char *
   2095 get_field(char **fspp)
   2096 {
   2097 	char *cp = NULL, *fld;
   2098 
   2099 	fld = *fspp;
   2100 
   2101 	if (fld != NULL && *fld == '\0')
   2102 		fld = NULL;
   2103 
   2104 	if (fld != NULL) {
   2105 		cp = strchr(*fspp, FDELIM);
   2106 		if (cp == NULL) {
   2107 			*fspp = NULL;
   2108 		} else {
   2109 			*cp = '\0';
   2110 			*fspp = cp + 1;
   2111 			if (*fld == '\0')
   2112 				fld = NULL;
   2113 		}
   2114 	}
   2115 	return (fld);
   2116 }
   2117 
   2118 /*
   2119  * process_fields -
   2120  */
   2121 static int
   2122 process_fields(
   2123 	int ncol,
   2124 	struct print_col *list,
   2125 	int line2,
   2126 	char *fmt)
   2127 {
   2128 	struct print_col *pp = NULL;
   2129 	struct field_info *fldp = NULL;
   2130 	char *fmtx;
   2131 	char *fldn;
   2132 	int err;
   2133 
   2134 	err = 0;
   2135 	fmtx = fmt;
   2136 	for (pp = list; pp < &list[ncol]; pp++) {
   2137 		fldn = get_field(&fmtx);
   2138 		fldp = &null_field;
   2139 		if (fldn != NULL) {
   2140 			struct field_info *tfldp;
   2141 
   2142 			tfldp = find_field(fldn);
   2143 			if (tfldp != NULL) {
   2144 				fldp = tfldp;
   2145 			} else {
   2146 				(void) fprintf(stderr, gettext(unk_field),
   2147 				    cmdname, fldn);
   2148 				err = 1;
   2149 			}
   2150 		}
   2151 		if (line2) {
   2152 			pp->line2 = fldp;
   2153 			if (fldp->width > pp->width)
   2154 				pp->width = fldp->width;
   2155 		} else {
   2156 			pp->line1 = fldp;
   2157 			pp->width = fldp->width;
   2158 		}
   2159 	}
   2160 	return (err);
   2161 }
   2162 
   2163 /*
   2164  * process_sort_fields -
   2165  */
   2166 static int
   2167 process_sort_fields(
   2168 	int nsort,
   2169 	struct sort_el *list,
   2170 	char *fmt)
   2171 {
   2172 	int i;
   2173 	int rev;
   2174 	struct field_info *fldp = NULL;
   2175 	char *fmtx;
   2176 	char *fldn;
   2177 	int err;
   2178 
   2179 	err = 0;
   2180 	fmtx = fmt;
   2181 	for (i = 0; i < nsort; i++) {
   2182 		fldn = get_field(&fmtx);
   2183 		fldp = &null_field;
   2184 		rev = 0;
   2185 		if (fldn != NULL) {
   2186 			struct field_info *tfldp = NULL;
   2187 
   2188 			if (*fldn == '-') {
   2189 				rev = 1;
   2190 				fldn++;
   2191 			}
   2192 			tfldp = find_field(fldn);
   2193 			if (tfldp != NULL) {
   2194 				fldp = tfldp;
   2195 			} else {
   2196 				(void) fprintf(stderr, gettext(unk_field),
   2197 				    cmdname, fldn);
   2198 				err = 1;
   2199 			}
   2200 		}
   2201 		list[i].reverse = rev;
   2202 		list[i].fld = fldp;
   2203 	}
   2204 	return (err);
   2205 }
   2206 
   2207 /*
   2208  * print_fields -
   2209  */
   2210 static cfga_err_t
   2211 print_fields(
   2212 	int ncol,
   2213 	struct print_col *list,
   2214 	int heading,
   2215 	int line2,
   2216 	char *delim,
   2217 	cfga_list_data_t *bdp,
   2218 	FILE *fp)
   2219 {
   2220 	char *del = NULL;
   2221 	struct print_col *pp = NULL;
   2222 	struct field_info *fldp = NULL;
   2223 	static char *outline, *end;
   2224 	char *lp;
   2225 
   2226 	if (outline == NULL) {
   2227 		int out_len, delim_len;
   2228 
   2229 		delim_len = strlen(delim);
   2230 		out_len = 0;
   2231 		for (pp = list; pp < &list[ncol]; pp++) {
   2232 			out_len += pp->width;
   2233 			out_len += delim_len;
   2234 		}
   2235 		out_len -= delim_len;
   2236 		outline = config_calloc_check(out_len + 1, 1);
   2237 		if (outline == NULL) {
   2238 			return (CFGA_LIB_ERROR);
   2239 		}
   2240 		end = &outline[out_len + 1];
   2241 	}
   2242 
   2243 	lp = outline;
   2244 	del = "";
   2245 	for (pp = list; pp < &list[ncol]; pp++) {
   2246 		fldp = line2 ? pp->line2 : pp->line1;
   2247 		(void) snprintf(lp, end - lp, "%s", del);
   2248 		lp += strlen(lp);
   2249 		if (heading) {
   2250 			(void) snprintf(lp, end - lp, "%-*s",
   2251 			    fldp->width, fldp->heading);
   2252 		} else {
   2253 			(*fldp->printfn)(bdp, fldp->width, lp);
   2254 		}
   2255 		lp += strlen(lp);
   2256 		del = delim;
   2257 	}
   2258 
   2259 	/*
   2260 	 * Trim trailing spaces
   2261 	 */
   2262 	while (--lp >= outline && *lp == ' ')
   2263 		*lp = '\0';
   2264 	(void) fprintf(fp, "%s\n", outline);
   2265 	return (CFGA_OK);
   2266 }
   2267 
   2268 /*
   2269  * config_calloc_check - perform allocation, check result and
   2270  * set error indicator
   2271  */
   2272 static void *
   2273 config_calloc_check(
   2274 	size_t nelem,
   2275 	size_t elsize)
   2276 {
   2277 	void *p;
   2278 	static char alloc_fail[] =
   2279 "%s: memory allocation failed (%d*%d bytes)\n";
   2280 
   2281 
   2282 	p = calloc(nelem, elsize);
   2283 	if (p == NULL) {
   2284 		(void) fprintf(stderr, gettext(alloc_fail), cmdname,
   2285 		    nelem, elsize);
   2286 	}
   2287 	return (p);
   2288 }
   2289 
   2290 /*
   2291  * find_arg_type - determine if an argument is an ap_id or an ap_type.
   2292  */
   2293 static cfga_ap_types_t
   2294 find_arg_type(const char *ap_id)
   2295 {
   2296 	struct stat sbuf;
   2297 	cfga_ap_types_t type;
   2298 	char *mkr = NULL, *cp;
   2299 	int size_ap = 0, size_mkr = 0, digit = 0, i = 0;
   2300 	char path[MAXPATHLEN];
   2301 	char apbuf[MAXPATHLEN];
   2302 	size_t len;
   2303 
   2304 
   2305 	/*
   2306 	 * sanity checks
   2307 	 */
   2308 	if (ap_id == NULL || *ap_id == '\0') {
   2309 		return (UNKNOWN_AP);
   2310 	}
   2311 
   2312 	/*
   2313 	 * Mask the dynamic component if any
   2314 	 */
   2315 	if ((cp = GET_DYN(ap_id)) != NULL) {
   2316 		len = cp - ap_id;
   2317 	} else {
   2318 		len = strlen(ap_id);
   2319 	}
   2320 
   2321 	if (len >= sizeof (apbuf)) {
   2322 		return (UNKNOWN_AP);
   2323 	}
   2324 
   2325 	(void) strncpy(apbuf, ap_id, len);
   2326 	apbuf[len] = '\0';
   2327 
   2328 	/*
   2329 	 * If it starts with a slash and is stat-able
   2330 	 * its a physical.
   2331 	 */
   2332 	if (*apbuf == '/' && stat(apbuf, &sbuf) == 0) {
   2333 		return (PHYSICAL_AP_ID);
   2334 	}
   2335 
   2336 	/*
   2337 	 * Is this a symlink in CFGA_DEV_DIR ?
   2338 	 */
   2339 	(void) snprintf(path, sizeof (path), "%s/%s", CFGA_DEV_DIR, apbuf);
   2340 
   2341 	if (lstat(path, &sbuf) == 0 && S_ISLNK(sbuf.st_mode) &&
   2342 	    stat(path, &sbuf) == 0) {
   2343 		return (LOGICAL_AP_ID);
   2344 	}
   2345 
   2346 	/*
   2347 	 * Check for ":" which is always present in an ap_id but not maybe
   2348 	 * present or absent in an ap_type.
   2349 	 * We need to check that the characters right before the : are digits
   2350 	 * since an ap_id is of the form <name><instance>:<specific ap name>
   2351 	 */
   2352 	if ((mkr = strchr(apbuf, ':')) == NULL)  {
   2353 		type = AP_TYPE;
   2354 	} else {
   2355 		size_ap = strlen(apbuf);
   2356 		size_mkr = strlen(mkr);
   2357 		mkr = apbuf;
   2358 
   2359 		digit = 0;
   2360 		for (i = size_ap - size_mkr - 1;  i > 0; i--) {
   2361 			if ((int)isdigit(mkr[i])) {
   2362 				digit++;
   2363 				break;
   2364 			}
   2365 		}
   2366 		if (digit == 0) {
   2367 			type = AP_TYPE;
   2368 		} else {
   2369 			type = LOGICAL_AP_ID;
   2370 		}
   2371 	}
   2372 
   2373 	return (type);
   2374 }
   2375 
   2376 
   2377 static char *
   2378 get_dyn(const char *ap_id)
   2379 {
   2380 	if (ap_id == NULL) {
   2381 		return (NULL);
   2382 	}
   2383 
   2384 	return (strstr(ap_id, CFGA_DYN_SEP));
   2385 }
   2386 
   2387 /*
   2388  * removes the dynamic component
   2389  */
   2390 static void
   2391 remove_dyn(char *ap_id)
   2392 {
   2393 	char *cp;
   2394 
   2395 	if (ap_id == NULL) {
   2396 		return;
   2397 	}
   2398 
   2399 	cp = strstr(ap_id, CFGA_DYN_SEP);
   2400 	if (cp != NULL) {
   2401 		*cp = '\0';
   2402 	}
   2403 }
   2404 
   2405 
   2406 static char *
   2407 s_strdup(char *str)
   2408 {
   2409 	char *dup;
   2410 
   2411 	/*
   2412 	 * sometimes NULL strings may be passed in (see DEF_COLS2). This
   2413 	 * is not an error.
   2414 	 */
   2415 	if (str == NULL) {
   2416 		return (NULL);
   2417 	}
   2418 
   2419 	dup = strdup(str);
   2420 	if (dup == NULL) {
   2421 		(void) fprintf(stderr,
   2422 		    "%s \"%s\"\n", gettext("Cannot copy string"), str);
   2423 		return (NULL);
   2424 	}
   2425 
   2426 	return (dup);
   2427 }
   2428