xref: /illumos-gate/usr/src/lib/print/libpapi-common/common/attribute.c (revision 8a2b682e57a046b828f37bcde1776f131ef4629f)
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 			}
166 			break;
167 		case PAPI_INTEGER:
168 			result->integer = v->integer;
169 			break;
170 		case PAPI_BOOLEAN:
171 			result->boolean = v->boolean;
172 			break;
173 		case PAPI_RANGE:
174 			result->range.lower = v->range.lower;
175 			result->range.upper = v->range.upper;
176 			break;
177 		case PAPI_RESOLUTION:
178 			result->resolution.xres = v->resolution.xres;
179 			result->resolution.yres = v->resolution.yres;
180 			result->resolution.units = v->resolution.units;
181 			break;
182 		case PAPI_DATETIME:
183 			result->datetime = v->datetime;
184 			break;
185 		case PAPI_COLLECTION:
186 			result->collection = collection_dup(v->collection);
187 			break;
188 		case PAPI_METADATA:
189 			result->metadata = v->metadata;
190 			break;
191 		default:	/* unknown type, fail to duplicate */
192 			free(result);
193 			result = NULL;
194 		}
195 	}
196 
197 	return (result);
198 }
199 
200 static papi_attribute_t *
201 papiAttributeAlloc(char *name, papi_attribute_value_type_t type)
202 {
203 	papi_attribute_t *result = NULL;
204 
205 	if ((result = calloc(1, sizeof (*result))) != NULL) {
206 		result->name = strdup(name);
207 		result->type = type;
208 	}
209 
210 	return (result);
211 }
212 
213 static papi_status_t
214 papiAttributeListAppendValue(papi_attribute_value_t ***values,
215     papi_attribute_value_type_t type,
216     papi_attribute_value_t *value)
217 {
218 
219 	if (values == NULL)
220 		return (PAPI_BAD_ARGUMENT);
221 
222 	if (value != NULL) {	/* this allows "empty" attributes */
223 		papi_attribute_value_t *tmp = NULL;
224 
225 		if ((tmp = papiAttributeValueDup(type, value)) == NULL)
226 			return (PAPI_TEMPORARY_ERROR);
227 
228 		list_append(values, tmp);
229 	}
230 
231 	return (PAPI_OK);
232 }
233 
234 papi_status_t
235 papiAttributeListAddValue(papi_attribute_t ***list, int flgs,
236     char *name, papi_attribute_value_type_t type,
237     papi_attribute_value_t *value)
238 {
239 	papi_status_t result;
240 	int flags = flgs;
241 	papi_attribute_t *attribute = NULL;
242 	papi_attribute_value_t **values = NULL;
243 
244 	if ((list == NULL) || (name == NULL))
245 		return (PAPI_BAD_ARGUMENT);
246 
247 	if ((type == PAPI_RANGE) && (value != NULL) &&
248 	    (value->range.lower > value->range.upper))
249 		return (PAPI_BAD_ARGUMENT);	/* RANGE must have min <= max */
250 
251 	if (flags == 0) /* if it wasn't set, set a default behaviour */
252 		flags = PAPI_ATTR_APPEND;
253 
254 	/* look for an existing one */
255 	attribute = papiAttributeListFind(*list, name);
256 
257 	if (((flags & PAPI_ATTR_EXCL) != 0) && (attribute != NULL))
258 		return (PAPI_CONFLICT); /* EXISTS */
259 
260 	if (((flags & PAPI_ATTR_REPLACE) == 0) && (attribute != NULL) &&
261 	    (attribute->type != type))
262 		return (PAPI_CONFLICT); /* TYPE CONFLICT */
263 
264 	/* if we don't have one, create it and add it to the list */
265 	if ((attribute == NULL) &&
266 	    ((attribute = papiAttributeAlloc(name, type)) != NULL))
267 		list_append(list, attribute);
268 
269 	/* if we don't have one by now, it's most likely an alloc fail */
270 	if (attribute == NULL)
271 		return (PAPI_TEMPORARY_ERROR);
272 
273 	/*
274 	 * if we are replacing, clear any existing values, but don't free
275 	 * until after we have replaced the values, in case we are replacing
276 	 * a collection with a relocated version of the original collection.
277 	 */
278 	if (((flags & PAPI_ATTR_REPLACE) != 0) && (attribute->values != NULL)) {
279 		values = attribute->values;
280 		attribute->values = NULL;
281 	}
282 
283 	attribute->type = type;
284 
285 	result = papiAttributeListAppendValue(&attribute->values, type, value);
286 
287 	/* free old values if we replaced them */
288 	if (values != NULL)
289 		papiAttributeValuesFree(type, values);
290 
291 	return (result);
292 }
293 
294 papi_status_t
295 papiAttributeListAddString(papi_attribute_t ***list, int flags,
296     char *name, char *string)
297 {
298 	papi_attribute_value_t v;
299 
300 	v.string = (char *)string;
301 	return (papiAttributeListAddValue(list, flags, name, PAPI_STRING, &v));
302 }
303 
304 papi_status_t
305 papiAttributeListAddInteger(papi_attribute_t ***list, int flags,
306     char *name, int integer)
307 {
308 	papi_attribute_value_t v;
309 
310 	v.integer = integer;
311 	return (papiAttributeListAddValue(list, flags, name, PAPI_INTEGER, &v));
312 }
313 
314 papi_status_t
315 papiAttributeListAddBoolean(papi_attribute_t ***list, int flags,
316     char *name, char boolean)
317 {
318 	papi_attribute_value_t v;
319 
320 	v.boolean = boolean;
321 	return (papiAttributeListAddValue(list, flags, name, PAPI_BOOLEAN, &v));
322 }
323 
324 papi_status_t
325 papiAttributeListAddRange(papi_attribute_t ***list, int flags,
326     char *name, int lower, int upper)
327 {
328 	papi_attribute_value_t v;
329 
330 	v.range.lower = lower;
331 	v.range.upper = upper;
332 	return (papiAttributeListAddValue(list, flags, name, PAPI_RANGE, &v));
333 }
334 
335 papi_status_t
336 papiAttributeListAddResolution(papi_attribute_t ***list, int flags,
337     char *name, int xres, int yres, 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 
459 	if (tmp == NULL)
460 		return (PAPI_NOT_FOUND);
461 
462 	*value = *tmp;
463 	*iter =  ++tmp;
464 
465 	if (*value == NULL)
466 		return (PAPI_GONE);
467 
468 	return (PAPI_OK);
469 }
470 
471 papi_status_t
472 papiAttributeListGetString(papi_attribute_t **list, void **iter,
473     char *name, char **vptr)
474 {
475 	papi_status_t status;
476 	papi_attribute_value_t *value = NULL;
477 
478 	if (vptr == NULL)
479 		return (PAPI_BAD_ARGUMENT);
480 
481 	status = papiAttributeListGetValue(list, iter, name,
482 	    PAPI_STRING, &value);
483 	if (status == PAPI_OK)
484 		*vptr = value->string;
485 
486 	return (status);
487 }
488 
489 papi_status_t
490 papiAttributeListGetInteger(papi_attribute_t **list, void **iter,
491     char *name, int *vptr)
492 {
493 	papi_status_t status;
494 	papi_attribute_value_t *value = NULL;
495 
496 	if (vptr == NULL)
497 		return (PAPI_BAD_ARGUMENT);
498 
499 	status = papiAttributeListGetValue(list, iter, name,
500 	    PAPI_INTEGER, &value);
501 	if (status == PAPI_OK)
502 		*vptr = value->integer;
503 
504 	return (status);
505 }
506 
507 papi_status_t
508 papiAttributeListGetBoolean(papi_attribute_t **list, void **iter,
509     char *name, char *vptr)
510 {
511 	papi_status_t status;
512 	papi_attribute_value_t *value = NULL;
513 
514 	if (vptr == NULL)
515 		return (PAPI_BAD_ARGUMENT);
516 
517 	status = papiAttributeListGetValue(list, iter, name,
518 	    PAPI_BOOLEAN, &value);
519 	if (status == PAPI_OK)
520 		*vptr = value->boolean;
521 
522 	return (status);
523 }
524 
525 papi_status_t
526 papiAttributeListGetRange(papi_attribute_t **list, void **iter,
527     char *name, int *min, int *max)
528 {
529 	papi_status_t status;
530 	papi_attribute_value_t *value = NULL;
531 
532 	if ((min == NULL) || (max == NULL))
533 		return (PAPI_BAD_ARGUMENT);
534 
535 	status = papiAttributeListGetValue(list, iter, name,
536 	    PAPI_RANGE, &value);
537 	if (status == PAPI_OK) {
538 		*min = value->range.lower;
539 		*max = value->range.upper;
540 	}
541 
542 	return (status);
543 }
544 
545 papi_status_t
546 papiAttributeListGetResolution(papi_attribute_t **list, void **iter,
547     char *name, int *x, int *y, 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 		0
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 			if (atoi(parts[0]) < 0) {
704 				/*
705 				 * Handles -P -x case
706 				 * which prints from page number 1
707 				 * till page number x
708 				 */
709 				value->range.lower = 1;
710 				value->range.upper = 0 - (atoi(parts[0]));
711 			} else {
712 				value->range.lower = value->range.upper
713 				    = atoi(parts[0]);
714 			}
715 		} else if (dtype == PAPI_RANGE)  {
716 			if (parts[2] == NULL) {
717 				value->range.lower = atoi(parts[1]);
718 				/*
719 				 * Imposing an artificial limit on
720 				 * the upper bound for page range.
721 				 */
722 				value->range.upper = MAX_PAGES;
723 			} else if ((parts[1] != NULL) && (parts[2] != NULL)) {
724 				value->range.lower = atoi(parts[1]);
725 				value->range.upper = atoi(parts[2]);
726 			}
727 		}
728 		list_append(list, value);
729 		break;
730 	case PAPI_RESOLUTION:
731 		value->resolution.xres = atoi(parts[1]);
732 		value->resolution.yres = atoi(parts[2]);
733 		if (parts[3][0] == 'i')
734 			value->resolution.units = PAPI_RES_PER_INCH;
735 		else
736 			value->resolution.units = PAPI_RES_PER_CM;
737 		list_append(list, value);
738 		break;
739 	case PAPI_COLLECTION:
740 		papiAttributeListFromString(&(value->collection), 0, parts[0]);
741 		list_append(list, value);
742 		break;
743 	}
744 }
745 
746 static papi_status_t
747 _papiAttributeFromStrings(papi_attribute_t ***list, int flags,
748     char *key, char **values)
749 {
750 	int i;
751 	papi_status_t result = PAPI_OK;
752 	papi_attribute_t *attr = calloc(1, sizeof (*attr));
753 
754 	/* these are specified in the papi spec as ranges */
755 	char *ranges[] = { "copies-supported", "job-impressions-supported",
756 				"job-k-octets-supported",
757 				"job-media-sheets-supported", "page-ranges",
758 				NULL };
759 
760 	if ((attr == NULL) || ((attr->name = strdup(key)) == NULL))
761 		return (PAPI_TEMPORARY_ERROR);
762 
763 	attr->type = PAPI_METADATA;
764 	/* these are known ranges */
765 	for (i = 0; ranges[i] != NULL; i++)
766 		if (strcasecmp(attr->name, ranges[i]) == 0) {
767 			attr->type = PAPI_RANGE;
768 			break;
769 	}
770 
771 	if (values != NULL) {
772 		papi_attribute_value_t **vals = NULL;
773 
774 		for (i = 0; values[i] != NULL; i++) {
775 			papi_attribute_value_type_t dtype;
776 			char **parts = NULL;
777 
778 			dtype = _process_value(values[i], &parts);
779 			if (attr->type == PAPI_METADATA)
780 				attr->type = dtype;
781 			_add_attribute_value(&vals, attr->type, dtype, parts);
782 			free(parts);
783 		}
784 		attr->values = vals;
785 	}
786 
787 	list_append(list, attr);
788 
789 	return (result);
790 }
791 
792 static papi_status_t
793 _parse_attribute_list(papi_attribute_t ***list, int flags, char *string)
794 {
795 	papi_status_t result = PAPI_OK;
796 	char *ptr;
797 
798 	if ((list == NULL) || (string == NULL))
799 		return (PAPI_BAD_ARGUMENT);
800 
801 	if ((ptr = strdup(string)) == NULL)
802 		return (PAPI_TEMPORARY_ERROR);
803 
804 	while ((*ptr != '\0') && (result == PAPI_OK)) {
805 		char *key, **values = NULL;
806 
807 		/* strip any leading whitespace */
808 		while (isspace(*ptr) != 0)
809 			ptr++;
810 
811 		/* Get the name: name[=value] */
812 		key = ptr;
813 		while ((*ptr != '\0') && (*ptr != '=') && (isspace(*ptr) == 0))
814 			ptr++;
815 
816 		if (*ptr == '=') {
817 			*ptr++ = '\0';
818 
819 			while ((*ptr != '\0') && (isspace(*ptr) == 0)) {
820 				char *value = ptr;
821 
822 				if ((*ptr == '\'') || (*ptr == '"')) {
823 					char q = *ptr++;
824 
825 					/* quoted string value */
826 					while ((*ptr != '\0') && (*ptr != q))
827 						ptr++;
828 					if (*ptr == q)
829 						ptr++;
830 				} else if (*ptr == '{') {
831 					/* collection */
832 					while ((*ptr != '\0') && (*ptr != '}'))
833 						ptr++;
834 					if (*ptr == '}')
835 						ptr++;
836 				} else {
837 					/* value */
838 					while ((*ptr != '\0') &&
839 					    (*ptr != ',') &&
840 					    (isspace(*ptr) == 0))
841 						ptr++;
842 				}
843 				if (*ptr == ',')
844 					*ptr++ = '\0';
845 				list_append(&values, value);
846 			}
847 		} else { /* boolean "[no]key" */
848 			char *value = "true";
849 
850 			if (strncasecmp(key, "no", 2) == 0) {
851 				key += 2;
852 				value = "false";
853 			}
854 			list_append(&values, value);
855 		}
856 		if (*ptr != '\0')
857 			*ptr++ = '\0';
858 
859 		result = _papiAttributeFromStrings(list, flags, key, values);
860 		free(values);
861 	}
862 
863 	return (result);
864 }
865 
866 papi_status_t
867 papiAttributeListFromString(papi_attribute_t ***attrs, int flags, char *string)
868 {
869 	papi_status_t result = PAPI_OK;
870 
871 	if ((attrs != NULL) && (string != NULL) &&
872 	    ((flags & ~(PAPI_ATTR_APPEND + PAPI_ATTR_REPLACE + PAPI_ATTR_EXCL))
873 	    == 0)) {
874 		result = _parse_attribute_list(attrs, flags, string);
875 	} else {
876 		result = PAPI_BAD_ARGUMENT;
877 	}
878 
879 	return (result);
880 }
881 
882 static papi_status_t
883 papiAttributeToString(papi_attribute_t *attribute, char *delim,
884     char *buffer, size_t buflen)
885 {
886 	papi_attribute_value_t **values = attribute->values;
887 	int rc, i;
888 
889 	if ((attribute->type == PAPI_BOOLEAN) && (values[1] == NULL)) {
890 		if (values[0]->boolean == PAPI_FALSE) {
891 			if (isupper(attribute->name[0]) == 0)
892 				strlcat(buffer, "no", buflen);
893 			else
894 				strlcat(buffer, "No", buflen);
895 		}
896 		rc = strlcat(buffer, attribute->name, buflen);
897 	} else {
898 		strlcat(buffer, attribute->name, buflen);
899 		rc = strlcat(buffer, "=", buflen);
900 	}
901 
902 	if (values == NULL)
903 		return (PAPI_OK);
904 
905 	for (i = 0; values[i] != NULL; i++) {
906 		switch (attribute->type) {
907 		case PAPI_STRING:
908 			rc = strlcat(buffer, values[i]->string, buflen);
909 			break;
910 		case PAPI_INTEGER: {
911 			char string[24];
912 
913 			snprintf(string, sizeof (string), "%d",
914 			    values[i]->integer);
915 			rc = strlcat(buffer, string, buflen);
916 			}
917 			break;
918 		case PAPI_BOOLEAN:
919 			if (values[1] != NULL)
920 				rc = strlcat(buffer, values[i]->boolean ?
921 				    "true" : "false", buflen);
922 			break;
923 		case PAPI_RANGE: {
924 			char string[24];
925 
926 			if (values[i]->range.lower == values[i]->range.upper)
927 				snprintf(string, sizeof (string), "%d",
928 				    values[i]->range.lower);
929 			else
930 				snprintf(string, sizeof (string), "%d-%d",
931 				    values[i]->range.lower,
932 				    values[i]->range.upper);
933 			rc = strlcat(buffer, string, buflen);
934 			}
935 			break;
936 		case PAPI_RESOLUTION: {
937 			char string[24];
938 
939 			snprintf(string, sizeof (string), "%dx%ddp%c",
940 			    values[i]->resolution.xres,
941 			    values[i]->resolution.yres,
942 			    values[i]->resolution.units == PAPI_RES_PER_CM ?
943 			    'c' : 'i');
944 			rc = strlcat(buffer, string, buflen);
945 			}
946 			break;
947 		case PAPI_DATETIME: {
948 			struct tm *tm = localtime(&values[i]->datetime);
949 
950 			if (tm != NULL) {
951 				char string[64];
952 
953 				strftime(string, sizeof (string), "%c", tm);
954 				rc = strlcat(buffer, string, buflen);
955 			}}
956 			break;
957 		case PAPI_COLLECTION: {
958 			char *string = alloca(buflen);
959 
960 			papiAttributeListToString(values[i]->collection,
961 			    delim, string, buflen);
962 			rc = strlcat(buffer, string, buflen);
963 			}
964 			break;
965 		default: {
966 			char string[32];
967 
968 			snprintf(string, sizeof (string), "unknown-type-0x%x",
969 			    attribute->type);
970 			rc = strlcat(buffer, string, buflen);
971 			}
972 		}
973 		if (values[i+1] != NULL)
974 			rc = strlcat(buffer, ",", buflen);
975 
976 		if (rc >= buflen)
977 			return (PAPI_NOT_POSSIBLE);
978 
979 	}
980 
981 	return (PAPI_OK);
982 }
983 
984 papi_status_t
985 papiAttributeListToString(papi_attribute_t **attrs,
986     char *delim, char *buffer, size_t buflen)
987 {
988 	papi_status_t status = PAPI_OK;
989 	int i;
990 
991 	if ((attrs == NULL) || (buffer == NULL))
992 		return (PAPI_BAD_ARGUMENT);
993 
994 	buffer[0] = '\0';
995 	if (!delim)
996 		delim = " ";
997 
998 	for (i = 0; ((attrs[i] != NULL) && (status == PAPI_OK)); i++) {
999 		status = papiAttributeToString(attrs[i], delim, buffer, buflen);
1000 		if (attrs[i+1] != NULL)
1001 			strlcat(buffer, delim, buflen);
1002 	}
1003 
1004 	return (status);
1005 }
1006 
1007 static int
1008 is_in_list(char *value, char **list)
1009 {
1010 	if ((list != NULL) && (value != NULL)) {
1011 		int i;
1012 
1013 		for (i = 0; list[i] != NULL; i++)
1014 			if (strcasecmp(value, list[i]) == 0)
1015 				return (0);
1016 	}
1017 
1018 	return (1);
1019 }
1020 
1021 static papi_status_t
1022 copy_attribute(papi_attribute_t ***list, papi_attribute_t *attribute)
1023 {
1024 	papi_status_t status;
1025 	int i = 0;
1026 
1027 	if ((list == NULL) || (attribute == NULL) ||
1028 	    (attribute->values == NULL))
1029 		return (PAPI_BAD_ARGUMENT);
1030 
1031 	for (status = papiAttributeListAddValue(list, PAPI_ATTR_EXCL,
1032 	    attribute->name, attribute->type, attribute->values[i]);
1033 	    ((status == PAPI_OK) && (attribute->values[i] != NULL));
1034 	    status = papiAttributeListAddValue(list, PAPI_ATTR_APPEND,
1035 	    attribute->name, attribute->type, attribute->values[i]))
1036 		i++;
1037 
1038 	return (status);
1039 }
1040 
1041 void
1042 copy_attributes(papi_attribute_t ***result, papi_attribute_t **attributes)
1043 {
1044 	int i;
1045 
1046 	if ((result == NULL) || (attributes == NULL))
1047 		return;
1048 
1049 	for (i = 0; attributes[i] != NULL; i++)
1050 		copy_attribute(result, attributes[i]);
1051 }
1052 
1053 void
1054 split_and_copy_attributes(char **list, papi_attribute_t **attributes,
1055     papi_attribute_t ***in, papi_attribute_t ***out)
1056 {
1057 	int i;
1058 
1059 	if ((list == NULL) || (attributes == NULL))
1060 		return;
1061 
1062 	for (i = 0; attributes[i] != NULL; i++)
1063 		if (is_in_list(attributes[i]->name, list) == 0)
1064 			copy_attribute(in, attributes[i]);
1065 		else
1066 			copy_attribute(out, attributes[i]);
1067 }
1068 
1069 void
1070 papiAttributeListPrint(FILE *fp, papi_attribute_t **attributes,
1071     char *prefix_fmt, ...)
1072 {
1073 	char *prefix = NULL;
1074 	char *buffer = NULL;
1075 	char *newfmt = NULL;
1076 	void *mem;
1077 	ssize_t size = 0;
1078 	va_list ap;
1079 
1080 	newfmt = malloc(strlen(prefix_fmt) + 2);
1081 	sprintf(newfmt, "\n%s", prefix_fmt);
1082 
1083 	va_start(ap, prefix_fmt);
1084 	while (vsnprintf(prefix, size, newfmt, ap) > size) {
1085 		size += 1024;
1086 		mem = realloc(prefix, size);
1087 		if (!mem) goto error;
1088 		prefix = mem;
1089 	}
1090 	va_end(ap);
1091 
1092 	if (attributes) {
1093 		size = 0;
1094 		while (papiAttributeListToString(attributes, prefix, buffer,
1095 		    size) != PAPI_OK) {
1096 			size += 1024;
1097 			mem = realloc(buffer, size);
1098 			if (!mem) goto error;
1099 			buffer = mem;
1100 		}
1101 	}
1102 
1103 	fprintf(fp, "%s%s\n", prefix, buffer ? buffer : "");
1104 	fflush(fp);
1105 
1106 error:
1107 	free(newfmt);
1108 	free(prefix);
1109 	free(buffer);
1110 }
1111