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