xref: /illumos-gate/usr/src/lib/print/libpapi-common/common/attribute.c (revision 8c69cc8fbe729fa7b091e901c4b50508ccc6bb33)
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 (c) 2014 Gary Mills
24  *
25  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
26  * Use is subject to license terms.
27  *
28  */
29 
30 /*LINTLIBRARY*/
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <stdarg.h>
35 #include <string.h>
36 #include <ctype.h>
37 #include <alloca.h>
38 #include <papi.h>
39 #include <regex.h>
40 
41 #define	MAX_PAGES 32767
42 /*
43  * Assuming the maximum number of pages in
44  * a document to be 32767
45  */
46 
47 static void papiAttributeFree(papi_attribute_t *attribute);
48 
49 static void
50 papiAttributeValueFree(papi_attribute_value_type_t type,
51     papi_attribute_value_t *value)
52 {
53 	if (value != NULL) {
54 		switch (type) {
55 		case PAPI_STRING:
56 			if (value->string != NULL)
57 				free(value->string);
58 			break;
59 		case PAPI_COLLECTION:
60 			if (value->collection != NULL) {
61 				int i;
62 
63 				for (i = 0; value->collection[i] != NULL; i++)
64 					papiAttributeFree(value->collection[i]);
65 
66 				free(value->collection);
67 			}
68 			break;
69 		default: /* don't need to free anything extra */
70 			break;
71 		}
72 
73 		free(value);
74 	}
75 }
76 
77 static void
78 papiAttributeValuesFree(papi_attribute_value_type_t type,
79     papi_attribute_value_t **values)
80 {
81 	if (values != NULL) {
82 		int i;
83 
84 		for (i = 0; values[i] != NULL; i++)
85 			papiAttributeValueFree(type, values[i]);
86 
87 		free(values);
88 	}
89 }
90 
91 static void
92 papiAttributeFree(papi_attribute_t *attribute)
93 {
94 	if (attribute != NULL) {
95 		free(attribute->name);
96 		if (attribute->values != NULL)
97 			papiAttributeValuesFree(attribute->type,
98 			    attribute->values);
99 		free(attribute);
100 	}
101 }
102 
103 void
104 papiAttributeListFree(papi_attribute_t **list)
105 {
106 	if (list != NULL) {
107 		int i;
108 
109 		for (i = 0; list[i] != NULL; i++)
110 			papiAttributeFree(list[i]);
111 
112 		free(list);
113 	}
114 }
115 
116 static papi_attribute_t **
117 collection_dup(papi_attribute_t **collection)
118 {
119 	papi_attribute_t **result = NULL;
120 
121 	/* allows a NULL collection that is "empty" or "no value" */
122 	if (collection != NULL) {
123 		papi_status_t status = PAPI_OK;
124 		int i;
125 
126 		for (i = 0; ((collection[i] != NULL) && (status == PAPI_OK));
127 		    i++) {
128 			papi_attribute_t *a = collection[i];
129 
130 			status = papiAttributeListAddValue(&result,
131 			    PAPI_ATTR_APPEND, a->name, a->type, NULL);
132 			if ((status == PAPI_OK) && (a->values != NULL)) {
133 				int j;
134 
135 				for (j = 0; ((a->values[j] != NULL) &&
136 				    (status == PAPI_OK)); j++)
137 					status = papiAttributeListAddValue(
138 					    &result, PAPI_ATTR_APPEND,
139 					    a->name, a->type, 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, papi_resolution_unit_t units)
337 {
338 	papi_attribute_value_t v;
339 
340 	v.resolution.xres = xres;
341 	v.resolution.yres = yres;
342 	v.resolution.units = units;
343 	return (papiAttributeListAddValue(list, flags, name,
344 	    PAPI_RESOLUTION, &v));
345 }
346 
347 papi_status_t
348 papiAttributeListAddDatetime(papi_attribute_t ***list, int flags,
349     char *name, time_t datetime)
350 {
351 	papi_attribute_value_t v;
352 
353 	v.datetime = datetime;
354 	return (papiAttributeListAddValue(list, flags, name,
355 	    PAPI_DATETIME, &v));
356 }
357 
358 papi_status_t
359 papiAttributeListAddCollection(papi_attribute_t ***list, int flags,
360     char *name, papi_attribute_t **collection)
361 {
362 	papi_attribute_value_t v;
363 
364 	v.collection = (papi_attribute_t **)collection;
365 	return (papiAttributeListAddValue(list, flags, name,
366 	    PAPI_COLLECTION, &v));
367 }
368 
369 papi_status_t
370 papiAttributeListAddMetadata(papi_attribute_t ***list, int flags,
371     char *name, papi_metadata_t metadata)
372 {
373 	papi_attribute_value_t v;
374 
375 	v.metadata = metadata;
376 	return (papiAttributeListAddValue(list, flags, name,
377 	    PAPI_METADATA, &v));
378 }
379 
380 papi_status_t
381 papiAttributeListDelete(papi_attribute_t ***list, char *name)
382 {
383 	papi_attribute_t *attribute;
384 
385 	if ((list == NULL) || (name == NULL))
386 		return (PAPI_BAD_ARGUMENT);
387 
388 	if ((attribute = papiAttributeListFind(*list, name)) == NULL)
389 		return (PAPI_NOT_FOUND);
390 
391 	list_remove(list, attribute);
392 	papiAttributeFree(attribute);
393 
394 	return (PAPI_OK);
395 }
396 
397 papi_attribute_t *
398 papiAttributeListFind(papi_attribute_t **list, char *name)
399 {
400 	int i;
401 	if ((list == NULL) || (name == NULL))
402 		return (NULL);
403 
404 	for (i = 0; list[i] != NULL; i++)
405 		if (strcasecmp(list[i]->name, name) == 0)
406 			return ((papi_attribute_t *)list[i]);
407 
408 	return (NULL);
409 }
410 
411 papi_attribute_t *
412 papiAttributeListGetNext(papi_attribute_t **list, void **iter)
413 {
414 	papi_attribute_t **tmp, *result;
415 
416 	if ((list == NULL) && (iter == NULL))
417 		return (NULL);
418 
419 	if (*iter == NULL)
420 		*iter = list;
421 
422 	tmp = *iter;
423 	result = *tmp;
424 	*iter = ++tmp;
425 
426 	return (result);
427 }
428 
429 papi_status_t
430 papiAttributeListGetValue(papi_attribute_t **list, void **iter,
431     char *name, papi_attribute_value_type_t type,
432     papi_attribute_value_t **value)
433 {
434 	papi_attribute_value_t **tmp;
435 	void *fodder = NULL;
436 
437 	if ((list == NULL) || ((name == NULL) && (iter == NULL)) ||
438 	    (value == NULL))
439 		return (PAPI_BAD_ARGUMENT);
440 
441 	if (iter == NULL)
442 		iter = &fodder;
443 
444 	if ((iter == NULL) || (*iter == NULL)) {
445 		papi_attribute_t *attr = papiAttributeListFind(list, name);
446 
447 		if (attr == NULL)
448 			return (PAPI_NOT_FOUND);
449 
450 		if (attr->type != type)
451 			return (PAPI_NOT_POSSIBLE);
452 
453 		tmp = attr->values;
454 	} else
455 		tmp = *iter;
456 
457 	if (tmp == NULL)
458 		return (PAPI_NOT_FOUND);
459 
460 	*value = *tmp;
461 	*iter =  ++tmp;
462 
463 	if (*value == NULL)
464 		return (PAPI_GONE);
465 
466 	return (PAPI_OK);
467 }
468 
469 papi_status_t
470 papiAttributeListGetString(papi_attribute_t **list, void **iter,
471     char *name, char **vptr)
472 {
473 	papi_status_t status;
474 	papi_attribute_value_t *value = NULL;
475 
476 	if (vptr == NULL)
477 		return (PAPI_BAD_ARGUMENT);
478 
479 	status = papiAttributeListGetValue(list, iter, name,
480 	    PAPI_STRING, &value);
481 	if (status == PAPI_OK)
482 		*vptr = value->string;
483 
484 	return (status);
485 }
486 
487 papi_status_t
488 papiAttributeListGetInteger(papi_attribute_t **list, void **iter,
489     char *name, int *vptr)
490 {
491 	papi_status_t status;
492 	papi_attribute_value_t *value = NULL;
493 
494 	if (vptr == NULL)
495 		return (PAPI_BAD_ARGUMENT);
496 
497 	status = papiAttributeListGetValue(list, iter, name,
498 	    PAPI_INTEGER, &value);
499 	if (status == PAPI_OK)
500 		*vptr = value->integer;
501 
502 	return (status);
503 }
504 
505 papi_status_t
506 papiAttributeListGetBoolean(papi_attribute_t **list, void **iter,
507     char *name, char *vptr)
508 {
509 	papi_status_t status;
510 	papi_attribute_value_t *value = NULL;
511 
512 	if (vptr == NULL)
513 		return (PAPI_BAD_ARGUMENT);
514 
515 	status = papiAttributeListGetValue(list, iter, name,
516 	    PAPI_BOOLEAN, &value);
517 	if (status == PAPI_OK)
518 		*vptr = value->boolean;
519 
520 	return (status);
521 }
522 
523 papi_status_t
524 papiAttributeListGetRange(papi_attribute_t **list, void **iter,
525     char *name, int *min, int *max)
526 {
527 	papi_status_t status;
528 	papi_attribute_value_t *value = NULL;
529 
530 	if ((min == NULL) || (max == NULL))
531 		return (PAPI_BAD_ARGUMENT);
532 
533 	status = papiAttributeListGetValue(list, iter, name,
534 	    PAPI_RANGE, &value);
535 	if (status == PAPI_OK) {
536 		*min = value->range.lower;
537 		*max = value->range.upper;
538 	}
539 
540 	return (status);
541 }
542 
543 papi_status_t
544 papiAttributeListGetResolution(papi_attribute_t **list, void **iter,
545     char *name, int *x, int *y, papi_resolution_unit_t *units)
546 {
547 	papi_status_t status;
548 	papi_attribute_value_t *value = NULL;
549 
550 	if ((x == NULL) || (y == NULL) || (units == NULL))
551 		return (PAPI_BAD_ARGUMENT);
552 
553 	status = papiAttributeListGetValue(list, iter, name,
554 	    PAPI_RESOLUTION, &value);
555 	if (status == PAPI_OK) {
556 		*x = value->resolution.xres;
557 		*y = value->resolution.yres;
558 		*units = value->resolution.units;
559 	}
560 
561 	return (status);
562 }
563 
564 papi_status_t
565 papiAttributeListGetDatetime(papi_attribute_t **list, void **iter,
566     char *name, time_t *dt)
567 {
568 	papi_status_t status;
569 	papi_attribute_value_t *value = NULL;
570 
571 	if (dt == NULL)
572 		return (PAPI_BAD_ARGUMENT);
573 
574 	status = papiAttributeListGetValue(list, iter, name,
575 	    PAPI_DATETIME, &value);
576 	if (status == PAPI_OK) {
577 		*dt = value->datetime;
578 	}
579 
580 	return (status);
581 }
582 
583 papi_status_t
584 papiAttributeListGetCollection(papi_attribute_t **list, void **iter,
585     char *name, papi_attribute_t ***collection)
586 {
587 	papi_status_t status;
588 	papi_attribute_value_t *value = NULL;
589 
590 	if (collection == NULL)
591 		return (PAPI_BAD_ARGUMENT);
592 
593 	status = papiAttributeListGetValue(list, iter, name,
594 	    PAPI_COLLECTION, &value);
595 	if (status == PAPI_OK) {
596 		*collection = value->collection;
597 	}
598 
599 	return (status);
600 }
601 
602 papi_status_t
603 papiAttributeListGetMetadata(papi_attribute_t **list, void **iter,
604     char *name, papi_metadata_t *vptr)
605 {
606 	papi_status_t status;
607 	papi_attribute_value_t *value = NULL;
608 
609 	if (vptr == NULL)
610 		return (PAPI_BAD_ARGUMENT);
611 
612 	status = papiAttributeListGetValue(list, iter, name,
613 	    PAPI_METADATA, &value);
614 	if (status == PAPI_OK)
615 		*vptr = value->metadata;
616 
617 	return (status);
618 }
619 
620 
621 /* The string is modified by this call */
622 static char *
623 regvalue(regmatch_t match, char *string)
624 {
625 	char *result = NULL;
626 	if (match.rm_so != match.rm_eo) {
627 		result = string + match.rm_so;
628 		*(result + (match.rm_eo - match.rm_so)) = '\0';
629 	}
630 	return (result);
631 }
632 
633 static papi_attribute_value_type_t
634 _process_value(char *string, char ***parts)
635 {
636 	int i;
637 	static struct {
638 		papi_attribute_value_type_t	type;
639 		size_t vals;
640 		char *expression;
641 		int	compiled;
642 		regex_t re;
643 	} types[] = {
644 		{ PAPI_BOOLEAN,	   1, "^(true|false|yes|no)$", 0 },
645 		{ PAPI_COLLECTION, 1, "^\\{(.+)\\}$", 0 },
646 		/* PAPI_DATETIME is unsupported, too much like an integer */
647 		{ PAPI_INTEGER,	   1, "^([+-]{0,1}[[:digit:]]+)$", 0 },
648 		{ PAPI_RANGE,	   3, "^([[:digit:]]*)-([[:digit:]]*)$", 0 },
649 		{ PAPI_RESOLUTION, 4, "^([[:digit:]]+)x([[:digit:]]+)dp(i|c)$",
650 			0 },
651 		NULL
652 	};
653 	regmatch_t matches[4];
654 
655 	for (i = 0; i < 5; i++) {
656 		int j;
657 
658 		if (types[i].compiled == 0) {
659 			(void) regcomp(&(types[i].re), types[i].expression,
660 			    REG_EXTENDED|REG_ICASE);
661 			types[i].compiled = 1;
662 		}
663 		if (regexec(&(types[i].re), string, (size_t)types[i].vals,
664 		    matches, 0) == REG_NOMATCH)
665 			continue;
666 
667 		for (j = 0; j < types[i].vals; j++)
668 			list_append(parts, regvalue(matches[j], string));
669 		return (types[i].type);
670 	}
671 
672 	list_append(parts, string);
673 	return (PAPI_STRING);
674 }
675 
676 static void
677 _add_attribute_value(papi_attribute_value_t ***list,
678     papi_attribute_value_type_t type,
679     papi_attribute_value_type_t dtype, char **parts)
680 {
681 	papi_attribute_value_t *value = calloc(1, sizeof (*value));
682 
683 	switch (type) {
684 	case PAPI_STRING:
685 		value->string = strdup(parts[0]);
686 		list_append(list, value);
687 		break;
688 	case PAPI_BOOLEAN:
689 		value->boolean = PAPI_TRUE;
690 		if ((strcasecmp(parts[0], "false") == 0) ||
691 		    (strcasecmp(parts[0], "no") == 0))
692 			value->boolean = PAPI_FALSE;
693 		list_append(list, value);
694 		break;
695 	case PAPI_INTEGER:
696 		value->integer = atoi(parts[0]);
697 		list_append(list, value);
698 		break;
699 	case PAPI_RANGE:
700 		if (dtype == PAPI_INTEGER) {
701 			if (atoi(parts[0]) < 0) {
702 				/*
703 				 * Handles -P -x case
704 				 * which prints from page number 1
705 				 * till page number x
706 				 */
707 				value->range.lower = 1;
708 				value->range.upper = 0 - (atoi(parts[0]));
709 			} else {
710 				value->range.lower = value->range.upper
711 				    = atoi(parts[0]);
712 			}
713 		} else if (dtype == PAPI_RANGE)  {
714 			if (parts[2] == NULL) {
715 				value->range.lower = atoi(parts[1]);
716 				/*
717 				 * Imposing an artificial limit on
718 				 * the upper bound for page range.
719 				 */
720 				value->range.upper = MAX_PAGES;
721 			} else if ((parts[1] != NULL) && (parts[2] != NULL)) {
722 				value->range.lower = atoi(parts[1]);
723 				value->range.upper = atoi(parts[2]);
724 			}
725 		}
726 		list_append(list, value);
727 		break;
728 	case PAPI_RESOLUTION:
729 		value->resolution.xres = atoi(parts[1]);
730 		value->resolution.yres = atoi(parts[2]);
731 		if (parts[3][0] == 'i')
732 			value->resolution.units = PAPI_RES_PER_INCH;
733 		else
734 			value->resolution.units = PAPI_RES_PER_CM;
735 		list_append(list, value);
736 		break;
737 	case PAPI_COLLECTION:
738 		papiAttributeListFromString(&(value->collection), 0, parts[0]);
739 		list_append(list, value);
740 		break;
741 	}
742 }
743 
744 static papi_status_t
745 _papiAttributeFromStrings(papi_attribute_t ***list, int flags,
746     char *key, char **values)
747 {
748 	int i;
749 	papi_status_t result = PAPI_OK;
750 	papi_attribute_t *attr = calloc(1, sizeof (*attr));
751 
752 	/* these are specified in the papi spec as ranges */
753 	char *ranges[] = { "copies-supported", "job-impressions-supported",
754 				"job-k-octets-supported",
755 				"job-media-sheets-supported", "page-ranges",
756 				NULL };
757 
758 	if ((attr == NULL) || ((attr->name = strdup(key)) == NULL))
759 		return (PAPI_TEMPORARY_ERROR);
760 
761 	attr->type = PAPI_METADATA;
762 	/* these are known ranges */
763 	for (i = 0; ranges[i] != NULL; i++)
764 		if (strcasecmp(attr->name, ranges[i]) == 0) {
765 			attr->type = PAPI_RANGE;
766 			break;
767 	}
768 
769 	if (values != NULL) {
770 		papi_attribute_value_t **vals = NULL;
771 
772 		for (i = 0; values[i] != NULL; i++) {
773 			papi_attribute_value_type_t dtype;
774 			char **parts = NULL;
775 
776 			dtype = _process_value(values[i], &parts);
777 			if (attr->type == PAPI_METADATA)
778 				attr->type = dtype;
779 			_add_attribute_value(&vals, attr->type, dtype, parts);
780 			free(parts);
781 		}
782 		attr->values = vals;
783 	}
784 
785 	list_append(list, attr);
786 
787 	return (result);
788 }
789 
790 static papi_status_t
791 _parse_attribute_list(papi_attribute_t ***list, int flags, char *string)
792 {
793 	papi_status_t result = PAPI_OK;
794 	char *ptr;
795 
796 	if ((list == NULL) || (string == NULL))
797 		return (PAPI_BAD_ARGUMENT);
798 
799 	if ((ptr = strdup(string)) == NULL)
800 		return (PAPI_TEMPORARY_ERROR);
801 
802 	while ((*ptr != '\0') && (result == PAPI_OK)) {
803 		char *key, **values = NULL;
804 
805 		/* strip any leading whitespace */
806 		while (isspace(*ptr) != 0)
807 			ptr++;
808 
809 		/* Get the name: name[=value] */
810 		key = ptr;
811 		while ((*ptr != '\0') && (*ptr != '=') && (isspace(*ptr) == 0))
812 			ptr++;
813 
814 		if (*ptr == '=') {
815 			*ptr++ = '\0';
816 
817 			while ((*ptr != '\0') && (isspace(*ptr) == 0)) {
818 				char *value = ptr;
819 
820 				if ((*ptr == '\'') || (*ptr == '"')) {
821 					char q = *ptr++;
822 
823 					/* quoted string value */
824 					while ((*ptr != '\0') && (*ptr != q))
825 						ptr++;
826 					if (*ptr == q)
827 						ptr++;
828 				} else if (*ptr == '{') {
829 					/* collection */
830 					while ((*ptr != '\0') && (*ptr != '}'))
831 						ptr++;
832 					if (*ptr == '}')
833 						ptr++;
834 				} else {
835 					/* value */
836 					while ((*ptr != '\0') &&
837 					    (*ptr != ',') &&
838 					    (isspace(*ptr) == 0))
839 						ptr++;
840 				}
841 				if (*ptr == ',')
842 					*ptr++ = '\0';
843 				list_append(&values, value);
844 			}
845 		} else { /* boolean "[no]key" */
846 			char *value = "true";
847 
848 			if (strncasecmp(key, "no", 2) == 0) {
849 				key += 2;
850 				value = "false";
851 			}
852 			list_append(&values, value);
853 		}
854 		if (*ptr != '\0')
855 			*ptr++ = '\0';
856 
857 		result = _papiAttributeFromStrings(list, flags, key, values);
858 		free(values);
859 	}
860 
861 	return (result);
862 }
863 
864 papi_status_t
865 papiAttributeListFromString(papi_attribute_t ***attrs, int flags, char *string)
866 {
867 	papi_status_t result = PAPI_OK;
868 
869 	if ((attrs != NULL) && (string != NULL) &&
870 	    ((flags & ~(PAPI_ATTR_APPEND+PAPI_ATTR_REPLACE+PAPI_ATTR_EXCL))
871 	    == 0)) {
872 		result = _parse_attribute_list(attrs, flags, string);
873 	} else {
874 		result = PAPI_BAD_ARGUMENT;
875 	}
876 
877 	return (result);
878 }
879 
880 static papi_status_t
881 papiAttributeToString(papi_attribute_t *attribute, char *delim,
882     char *buffer, size_t buflen)
883 {
884 	papi_attribute_value_t **values = attribute->values;
885 	int rc, i;
886 
887 	if ((attribute->type == PAPI_BOOLEAN) && (values[1] == NULL)) {
888 		if (values[0]->boolean == PAPI_FALSE) {
889 			if (isupper(attribute->name[0]) == 0)
890 				strlcat(buffer, "no", buflen);
891 			else
892 				strlcat(buffer, "No", buflen);
893 		}
894 		rc = strlcat(buffer, attribute->name, buflen);
895 	} else {
896 		strlcat(buffer, attribute->name, buflen);
897 		rc = strlcat(buffer, "=", buflen);
898 	}
899 
900 	if (values == NULL)
901 		return (PAPI_OK);
902 
903 	for (i = 0; values[i] != NULL; i++) {
904 		switch (attribute->type) {
905 		case PAPI_STRING:
906 			rc = strlcat(buffer, values[i]->string, buflen);
907 			break;
908 		case PAPI_INTEGER: {
909 			char string[24];
910 
911 			snprintf(string, sizeof (string), "%d",
912 			    values[i]->integer);
913 			rc = strlcat(buffer, string, buflen);
914 			}
915 			break;
916 		case PAPI_BOOLEAN:
917 			if (values[1] != NULL)
918 				rc = strlcat(buffer, values[i]->boolean ?
919 				    "true" : "false", buflen);
920 			break;
921 		case PAPI_RANGE: {
922 			char string[24];
923 
924 			if (values[i]->range.lower == values[i]->range.upper)
925 				snprintf(string, sizeof (string), "%d",
926 				    values[i]->range.lower);
927 			else
928 				snprintf(string, sizeof (string), "%d-%d",
929 				    values[i]->range.lower,
930 				    values[i]->range.upper);
931 			rc = strlcat(buffer, string, buflen);
932 			}
933 			break;
934 		case PAPI_RESOLUTION: {
935 			char string[24];
936 
937 			snprintf(string, sizeof (string), "%dx%ddp%c",
938 			    values[i]->resolution.xres,
939 			    values[i]->resolution.yres,
940 			    values[i]->resolution.units == PAPI_RES_PER_CM ?
941 			    'c' : 'i');
942 			rc = strlcat(buffer, string, buflen);
943 			}
944 			break;
945 		case PAPI_DATETIME: {
946 			struct tm *tm = localtime(&values[i]->datetime);
947 
948 			if (tm != NULL) {
949 				char string[64];
950 
951 				strftime(string, sizeof (string), "%c", tm);
952 				rc = strlcat(buffer, string, buflen);
953 			}}
954 			break;
955 		case PAPI_COLLECTION: {
956 			char *string = alloca(buflen);
957 
958 			papiAttributeListToString(values[i]->collection,
959 			    delim, string, buflen);
960 			rc = strlcat(buffer, string, buflen);
961 			}
962 			break;
963 		default: {
964 			char string[32];
965 
966 			snprintf(string, sizeof (string), "unknown-type-0x%x",
967 			    attribute->type);
968 			rc = strlcat(buffer, string, buflen);
969 			}
970 		}
971 		if (values[i+1] != NULL)
972 			rc = strlcat(buffer, ",", buflen);
973 
974 		if (rc >= buflen)
975 			return (PAPI_NOT_POSSIBLE);
976 
977 	}
978 
979 	return (PAPI_OK);
980 }
981 
982 papi_status_t
983 papiAttributeListToString(papi_attribute_t **attrs,
984     char *delim, char *buffer, size_t buflen)
985 {
986 	papi_status_t status = PAPI_OK;
987 	int i;
988 
989 	if ((attrs == NULL) || (buffer == NULL))
990 		return (PAPI_BAD_ARGUMENT);
991 
992 	buffer[0] = '\0';
993 	if (!delim)
994 		delim = " ";
995 
996 	for (i = 0; ((attrs[i] != NULL) && (status == PAPI_OK)); i++) {
997 		status = papiAttributeToString(attrs[i], delim, buffer, buflen);
998 		if (attrs[i+1] != NULL)
999 			strlcat(buffer, delim, buflen);
1000 	}
1001 
1002 	return (status);
1003 }
1004 
1005 static int
1006 is_in_list(char *value, char **list)
1007 {
1008 	if ((list != NULL) && (value != NULL)) {
1009 		int i;
1010 
1011 		for (i = 0; list[i] != NULL; i++)
1012 			if (strcasecmp(value, list[i]) == 0)
1013 				return (0);
1014 	}
1015 
1016 	return (1);
1017 }
1018 
1019 static papi_status_t
1020 copy_attribute(papi_attribute_t ***list, papi_attribute_t *attribute)
1021 {
1022 	papi_status_t status;
1023 	int i = 0;
1024 
1025 	if ((list == NULL) || (attribute == NULL) ||
1026 	    (attribute->values == NULL))
1027 		return (PAPI_BAD_ARGUMENT);
1028 
1029 	for (status = papiAttributeListAddValue(list, PAPI_ATTR_EXCL,
1030 	    attribute->name, attribute->type, attribute->values[i]);
1031 	    ((status == PAPI_OK) && (attribute->values[i] != NULL));
1032 	    status = papiAttributeListAddValue(list, PAPI_ATTR_APPEND,
1033 	    attribute->name, attribute->type, attribute->values[i]))
1034 		i++;
1035 
1036 	return (status);
1037 }
1038 
1039 void
1040 copy_attributes(papi_attribute_t ***result, papi_attribute_t **attributes)
1041 {
1042 	int i;
1043 
1044 	if ((result == NULL) || (attributes == NULL))
1045 		return;
1046 
1047 	for (i = 0; attributes[i] != NULL; i++)
1048 		copy_attribute(result, attributes[i]);
1049 }
1050 
1051 void
1052 split_and_copy_attributes(char **list, papi_attribute_t **attributes,
1053     papi_attribute_t ***in, papi_attribute_t ***out)
1054 {
1055 	int i;
1056 
1057 	if ((list == NULL) || (attributes == NULL))
1058 		return;
1059 
1060 	for (i = 0; attributes[i] != NULL; i++)
1061 		if (is_in_list(attributes[i]->name, list) == 0)
1062 			copy_attribute(in, attributes[i]);
1063 		else
1064 			copy_attribute(out, attributes[i]);
1065 }
1066 
1067 void
1068 papiAttributeListPrint(FILE *fp, papi_attribute_t **attributes,
1069     char *prefix_fmt, ...)
1070 {
1071 	char *prefix = NULL;
1072 	char *buffer = NULL;
1073 	char *newfmt = NULL;
1074 	void *mem;
1075 	ssize_t size = 0;
1076 	va_list ap;
1077 
1078 	newfmt = malloc(strlen(prefix_fmt) + 2);
1079 	sprintf(newfmt, "\n%s", prefix_fmt);
1080 
1081 	va_start(ap, prefix_fmt);
1082 	while (vsnprintf(prefix, size, newfmt, ap) > size) {
1083 		size += 1024;
1084 		mem = realloc(prefix, size);
1085 		if (!mem) goto error;
1086 		prefix = mem;
1087 	}
1088 	va_end(ap);
1089 
1090 	if (attributes) {
1091 		size = 0;
1092 		while (papiAttributeListToString(attributes, prefix, buffer,
1093 		    size) != PAPI_OK) {
1094 			size += 1024;
1095 			mem = realloc(buffer, size);
1096 			if (!mem) goto error;
1097 			buffer = mem;
1098 		}
1099 	}
1100 
1101 	fprintf(fp, "%s%s\n", prefix, buffer ? buffer : "");
1102 	fflush(fp);
1103 
1104 error:
1105 	free(newfmt);
1106 	free(prefix);
1107 	free(buffer);
1108 }
1109