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