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 /* $Id: attribute.c 157 2006-04-26 15:07:55Z ktou $ */
  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(const 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                 const 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                         const 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, const 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 }