xref: /illumos-gate/usr/src/lib/print/libpapi-common/common/attribute.c (revision 43b9c05035ac59f7f7a8e7827598db5a15f30ed3)
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 /* $Id: attribute.c 157 2006-04-26 15:07:55Z ktou $ */
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 /*LINTLIBRARY*/
33 
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <stdarg.h>
37 #include <string.h>
38 #include <ctype.h>
39 #include <alloca.h>
40 #include <papi.h>
41 #include <regex.h>
42 
43 static void papiAttributeFree(papi_attribute_t *attribute);
44 
45 static void
46 papiAttributeValueFree(papi_attribute_value_type_t type,
47 		    papi_attribute_value_t *value)
48 {
49 	if (value != NULL) {
50 		switch (type) {
51 		case PAPI_STRING:
52 			if (value->string != NULL)
53 				free(value->string);
54 			break;
55 		case PAPI_COLLECTION:
56 			if (value->collection != NULL) {
57 				int i;
58 
59 				for (i = 0; value->collection[i] != NULL; i++)
60 					papiAttributeFree(value->collection[i]);
61 
62 				free(value->collection);
63 			}
64 			break;
65 		default: /* don't need to free anything extra */
66 			break;
67 		}
68 
69 		free(value);
70 	}
71 }
72 
73 static void
74 papiAttributeValuesFree(papi_attribute_value_type_t type,
75 		    papi_attribute_value_t **values)
76 {
77 	if (values != NULL) {
78 		int i;
79 
80 		for (i = 0; values[i] != NULL; i++)
81 			papiAttributeValueFree(type, values[i]);
82 
83 		free(values);
84 	}
85 }
86 
87 static void
88 papiAttributeFree(papi_attribute_t *attribute)
89 {
90 	if (attribute != NULL) {
91 		if (attribute->name != NULL)
92 			free(attribute->name);
93 		if (attribute->values != NULL)
94 			papiAttributeValuesFree(attribute->type,
95 						attribute->values);
96 			free(attribute);
97 	}
98 }
99 
100 void
101 papiAttributeListFree(papi_attribute_t **list)
102 {
103 	if (list != NULL) {
104 		int i;
105 
106 		for (i = 0; list[i] != NULL; i++)
107 			papiAttributeFree(list[i]);
108 
109 		free(list);
110 	}
111 }
112 
113 static papi_attribute_t **
114 collection_dup(papi_attribute_t **collection)
115 {
116 	papi_attribute_t **result = NULL;
117 
118 	/* allows a NULL collection that is "empty" or "no value" */
119 	if (collection != NULL) {
120 		papi_status_t status = PAPI_OK;
121 		int i;
122 
123 		for (i = 0; ((collection[i] != NULL) && (status == PAPI_OK));
124 		     i++) {
125 			papi_attribute_t *a = collection[i];
126 
127 			status = papiAttributeListAddValue(&result,
128 					PAPI_ATTR_APPEND, a->name, a->type,
129 					NULL);
130 			if ((status == PAPI_OK) && (a->values != NULL)) {
131 				int j;
132 
133 				for (j = 0; ((a->values[j] != NULL) &&
134 					     (status == PAPI_OK)); j++)
135 					status = papiAttributeListAddValue(
136 							&result,
137 							PAPI_ATTR_APPEND,
138 							a->name, a->type,
139 							a->values[j]);
140 			}
141 		}
142 		if (status != PAPI_OK) {
143 			papiAttributeListFree(result);
144 			result = NULL;
145 		}
146 	}
147 
148 	return (result);
149 }
150 
151 static papi_attribute_value_t *
152 papiAttributeValueDup(papi_attribute_value_type_t type,
153 		papi_attribute_value_t *v)
154 {
155 	papi_attribute_value_t *result = NULL;
156 
157 	if ((v != NULL) && ((result = calloc(1, sizeof (*result))) != NULL)) {
158 		switch (type) {
159 		case PAPI_STRING:
160 			if (v->string == NULL) {
161 				free(result);
162 				result = NULL;
163 			} else
164 				result->string = strdup(v->string);
165 			break;
166 		case PAPI_INTEGER:
167 			result->integer = v->integer;
168 			break;
169 		case PAPI_BOOLEAN:
170 			result->boolean = v->boolean;
171 			break;
172 		case PAPI_RANGE:
173 			result->range.lower = v->range.lower;
174 			result->range.upper = v->range.upper;
175 			break;
176 		case PAPI_RESOLUTION:
177 			result->resolution.xres = v->resolution.xres;
178 			result->resolution.yres = v->resolution.yres;
179 			result->resolution.units = v->resolution.units;
180 			break;
181 		case PAPI_DATETIME:
182 			result->datetime = v->datetime;
183 			break;
184 		case PAPI_COLLECTION:
185 			result->collection = collection_dup(v->collection);
186 			break;
187 		case PAPI_METADATA:
188 			result->metadata = v->metadata;
189 			break;
190 		default:	/* unknown type, fail to duplicate */
191 			free(result);
192 			result = NULL;
193 		}
194 	}
195 
196 	return (result);
197 }
198 
199 static papi_attribute_t *
200 papiAttributeAlloc(char *name, papi_attribute_value_type_t type)
201 {
202 	papi_attribute_t *result = NULL;
203 
204 	if ((result = calloc(1, sizeof (*result))) != NULL) {
205 		result->name = strdup(name);
206 		result->type = type;
207 	}
208 
209 	return (result);
210 }
211 
212 static papi_status_t
213 papiAttributeListAppendValue(papi_attribute_value_t ***values,
214 		papi_attribute_value_type_t type,
215 		papi_attribute_value_t *value)
216 {
217 
218 	if (values == NULL)
219 		return (PAPI_BAD_ARGUMENT);
220 
221 	if (value != NULL) {	/* this allows "empty" attributes */
222 		papi_attribute_value_t *tmp = NULL;
223 
224 		if ((tmp = papiAttributeValueDup(type, value)) == NULL)
225 			return (PAPI_TEMPORARY_ERROR);
226 
227 		list_append(values, tmp);
228 	}
229 
230 	return (PAPI_OK);
231 }
232 
233 papi_status_t
234 papiAttributeListAddValue(papi_attribute_t ***list, int flgs,
235 		char *name, papi_attribute_value_type_t type,
236 		papi_attribute_value_t *value)
237 {
238 	papi_status_t result;
239 	int flags = flgs;
240 	papi_attribute_t *attribute = NULL;
241 	papi_attribute_value_t **values = NULL;
242 
243 	if ((list == NULL) || (name == NULL))
244 		return (PAPI_BAD_ARGUMENT);
245 
246 	if ((type == PAPI_RANGE) && (value != NULL) &&
247 	    (value->range.lower > value->range.upper))
248 		return (PAPI_BAD_ARGUMENT);	/* RANGE must have min <= max */
249 
250 	if (flags == 0) /* if it wasn't set, set a default behaviour */
251 		flags = PAPI_ATTR_APPEND;
252 
253 	/* look for an existing one */
254 	attribute = papiAttributeListFind(*list, name);
255 
256 	if (((flags & PAPI_ATTR_EXCL) != 0) && (attribute != NULL))
257 		return (PAPI_CONFLICT); /* EXISTS */
258 
259 	if (((flags & PAPI_ATTR_REPLACE) == 0) && (attribute != NULL) &&
260 	    (attribute->type != type))
261 		return (PAPI_CONFLICT); /* TYPE CONFLICT */
262 
263 	/* if we don't have one, create it and add it to the list */
264 	if ((attribute == NULL) &&
265 	    ((attribute = papiAttributeAlloc(name, type)) != NULL))
266 		list_append(list, attribute);
267 
268 	/* if we don't have one by now, it's most likely an alloc fail */
269 	if (attribute == NULL)
270 		return (PAPI_TEMPORARY_ERROR);
271 
272 	/*
273 	 * if we are replacing, clear any existing values, but don't free
274 	 * until after we have replaced the values, in case we are replacing
275 	 * a collection with a relocated version of the original collection.
276 	 */
277 	if (((flags & PAPI_ATTR_REPLACE) != 0) && (attribute->values != NULL)) {
278 		values = attribute->values;
279 		attribute->values = NULL;
280 	}
281 
282 	attribute->type = type;
283 
284 	result = papiAttributeListAppendValue(&attribute->values, type, value);
285 
286 	/* free old values if we replaced them */
287 	if (values != NULL)
288 		papiAttributeValuesFree(type, values);
289 
290 	return (result);
291 }
292 
293 papi_status_t
294 papiAttributeListAddString(papi_attribute_t ***list, int flags,
295 			char *name, char *string)
296 {
297 	papi_attribute_value_t v;
298 
299 	v.string = (char *)string;
300 	return (papiAttributeListAddValue(list, flags, name, PAPI_STRING, &v));
301 }
302 
303 papi_status_t
304 papiAttributeListAddInteger(papi_attribute_t ***list, int flags,
305 			char *name, int integer)
306 {
307 	papi_attribute_value_t v;
308 
309 	v.integer = integer;
310 	return (papiAttributeListAddValue(list, flags, name, PAPI_INTEGER, &v));
311 }
312 
313 papi_status_t
314 papiAttributeListAddBoolean(papi_attribute_t ***list, int flags,
315 			char *name, char boolean)
316 {
317 	papi_attribute_value_t v;
318 
319 	v.boolean = boolean;
320 	return (papiAttributeListAddValue(list, flags, name, PAPI_BOOLEAN, &v));
321 }
322 
323 papi_status_t
324 papiAttributeListAddRange(papi_attribute_t ***list, int flags,
325 			char *name, int lower, int upper)
326 {
327 	papi_attribute_value_t v;
328 
329 	v.range.lower = lower;
330 	v.range.upper = upper;
331 	return (papiAttributeListAddValue(list, flags, name, PAPI_RANGE, &v));
332 }
333 
334 papi_status_t
335 papiAttributeListAddResolution(papi_attribute_t ***list, int flags,
336 			char *name, int xres, int yres,
337 			papi_resolution_unit_t units)
338 {
339 	papi_attribute_value_t v;
340 
341 	v.resolution.xres = xres;
342 	v.resolution.yres = yres;
343 	v.resolution.units = units;
344 	return (papiAttributeListAddValue(list, flags, name,
345 				PAPI_RESOLUTION, &v));
346 }
347 
348 papi_status_t
349 papiAttributeListAddDatetime(papi_attribute_t ***list, int flags,
350 			char *name, time_t datetime)
351 {
352 	papi_attribute_value_t v;
353 
354 	v.datetime = datetime;
355 	return (papiAttributeListAddValue(list, flags, name,
356 				PAPI_DATETIME, &v));
357 }
358 
359 papi_status_t
360 papiAttributeListAddCollection(papi_attribute_t ***list, int flags,
361 			char *name, papi_attribute_t **collection)
362 {
363 	papi_attribute_value_t v;
364 
365 	v.collection = (papi_attribute_t **)collection;
366 	return (papiAttributeListAddValue(list, flags, name,
367 				PAPI_COLLECTION, &v));
368 }
369 
370 papi_status_t
371 papiAttributeListAddMetadata(papi_attribute_t ***list, int flags,
372 			char *name, papi_metadata_t metadata)
373 {
374 	papi_attribute_value_t v;
375 
376 	v.metadata = metadata;
377 	return (papiAttributeListAddValue(list, flags, name,
378 				PAPI_METADATA, &v));
379 }
380 
381 papi_status_t
382 papiAttributeListDelete(papi_attribute_t ***list, char *name)
383 {
384 	papi_attribute_t *attribute;
385 
386 	if ((list == NULL) || (name == NULL))
387 		return (PAPI_BAD_ARGUMENT);
388 
389 	if ((attribute = papiAttributeListFind(*list, name)) == NULL)
390 		return (PAPI_NOT_FOUND);
391 
392 	list_remove(list, attribute);
393 	papiAttributeFree(attribute);
394 
395 	return (PAPI_OK);
396 }
397 
398 papi_attribute_t *
399 papiAttributeListFind(papi_attribute_t **list, char *name)
400 {
401 	int i;
402 	if ((list == NULL) || (name == NULL))
403 		return (NULL);
404 
405 	for (i = 0; list[i] != NULL; i++)
406 		if (strcasecmp(list[i]->name, name) == 0)
407 			return ((papi_attribute_t *)list[i]);
408 
409 	return (NULL);
410 }
411 
412 papi_attribute_t *
413 papiAttributeListGetNext(papi_attribute_t **list, void **iter)
414 {
415 	papi_attribute_t **tmp, *result;
416 
417 	if ((list == NULL) && (iter == NULL))
418 		return (NULL);
419 
420 	if (*iter == NULL)
421 		*iter = list;
422 
423 	tmp = *iter;
424 	result = *tmp;
425 	*iter = ++tmp;
426 
427 	return (result);
428 }
429 
430 papi_status_t
431 papiAttributeListGetValue(papi_attribute_t **list, void **iter,
432 			char *name, papi_attribute_value_type_t type,
433 			papi_attribute_value_t **value)
434 {
435 	papi_attribute_value_t **tmp;
436 	void *fodder = NULL;
437 
438 	if ((list == NULL) || ((name == NULL) && (iter == NULL)) ||
439 	    (value == NULL))
440 		return (PAPI_BAD_ARGUMENT);
441 
442 	if (iter == NULL)
443 		iter = &fodder;
444 
445 	if ((iter == NULL) || (*iter == NULL)) {
446 		papi_attribute_t *attr = papiAttributeListFind(list, name);
447 
448 		if (attr == NULL)
449 			return (PAPI_NOT_FOUND);
450 
451 		if (attr->type != type)
452 			return (PAPI_NOT_POSSIBLE);
453 
454 		tmp = attr->values;
455 	} else
456 		tmp = *iter;
457 
458 	if (tmp == NULL)
459 		return (PAPI_NOT_FOUND);
460 
461 	*value = *tmp;
462 	*iter =  ++tmp;
463 
464 	if (*value == NULL)
465 		return (PAPI_GONE);
466 
467 	return (PAPI_OK);
468 }
469 
470 papi_status_t
471 papiAttributeListGetString(papi_attribute_t **list, void **iter,
472 			char *name, char **vptr)
473 {
474 	papi_status_t status;
475 	papi_attribute_value_t *value = NULL;
476 
477 	if (vptr == NULL)
478 		return (PAPI_BAD_ARGUMENT);
479 
480 	status = papiAttributeListGetValue(list, iter, name,
481 				PAPI_STRING, &value);
482 	if (status == PAPI_OK)
483 		*vptr = value->string;
484 
485 	return (status);
486 }
487 
488 papi_status_t
489 papiAttributeListGetInteger(papi_attribute_t **list, void **iter,
490 			char *name, int *vptr)
491 {
492 	papi_status_t status;
493 	papi_attribute_value_t *value = NULL;
494 
495 	if (vptr == NULL)
496 		return (PAPI_BAD_ARGUMENT);
497 
498 	status = papiAttributeListGetValue(list, iter, name,
499 				PAPI_INTEGER, &value);
500 	if (status == PAPI_OK)
501 		*vptr = value->integer;
502 
503 	return (status);
504 }
505 
506 papi_status_t
507 papiAttributeListGetBoolean(papi_attribute_t **list, void **iter,
508 			char *name, char *vptr)
509 {
510 	papi_status_t status;
511 	papi_attribute_value_t *value = NULL;
512 
513 	if (vptr == NULL)
514 		return (PAPI_BAD_ARGUMENT);
515 
516 	status = papiAttributeListGetValue(list, iter, name,
517 				PAPI_BOOLEAN, &value);
518 	if (status == PAPI_OK)
519 		*vptr = value->boolean;
520 
521 	return (status);
522 }
523 
524 papi_status_t
525 papiAttributeListGetRange(papi_attribute_t **list, void **iter,
526 			char *name, int *min, int *max)
527 {
528 	papi_status_t status;
529 	papi_attribute_value_t *value = NULL;
530 
531 	if ((min == NULL) || (max == NULL))
532 		return (PAPI_BAD_ARGUMENT);
533 
534 	status = papiAttributeListGetValue(list, iter, name,
535 				PAPI_RANGE, &value);
536 	if (status == PAPI_OK) {
537 		*min = value->range.lower;
538 		*max = value->range.upper;
539 	}
540 
541 	return (status);
542 }
543 
544 papi_status_t
545 papiAttributeListGetResolution(papi_attribute_t **list, void **iter,
546 			char *name, int *x, int *y,
547 			papi_resolution_unit_t *units)
548 {
549 	papi_status_t status;
550 	papi_attribute_value_t *value = NULL;
551 
552 	if ((x == NULL) || (y == NULL) || (units == NULL))
553 		return (PAPI_BAD_ARGUMENT);
554 
555 	status = papiAttributeListGetValue(list, iter, name,
556 				PAPI_RESOLUTION, &value);
557 	if (status == PAPI_OK) {
558 		*x = value->resolution.xres;
559 		*y = value->resolution.yres;
560 		*units = value->resolution.units;
561 	}
562 
563 	return (status);
564 }
565 
566 papi_status_t
567 papiAttributeListGetDatetime(papi_attribute_t **list, void **iter,
568 			char *name, time_t *dt)
569 {
570 	papi_status_t status;
571 	papi_attribute_value_t *value = NULL;
572 
573 	if (dt == NULL)
574 		return (PAPI_BAD_ARGUMENT);
575 
576 	status = papiAttributeListGetValue(list, iter, name,
577 				PAPI_DATETIME, &value);
578 	if (status == PAPI_OK) {
579 		*dt = value->datetime;
580 	}
581 
582 	return (status);
583 }
584 
585 papi_status_t
586 papiAttributeListGetCollection(papi_attribute_t **list, void **iter,
587 			char *name, papi_attribute_t ***collection)
588 {
589 	papi_status_t status;
590 	papi_attribute_value_t *value = NULL;
591 
592 	if (collection == NULL)
593 		return (PAPI_BAD_ARGUMENT);
594 
595 	status = papiAttributeListGetValue(list, iter, name,
596 				PAPI_COLLECTION, &value);
597 	if (status == PAPI_OK) {
598 		*collection = value->collection;
599 	}
600 
601 	return (status);
602 }
603 
604 papi_status_t
605 papiAttributeListGetMetadata(papi_attribute_t **list, void **iter,
606 			char *name, papi_metadata_t *vptr)
607 {
608 	papi_status_t status;
609 	papi_attribute_value_t *value = NULL;
610 
611 	if (vptr == NULL)
612 		return (PAPI_BAD_ARGUMENT);
613 
614 	status = papiAttributeListGetValue(list, iter, name,
615 				PAPI_METADATA, &value);
616 	if (status == PAPI_OK)
617 		*vptr = value->metadata;
618 
619 	return (status);
620 }
621 
622 
623 /* The string is modified by this call */
624 static char *
625 regvalue(regmatch_t match, char *string)
626 {
627 	char *result = NULL;
628 	if (match.rm_so != match.rm_eo) {
629 		result = string + match.rm_so;
630 		*(result + (match.rm_eo - match.rm_so)) = '\0';
631 	}
632 	return (result);
633 }
634 
635 static papi_attribute_value_type_t
636 _process_value(char *string, char ***parts)
637 {
638 	int i;
639 	static struct {
640 		papi_attribute_value_type_t	type;
641 		size_t vals;
642 		char *expression;
643 		int	compiled;
644 		regex_t re;
645 	} types[] = {
646 		{ PAPI_BOOLEAN,	   1, "^(true|false|yes|no)$", 0 },
647 		{ PAPI_COLLECTION, 1, "^\\{(.+)\\}$", 0 },
648 		/* PAPI_DATETIME is unsupported, too much like an integer */
649 		{ PAPI_INTEGER,	   1, "^([+-]{0,1}[[:digit:]]+)$", 0 },
650 		{ PAPI_RANGE,	   3, "^([[:digit:]]+)-([[:digit:]]+)$", 0 },
651 		{ PAPI_RESOLUTION, 4, "^([[:digit:]]+)x([[:digit:]]+)dp(i|c)$",
652 			0 },
653 		NULL
654 	};
655 	regmatch_t matches[4];
656 
657 	for (i = 0; i < 5; i++) {
658 		int j;
659 
660 		if (types[i].compiled == 0) {
661 			(void) regcomp(&(types[i].re), types[i].expression,
662 						REG_EXTENDED|REG_ICASE);
663 			types[i].compiled = 1;
664 		}
665 		if (regexec(&(types[i].re), string, (size_t)types[i].vals,
666 				matches, 0) == REG_NOMATCH)
667 			continue;
668 
669 		for (j = 0 ; j < types[i].vals; j++)
670 			list_append(parts, regvalue(matches[j], string));
671 		return (types[i].type);
672 	}
673 
674 	list_append(parts, string);
675 	return (PAPI_STRING);
676 }
677 
678 static void
679 _add_attribute_value(papi_attribute_value_t ***list,
680 			papi_attribute_value_type_t type,
681 			papi_attribute_value_type_t dtype, char **parts)
682 {
683 	papi_attribute_value_t *value = calloc(1, sizeof (*value));
684 
685 	switch(type) {
686 	case PAPI_STRING:
687 		value->string = strdup(parts[0]);
688 		list_append(list, value);
689 		break;
690 	case PAPI_BOOLEAN:
691 		value->boolean = PAPI_TRUE;
692 		if ((strcasecmp(parts[0], "false") == 0) ||
693 		    (strcasecmp(parts[0], "no") == 0))
694 			value->boolean = PAPI_FALSE;
695 		list_append(list, value);
696 		break;
697 	case PAPI_INTEGER:
698 		value->integer = atoi(parts[0]);
699 		list_append(list, value);
700 		break;
701 	case PAPI_RANGE:
702 		if (dtype == PAPI_INTEGER)
703 			value->range.lower = value->range.upper
704 					 = atoi(parts[0]);
705 		else if (dtype == PAPI_RANGE)  {
706 			value->range.lower = atoi(parts[1]);
707 			value->range.upper = atoi(parts[2]);
708 		}
709 		list_append(list, value);
710 		break;
711 	case PAPI_RESOLUTION:
712 		value->resolution.xres = atoi(parts[1]);
713 		value->resolution.yres = atoi(parts[2]);
714 		if (parts[3][0] == 'i')
715 			value->resolution.units = PAPI_RES_PER_INCH;
716 		else
717 			value->resolution.units = PAPI_RES_PER_CM;
718 		list_append(list, value);
719 		break;
720 	case PAPI_COLLECTION:
721 		papiAttributeListFromString(&(value->collection), 0, parts[0]);
722 		list_append(list, value);
723 		break;
724 	}
725 }
726 
727 static papi_status_t
728 _papiAttributeFromStrings(papi_attribute_t ***list, int flags,
729 			char *key, char **values)
730 {
731 	int i;
732 	papi_status_t result = PAPI_OK;
733 	papi_attribute_t *attr = calloc(1, sizeof (*attr));
734 
735 	/* these are specified in the papi spec as ranges */
736 	char *ranges[] = { "copies-supported", "job-impressions-supported",
737 				"job-k-octets-supported",
738 				"job-media-sheets-supported", "page-ranges",
739 				NULL };
740 
741 	if ((attr == NULL) || ((attr->name = strdup(key)) == NULL))
742 		return (PAPI_TEMPORARY_ERROR);
743 
744 	attr->type = PAPI_METADATA;
745 	/* these are known ranges */
746 	for (i = 0; ranges[i] != NULL; i++)
747 		if (strcasecmp(attr->name, ranges[i]) == 0) {
748 			attr->type = PAPI_RANGE;
749 			break;
750 	}
751 
752 	if (values != NULL) {
753 		papi_attribute_value_t **vals = NULL;
754 
755 		for (i = 0; values[i] != NULL; i++) {
756 			papi_attribute_value_type_t dtype;
757 			char **parts = NULL;
758 
759 			dtype = _process_value(values[i], &parts);
760 			if (attr->type == PAPI_METADATA)
761 				attr->type = dtype;
762 			_add_attribute_value(&vals, attr->type, dtype, parts);
763 			free(parts);
764 		}
765 		attr->values = vals;
766 	}
767 
768 	list_append(list, attr);
769 
770 	return (result);
771 }
772 
773 static papi_status_t
774 _parse_attribute_list(papi_attribute_t ***list, int flags, char *string)
775 {
776 	papi_status_t result = PAPI_OK;
777 	char *ptr;
778 
779 	if ((list == NULL) || (string == NULL))
780 		return (PAPI_BAD_ARGUMENT);
781 
782 	if ((ptr = strdup(string)) == NULL)
783 		return (PAPI_TEMPORARY_ERROR);
784 
785 	while ((*ptr != '\0') && (result == PAPI_OK)) {
786 		char *key, **values = NULL;
787 
788 		/* strip any leading whitespace */
789 		while (isspace(*ptr) != 0)
790 			ptr++;
791 
792 		/* Get the name: name[=value] */
793 		key = ptr;
794 		while ((*ptr != '\0') && (*ptr != '=') && (isspace(*ptr) == 0))
795 			ptr++;
796 
797 		if (*ptr == '=') {
798 			*ptr++ = '\0';
799 
800 			while ((*ptr != '\0') && (isspace(*ptr) == 0)) {
801 				char *value = ptr;
802 
803 				if ((*ptr == '\'') || (*ptr == '"')) {
804 					char q = *ptr++;
805 
806 					/* quoted string value */
807 					while ((*ptr != '\0') && (*ptr != q))
808 						ptr++;
809 					if (*ptr == q)
810 						ptr++;
811 				} else if (*ptr == '{') {
812 					/* collection */
813 					while ((*ptr != '\0') && (*ptr != '}'))
814 						ptr++;
815 					if (*ptr == '}')
816 						ptr++;
817 				} else {
818 					/* value */
819 					while ((*ptr != '\0') &&
820 					       (*ptr != ',') &&
821 					       (isspace(*ptr) == 0))
822 						ptr++;
823 				}
824 				if (*ptr == ',')
825 					*ptr++ = '\0';
826 				list_append(&values, value);
827 			}
828 		} else { /* boolean "[no]key" */
829 			char *value = "true";
830 
831 			if (strncasecmp(key, "no", 2) == 0) {
832 				key += 2;
833 				value = "false";
834 			}
835 			list_append(&values, value);
836 		}
837 		if (*ptr != '\0')
838 			*ptr++ = '\0';
839 
840 		result = _papiAttributeFromStrings(list, flags, key, values);
841 		free(values);
842 	}
843 
844 	return (result);
845 }
846 
847 papi_status_t
848 papiAttributeListFromString(papi_attribute_t ***attrs,
849 		int flags, char *string)
850 {
851 	papi_status_t result = PAPI_OK;
852 
853 	if ((attrs != NULL) && (string != NULL) &&
854 	    ((flags & ~(PAPI_ATTR_APPEND+PAPI_ATTR_REPLACE+PAPI_ATTR_EXCL))
855 			== 0)) {
856 		result = _parse_attribute_list(attrs, flags, string);
857 	} else {
858 		result = PAPI_BAD_ARGUMENT;
859 	}
860 
861 	return (result);
862 }
863 
864 static papi_status_t
865 papiAttributeToString(papi_attribute_t *attribute, char *delim,
866 		char *buffer, size_t buflen)
867 {
868 	papi_attribute_value_t **values = attribute->values;
869 	int rc, i;
870 
871 	if ((attribute->type == PAPI_BOOLEAN) && (values[1] == NULL)) {
872 		if (values[0]->boolean == PAPI_FALSE) {
873 			if (isupper(attribute->name[0]) == 0)
874 				strlcat(buffer, "no", buflen);
875 			else
876 				strlcat(buffer, "No", buflen);
877 		}
878 		rc = strlcat(buffer, attribute->name, buflen);
879 	} else {
880 		strlcat(buffer, attribute->name, buflen);
881 		rc = strlcat(buffer, "=", buflen);
882 	}
883 
884 	if (values == NULL)
885 		return (PAPI_OK);
886 
887 	for (i = 0; values[i] != NULL; i++) {
888 		switch (attribute->type) {
889 		case PAPI_STRING:
890 			rc = strlcat(buffer, values[i]->string, buflen);
891 			break;
892 		case PAPI_INTEGER: {
893 			char string[24];
894 
895 			snprintf(string, sizeof (string), "%d",
896 				values[i]->integer);
897 			rc = strlcat(buffer, string, buflen);
898 			}
899 			break;
900 		case PAPI_BOOLEAN:
901 			if (values[1] != NULL)
902 				rc = strlcat(buffer, (values[i]->boolean ?
903 						"true" : "false"), buflen);
904 			break;
905 		case PAPI_RANGE: {
906 			char string[24];
907 
908 			if (values[i]->range.lower == values[i]->range.upper)
909 				snprintf(string, sizeof (string), "%d",
910 						values[i]->range.lower);
911 			else
912 				snprintf(string, sizeof (string), "%d-%d",
913 						values[i]->range.lower,
914 						values[i]->range.upper);
915 			rc = strlcat(buffer, string, buflen);
916 			}
917 			break;
918 		case PAPI_RESOLUTION: {
919 			char string[24];
920 
921 			snprintf(string, sizeof (string), "%dx%ddp%c",
922 				values[i]->resolution.xres,
923 				values[i]->resolution.yres,
924 				(values[i]->resolution.units == PAPI_RES_PER_CM
925 							? 'c' : 'i'));
926 			rc = strlcat(buffer, string, buflen);
927 			}
928 			break;
929 		case PAPI_DATETIME: {
930 			struct tm *tm = localtime(&values[i]->datetime);
931 
932 			if (tm != NULL) {
933 				char string[64];
934 
935 				strftime(string, sizeof (string), "%C", tm);
936 				rc = strlcat(buffer, string, buflen);
937 			}}
938 			break;
939 		case PAPI_COLLECTION: {
940 			char *string = alloca(buflen);
941 
942 			papiAttributeListToString(values[i]->collection,
943 					delim, string, buflen);
944 			rc = strlcat(buffer, string, buflen);
945 			}
946 			break;
947 		default: {
948 			char string[32];
949 
950 			snprintf(string, sizeof (string), "unknown-type-0x%x",
951 				attribute->type);
952 			rc = strlcat(buffer, string, buflen);
953 			}
954 		}
955 		if (values[i+1] != NULL)
956 			rc = strlcat(buffer, ",", buflen);
957 
958 		if (rc >= buflen)
959 			return (PAPI_NOT_POSSIBLE);
960 
961 	}
962 
963 	return (PAPI_OK);
964 }
965 
966 papi_status_t
967 papiAttributeListToString(papi_attribute_t **attrs,
968 		char *delim, char *buffer, size_t buflen)
969 {
970 	papi_status_t status = PAPI_OK;
971 	int i;
972 
973 	if ((attrs == NULL) || (buffer == NULL))
974 		return (PAPI_BAD_ARGUMENT);
975 
976 	buffer[0] = '\0';
977 	if (!delim)
978 		delim = " ";
979 
980 	for (i = 0; ((attrs[i] != NULL) && (status == PAPI_OK)); i++) {
981 		status = papiAttributeToString(attrs[i], delim, buffer, buflen);
982 		if (attrs[i+1] != NULL)
983 			strlcat(buffer, delim, buflen);
984 	}
985 
986 	return (status);
987 }
988 
989 static int
990 is_in_list(char *value, char **list)
991 {
992 	if ((list != NULL) && (value != NULL)) {
993 		int i;
994 
995 		for (i = 0; list[i] != NULL; i++)
996 			if (strcasecmp(value, list[i]) == 0)
997 				return (0);
998 	}
999 
1000 	return (1);
1001 }
1002 
1003 static papi_status_t
1004 copy_attribute(papi_attribute_t ***list, papi_attribute_t *attribute)
1005 {
1006 	papi_status_t status;
1007 	int i = 0;
1008 
1009 	if ((list == NULL) || (attribute == NULL) ||
1010 	    (attribute->values == NULL))
1011 		return (PAPI_BAD_ARGUMENT);
1012 
1013 	for (status = papiAttributeListAddValue(list, PAPI_ATTR_EXCL,
1014 					attribute->name, attribute->type,
1015 					attribute->values[i]);
1016 	     ((status == PAPI_OK) && (attribute->values[i] != NULL));
1017 	     status = papiAttributeListAddValue(list, PAPI_ATTR_APPEND,
1018 					attribute->name, attribute->type,
1019 					attribute->values[i]))
1020 		i++;
1021 
1022 	return (status);
1023 }
1024 
1025 void
1026 copy_attributes(papi_attribute_t ***result, papi_attribute_t **attributes)
1027 {
1028 	int i;
1029 
1030 	if ((result == NULL) || (attributes == NULL))
1031 		return;
1032 
1033 	for (i = 0; attributes[i] != NULL; i++)
1034 		copy_attribute(result, attributes[i]);
1035 }
1036 
1037 void
1038 split_and_copy_attributes(char **list, papi_attribute_t **attributes,
1039 		papi_attribute_t ***in, papi_attribute_t ***out)
1040 {
1041 	int i;
1042 
1043 	if ((list == NULL) || (attributes == NULL))
1044 		return;
1045 
1046 	for (i = 0; attributes[i] != NULL; i++)
1047 		if (is_in_list(attributes[i]->name, list) == 0)
1048 			copy_attribute(in, attributes[i]);
1049 		else
1050 			copy_attribute(out, attributes[i]);
1051 }
1052 
1053 void
1054 papiAttributeListPrint(FILE *fp, papi_attribute_t **attributes,
1055 		char *prefix_fmt, ...)
1056 {
1057 	char *prefix = NULL;
1058 	char *buffer = NULL;
1059 	char *newfmt = NULL;
1060 	void *mem;
1061 	ssize_t size = 0;
1062 	va_list ap;
1063 
1064 	newfmt = malloc(strlen(prefix_fmt) + 2);
1065 	sprintf(newfmt, "\n%s", prefix_fmt);
1066 
1067 	va_start(ap, prefix_fmt);
1068 	while (vsnprintf(prefix, size, newfmt, ap) > size) {
1069 		size += 1024;
1070 		mem = realloc(prefix, size);
1071 		if (!mem) goto error;
1072 		prefix = mem;
1073 	}
1074 	va_end(ap);
1075 
1076 	if (attributes) {
1077 		size = 0;
1078 		while (papiAttributeListToString(attributes, prefix, buffer,
1079 						size) != PAPI_OK) {
1080 			size += 1024;
1081 			mem = realloc(buffer, size);
1082 			if (!mem) goto error;
1083 			buffer = mem;
1084 		}
1085 	}
1086 
1087 	fprintf(fp, "%s%s\n", prefix, buffer ? buffer : "");
1088 	fflush(fp);
1089 
1090  error:
1091 	free(newfmt);
1092 	free(prefix);
1093 	free(buffer);
1094 }
1095