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