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