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