1fb2f18f8Sesaxe /* 2fb2f18f8Sesaxe * CDDL HEADER START 3fb2f18f8Sesaxe * 4fb2f18f8Sesaxe * The contents of this file are subject to the terms of the 5fb2f18f8Sesaxe * Common Development and Distribution License (the "License"). 6fb2f18f8Sesaxe * You may not use this file except in compliance with the License. 7fb2f18f8Sesaxe * 8fb2f18f8Sesaxe * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9fb2f18f8Sesaxe * or http://www.opensolaris.org/os/licensing. 10fb2f18f8Sesaxe * See the License for the specific language governing permissions 11fb2f18f8Sesaxe * and limitations under the License. 12fb2f18f8Sesaxe * 13fb2f18f8Sesaxe * When distributing Covered Code, include this CDDL HEADER in each 14fb2f18f8Sesaxe * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15fb2f18f8Sesaxe * If applicable, add the following below this CDDL HEADER, with the 16fb2f18f8Sesaxe * fields enclosed by brackets "[]" replaced with your own identifying 17fb2f18f8Sesaxe * information: Portions Copyright [yyyy] [name of copyright owner] 18fb2f18f8Sesaxe * 19fb2f18f8Sesaxe * CDDL HEADER END 20fb2f18f8Sesaxe */ 21fb2f18f8Sesaxe /* 220e751525SEric Saxe * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23fb2f18f8Sesaxe * Use is subject to license terms. 24fb2f18f8Sesaxe */ 25fb2f18f8Sesaxe 26fb2f18f8Sesaxe #include <sys/systm.h> 27fb2f18f8Sesaxe #include <sys/param.h> 28fb2f18f8Sesaxe #include <sys/debug.h> 29fb2f18f8Sesaxe #include <sys/kmem.h> 30fb2f18f8Sesaxe #include <sys/group.h> 31*b885580bSAlexander Kolbasov #include <sys/cmn_err.h> 32fb2f18f8Sesaxe 33fb2f18f8Sesaxe 34fb2f18f8Sesaxe #define GRP_SET_SIZE_DEFAULT 2 35fb2f18f8Sesaxe 36fb2f18f8Sesaxe static void group_grow_set(group_t *); 37fb2f18f8Sesaxe static void group_shrink_set(group_t *); 38fb2f18f8Sesaxe static void group_pack_set(void **, uint_t); 39fb2f18f8Sesaxe 40fb2f18f8Sesaxe /* 41fb2f18f8Sesaxe * Initialize a group_t 42fb2f18f8Sesaxe */ 43fb2f18f8Sesaxe void 44fb2f18f8Sesaxe group_create(group_t *g) 45fb2f18f8Sesaxe { 46fb2f18f8Sesaxe bzero(g, sizeof (group_t)); 47fb2f18f8Sesaxe } 48fb2f18f8Sesaxe 49fb2f18f8Sesaxe /* 50fb2f18f8Sesaxe * Destroy a group_t 51fb2f18f8Sesaxe * The group must already be empty 52fb2f18f8Sesaxe */ 53fb2f18f8Sesaxe void 54fb2f18f8Sesaxe group_destroy(group_t *g) 55fb2f18f8Sesaxe { 56fb2f18f8Sesaxe ASSERT(g->grp_size == 0); 57fb2f18f8Sesaxe 58fb2f18f8Sesaxe if (g->grp_capacity > 0) { 59fb2f18f8Sesaxe kmem_free(g->grp_set, g->grp_capacity * sizeof (void *)); 60fb2f18f8Sesaxe g->grp_capacity = 0; 61fb2f18f8Sesaxe } 62fb2f18f8Sesaxe g->grp_set = NULL; 63fb2f18f8Sesaxe } 64fb2f18f8Sesaxe 65fb2f18f8Sesaxe /* 660e751525SEric Saxe * Empty a group_t 670e751525SEric Saxe * Capacity is preserved. 680e751525SEric Saxe */ 690e751525SEric Saxe void 700e751525SEric Saxe group_empty(group_t *g) 710e751525SEric Saxe { 720e751525SEric Saxe int i; 730e751525SEric Saxe int sz = g->grp_size; 740e751525SEric Saxe 750e751525SEric Saxe g->grp_size = 0; 760e751525SEric Saxe for (i = 0; i < sz; i++) 770e751525SEric Saxe g->grp_set[i] = NULL; 780e751525SEric Saxe } 790e751525SEric Saxe 800e751525SEric Saxe /* 81fb2f18f8Sesaxe * Add element "e" to group "g" 82fb2f18f8Sesaxe * 83fb2f18f8Sesaxe * Returns -1 if addition would result in overcapacity, and 84fb2f18f8Sesaxe * resize operations aren't allowed, and 0 otherwise 85fb2f18f8Sesaxe */ 86fb2f18f8Sesaxe int 87fb2f18f8Sesaxe group_add(group_t *g, void *e, int gflag) 88fb2f18f8Sesaxe { 89fb2f18f8Sesaxe int entry; 90fb2f18f8Sesaxe 91fb2f18f8Sesaxe if ((gflag & GRP_NORESIZE) && 92fb2f18f8Sesaxe g->grp_size == g->grp_capacity) 93fb2f18f8Sesaxe return (-1); 94fb2f18f8Sesaxe 95fb2f18f8Sesaxe ASSERT(g->grp_size != g->grp_capacity || (gflag & GRP_RESIZE)); 96fb2f18f8Sesaxe 97fb2f18f8Sesaxe entry = g->grp_size++; 98fb2f18f8Sesaxe if (g->grp_size > g->grp_capacity) 99fb2f18f8Sesaxe group_grow_set(g); 100fb2f18f8Sesaxe 101fb2f18f8Sesaxe ASSERT(g->grp_set[entry] == NULL); 102fb2f18f8Sesaxe g->grp_set[entry] = e; 103fb2f18f8Sesaxe 104fb2f18f8Sesaxe return (0); 105fb2f18f8Sesaxe } 106fb2f18f8Sesaxe 107fb2f18f8Sesaxe /* 108fb2f18f8Sesaxe * Remove element "e" from group "g" 109fb2f18f8Sesaxe * 110fb2f18f8Sesaxe * Returns -1 if "e" was not present in "g" and 0 otherwise 111fb2f18f8Sesaxe */ 112fb2f18f8Sesaxe int 113fb2f18f8Sesaxe group_remove(group_t *g, void *e, int gflag) 114fb2f18f8Sesaxe { 115fb2f18f8Sesaxe int i; 116fb2f18f8Sesaxe 1171a77c24bSEric Saxe if (g->grp_size == 0) 1181a77c24bSEric Saxe return (-1); 1191a77c24bSEric Saxe 120fb2f18f8Sesaxe /* 121fb2f18f8Sesaxe * Find the element in the group's set 122fb2f18f8Sesaxe */ 123fb2f18f8Sesaxe for (i = 0; i < g->grp_size; i++) 124fb2f18f8Sesaxe if (g->grp_set[i] == e) 125fb2f18f8Sesaxe break; 126fb2f18f8Sesaxe if (g->grp_set[i] != e) 127fb2f18f8Sesaxe return (-1); 128fb2f18f8Sesaxe 129fb2f18f8Sesaxe g->grp_set[i] = NULL; 130fb2f18f8Sesaxe group_pack_set(g->grp_set, g->grp_size); 131fb2f18f8Sesaxe g->grp_size--; 132fb2f18f8Sesaxe 133fb2f18f8Sesaxe if ((gflag & GRP_RESIZE) && 134fb2f18f8Sesaxe g->grp_size > GRP_SET_SIZE_DEFAULT && 135fb2f18f8Sesaxe ((g->grp_size - 1) & g->grp_size) == 0) 136fb2f18f8Sesaxe group_shrink_set(g); 137fb2f18f8Sesaxe 138fb2f18f8Sesaxe return (0); 139fb2f18f8Sesaxe } 140fb2f18f8Sesaxe 141fb2f18f8Sesaxe /* 142fb2f18f8Sesaxe * Expand the capacity of group "g" so that it may 143fb2f18f8Sesaxe * contain at least "n" elements 144fb2f18f8Sesaxe */ 145fb2f18f8Sesaxe void 146fb2f18f8Sesaxe group_expand(group_t *g, uint_t n) 147fb2f18f8Sesaxe { 148fb2f18f8Sesaxe while (g->grp_capacity < n) 149fb2f18f8Sesaxe group_grow_set(g); 150fb2f18f8Sesaxe } 151fb2f18f8Sesaxe 152fb2f18f8Sesaxe /* 153fb2f18f8Sesaxe * Upsize a group's holding capacity 154fb2f18f8Sesaxe */ 155fb2f18f8Sesaxe static void 156fb2f18f8Sesaxe group_grow_set(group_t *g) 157fb2f18f8Sesaxe { 158fb2f18f8Sesaxe uint_t cap_old, cap_new; 159fb2f18f8Sesaxe void **set_old, **set_new; 160fb2f18f8Sesaxe 161fb2f18f8Sesaxe cap_old = g->grp_capacity; 162fb2f18f8Sesaxe set_old = g->grp_set; 163fb2f18f8Sesaxe 164fb2f18f8Sesaxe /* 165fb2f18f8Sesaxe * The array size grows in powers of two 166fb2f18f8Sesaxe */ 167fb2f18f8Sesaxe if ((cap_new = (cap_old << 1)) == 0) { 168fb2f18f8Sesaxe /* 169fb2f18f8Sesaxe * The set is unallocated. 170fb2f18f8Sesaxe * Allocate a default sized set. 171fb2f18f8Sesaxe */ 172fb2f18f8Sesaxe cap_new = GRP_SET_SIZE_DEFAULT; 173fb2f18f8Sesaxe g->grp_set = kmem_zalloc(cap_new * sizeof (void *), KM_SLEEP); 174fb2f18f8Sesaxe g->grp_capacity = cap_new; 175fb2f18f8Sesaxe } else { 176fb2f18f8Sesaxe /* 177fb2f18f8Sesaxe * Allocate a newly sized array, 178fb2f18f8Sesaxe * copy the data, and free the old array. 179fb2f18f8Sesaxe */ 180fb2f18f8Sesaxe set_new = kmem_zalloc(cap_new * sizeof (void *), KM_SLEEP); 181fb2f18f8Sesaxe (void) kcopy(set_old, set_new, cap_old * sizeof (void *)); 182fb2f18f8Sesaxe g->grp_set = set_new; 183fb2f18f8Sesaxe g->grp_capacity = cap_new; 184fb2f18f8Sesaxe kmem_free(set_old, cap_old * sizeof (void *)); 185fb2f18f8Sesaxe } 186fb2f18f8Sesaxe /* 187fb2f18f8Sesaxe * The new array size should be a power of two 188fb2f18f8Sesaxe */ 189fb2f18f8Sesaxe ASSERT(((cap_new - 1) & cap_new) == 0); 190fb2f18f8Sesaxe } 191fb2f18f8Sesaxe 192fb2f18f8Sesaxe /* 193fb2f18f8Sesaxe * Downsize a group's holding capacity 194fb2f18f8Sesaxe */ 195fb2f18f8Sesaxe static void 196fb2f18f8Sesaxe group_shrink_set(group_t *g) 197fb2f18f8Sesaxe { 198fb2f18f8Sesaxe uint_t cap_old, cap_new; 199fb2f18f8Sesaxe void **set_old, **set_new; 200fb2f18f8Sesaxe 201fb2f18f8Sesaxe cap_old = g->grp_capacity; 202fb2f18f8Sesaxe set_old = g->grp_set; 203fb2f18f8Sesaxe 204fb2f18f8Sesaxe /* 205fb2f18f8Sesaxe * The group's existing array size must already 206fb2f18f8Sesaxe * be a power of two 207fb2f18f8Sesaxe */ 208fb2f18f8Sesaxe ASSERT(((cap_old - 1) & cap_old) == 0); 209fb2f18f8Sesaxe cap_new = cap_old >> 1; 210fb2f18f8Sesaxe 211fb2f18f8Sesaxe /* 212fb2f18f8Sesaxe * GRP_SET_SIZE_DEFAULT is the minumum set size. 213fb2f18f8Sesaxe */ 214fb2f18f8Sesaxe if (cap_new < GRP_SET_SIZE_DEFAULT) 215fb2f18f8Sesaxe return; 216fb2f18f8Sesaxe 217fb2f18f8Sesaxe set_new = kmem_zalloc(cap_new * sizeof (void *), KM_SLEEP); 218fb2f18f8Sesaxe (void) kcopy(set_old, set_new, cap_new * sizeof (void *)); 219fb2f18f8Sesaxe g->grp_capacity = cap_new; 220fb2f18f8Sesaxe g->grp_set = set_new; 221fb2f18f8Sesaxe 222fb2f18f8Sesaxe ASSERT(((cap_new - 1) & cap_new) == 0); 223fb2f18f8Sesaxe kmem_free(set_old, cap_old * sizeof (void *)); 224fb2f18f8Sesaxe } 225fb2f18f8Sesaxe 226fb2f18f8Sesaxe /* 227fb2f18f8Sesaxe * Pack a group's set 228fb2f18f8Sesaxe * Element order is not preserved 229fb2f18f8Sesaxe */ 230fb2f18f8Sesaxe static void 231fb2f18f8Sesaxe group_pack_set(void **set, uint_t sz) 232fb2f18f8Sesaxe { 233fb2f18f8Sesaxe uint_t i, j, free; 234fb2f18f8Sesaxe 235fb2f18f8Sesaxe free = (uint_t)-1; 236fb2f18f8Sesaxe 237fb2f18f8Sesaxe for (i = 0; i < sz; i++) { 238fb2f18f8Sesaxe if (set[i] == NULL && free == (uint_t)-1) { 239fb2f18f8Sesaxe /* 240fb2f18f8Sesaxe * Found a new free slot. 241fb2f18f8Sesaxe * Start packing from here. 242fb2f18f8Sesaxe */ 243fb2f18f8Sesaxe free = i; 244fb2f18f8Sesaxe } else if (set[i] != NULL && free != (uint_t)-1) { 245fb2f18f8Sesaxe /* 246fb2f18f8Sesaxe * Found a slot to pack into 247fb2f18f8Sesaxe * an earlier free slot. 248fb2f18f8Sesaxe */ 249fb2f18f8Sesaxe ASSERT(set[free] == NULL); 250fb2f18f8Sesaxe set[free] = set[i]; 251fb2f18f8Sesaxe set[i] = NULL; 252fb2f18f8Sesaxe 253fb2f18f8Sesaxe /* 254fb2f18f8Sesaxe * Find the next free slot 255fb2f18f8Sesaxe */ 256fb2f18f8Sesaxe for (j = free + 1; set[j] != NULL; j++) { 257fb2f18f8Sesaxe ASSERT(j <= i); 258fb2f18f8Sesaxe if (j == i) 259fb2f18f8Sesaxe break; 260fb2f18f8Sesaxe } 261fb2f18f8Sesaxe if (set[j] == NULL) 262fb2f18f8Sesaxe free = j; 263fb2f18f8Sesaxe else 264fb2f18f8Sesaxe free = (uint_t)-1; 265fb2f18f8Sesaxe } 266fb2f18f8Sesaxe } 267fb2f18f8Sesaxe } 268fb2f18f8Sesaxe 269fb2f18f8Sesaxe /* 270fb2f18f8Sesaxe * Initialize a group iterator cookie 271fb2f18f8Sesaxe */ 272fb2f18f8Sesaxe void 273fb2f18f8Sesaxe group_iter_init(group_iter_t *iter) 274fb2f18f8Sesaxe { 275fb2f18f8Sesaxe *iter = 0; 276fb2f18f8Sesaxe } 277fb2f18f8Sesaxe 278fb2f18f8Sesaxe /* 279fb2f18f8Sesaxe * Iterate over the elements in a group 280fb2f18f8Sesaxe */ 281fb2f18f8Sesaxe void * 282fb2f18f8Sesaxe group_iterate(group_t *g, group_iter_t *iter) 283fb2f18f8Sesaxe { 284fb2f18f8Sesaxe uint_t idx = *iter; 285fb2f18f8Sesaxe void *data = NULL; 286fb2f18f8Sesaxe 287fb2f18f8Sesaxe while (idx < g->grp_size) { 288fb2f18f8Sesaxe data = g->grp_set[idx++]; 289fb2f18f8Sesaxe if (data != NULL) 290fb2f18f8Sesaxe break; 291fb2f18f8Sesaxe } 292fb2f18f8Sesaxe *iter = idx; 293fb2f18f8Sesaxe 294fb2f18f8Sesaxe return (data); 295fb2f18f8Sesaxe } 296fb2f18f8Sesaxe 297fb2f18f8Sesaxe /* 298fb2f18f8Sesaxe * Indexed access to a group's elements 299fb2f18f8Sesaxe */ 300fb2f18f8Sesaxe void * 301fb2f18f8Sesaxe group_access_at(group_t *g, uint_t idx) 302fb2f18f8Sesaxe { 303fb2f18f8Sesaxe if (idx >= g->grp_capacity) 304fb2f18f8Sesaxe return (NULL); 305fb2f18f8Sesaxe 306fb2f18f8Sesaxe return (g->grp_set[idx]); 307fb2f18f8Sesaxe } 308fb2f18f8Sesaxe 309fb2f18f8Sesaxe /* 310fb2f18f8Sesaxe * Add a new ordered group element at specified 311fb2f18f8Sesaxe * index. The group must already be of sufficient 312fb2f18f8Sesaxe * capacity to hold an element at the specified index. 313fb2f18f8Sesaxe * 314fb2f18f8Sesaxe * Returns 0 if addition was sucessful, and -1 if the 315fb2f18f8Sesaxe * addition failed because the table was too small 316fb2f18f8Sesaxe */ 317fb2f18f8Sesaxe int 318fb2f18f8Sesaxe group_add_at(group_t *g, void *e, uint_t idx) 319fb2f18f8Sesaxe { 320fb2f18f8Sesaxe if (idx >= g->grp_capacity) 321fb2f18f8Sesaxe return (-1); 322fb2f18f8Sesaxe 323fb2f18f8Sesaxe if (idx >= g->grp_size) 324fb2f18f8Sesaxe g->grp_size = idx + 1; 325fb2f18f8Sesaxe 326fb2f18f8Sesaxe ASSERT(g->grp_set[idx] == NULL); 327fb2f18f8Sesaxe g->grp_set[idx] = e; 328fb2f18f8Sesaxe return (0); 329fb2f18f8Sesaxe } 330fb2f18f8Sesaxe 331fb2f18f8Sesaxe /* 3320e751525SEric Saxe * Remove the element at the specified index 333fb2f18f8Sesaxe */ 334fb2f18f8Sesaxe void 335fb2f18f8Sesaxe group_remove_at(group_t *g, uint_t idx) 336fb2f18f8Sesaxe { 337fb2f18f8Sesaxe ASSERT(idx < g->grp_capacity); 338fb2f18f8Sesaxe g->grp_set[idx] = NULL; 339fb2f18f8Sesaxe } 3400e751525SEric Saxe 3410e751525SEric Saxe /* 3420e751525SEric Saxe * Find an element in the group, and return its index 3430e751525SEric Saxe * Returns -1 if the element could not be found. 3440e751525SEric Saxe */ 3450e751525SEric Saxe uint_t 3460e751525SEric Saxe group_find(group_t *g, void *e) 3470e751525SEric Saxe { 3480e751525SEric Saxe uint_t idx; 3490e751525SEric Saxe 3500e751525SEric Saxe for (idx = 0; idx < g->grp_capacity; idx++) { 3510e751525SEric Saxe if (g->grp_set[idx] == e) 3520e751525SEric Saxe return (idx); 3530e751525SEric Saxe } 3540e751525SEric Saxe return ((uint_t)-1); 3550e751525SEric Saxe } 356*b885580bSAlexander Kolbasov 357*b885580bSAlexander Kolbasov /* 358*b885580bSAlexander Kolbasov * Return a string in a given buffer with list of integer entries in a group. 359*b885580bSAlexander Kolbasov * The string concatenates consecutive integer ranges ax x-y. 360*b885580bSAlexander Kolbasov * The resulting string looks like "1,2-5,8" 361*b885580bSAlexander Kolbasov * 362*b885580bSAlexander Kolbasov * The convert argument is used to map group elements to integer IDs. 363*b885580bSAlexander Kolbasov */ 364*b885580bSAlexander Kolbasov char * 365*b885580bSAlexander Kolbasov group2intlist(group_t *group, char *buffer, size_t len, int (convert)(void*)) 366*b885580bSAlexander Kolbasov { 367*b885580bSAlexander Kolbasov char *ptr = buffer; 368*b885580bSAlexander Kolbasov void *v; 369*b885580bSAlexander Kolbasov group_iter_t iter; 370*b885580bSAlexander Kolbasov boolean_t first_iteration = B_TRUE; 371*b885580bSAlexander Kolbasov boolean_t first_value = B_TRUE; 372*b885580bSAlexander Kolbasov int start = 0, end = 0; 373*b885580bSAlexander Kolbasov 374*b885580bSAlexander Kolbasov /* 375*b885580bSAlexander Kolbasov * Allow for the terminating NULL-byte 376*b885580bSAlexander Kolbasov */ 377*b885580bSAlexander Kolbasov len = len -1; 378*b885580bSAlexander Kolbasov 379*b885580bSAlexander Kolbasov group_iter_init(&iter); 380*b885580bSAlexander Kolbasov while ((v = group_iterate(group, &iter)) != NULL && len > 0) { 381*b885580bSAlexander Kolbasov int id = convert(v); 382*b885580bSAlexander Kolbasov int nbytes = 0; 383*b885580bSAlexander Kolbasov 384*b885580bSAlexander Kolbasov if (first_iteration) { 385*b885580bSAlexander Kolbasov start = end = id; 386*b885580bSAlexander Kolbasov first_iteration = B_FALSE; 387*b885580bSAlexander Kolbasov } else if (end + 1 == id) { 388*b885580bSAlexander Kolbasov /* 389*b885580bSAlexander Kolbasov * Got consecutive ID, so extend end of range without 390*b885580bSAlexander Kolbasov * doing anything since the range may extend further 391*b885580bSAlexander Kolbasov */ 392*b885580bSAlexander Kolbasov end = id; 393*b885580bSAlexander Kolbasov } else { 394*b885580bSAlexander Kolbasov if (first_value) { 395*b885580bSAlexander Kolbasov first_value = B_FALSE; 396*b885580bSAlexander Kolbasov } else { 397*b885580bSAlexander Kolbasov *ptr++ = ','; 398*b885580bSAlexander Kolbasov len--; 399*b885580bSAlexander Kolbasov } 400*b885580bSAlexander Kolbasov 401*b885580bSAlexander Kolbasov if (len == 0) 402*b885580bSAlexander Kolbasov break; 403*b885580bSAlexander Kolbasov 404*b885580bSAlexander Kolbasov /* 405*b885580bSAlexander Kolbasov * Next ID is not consecutive, so dump IDs gotten so 406*b885580bSAlexander Kolbasov * far. 407*b885580bSAlexander Kolbasov */ 408*b885580bSAlexander Kolbasov if (end > start + 1) /* range */ 409*b885580bSAlexander Kolbasov nbytes = snprintf(ptr, len, "%d-%d", 410*b885580bSAlexander Kolbasov start, end); 411*b885580bSAlexander Kolbasov else if (end > start) /* different values */ 412*b885580bSAlexander Kolbasov nbytes = snprintf(ptr, len, "%d,%d", 413*b885580bSAlexander Kolbasov start, end); 414*b885580bSAlexander Kolbasov else /* same value */ 415*b885580bSAlexander Kolbasov nbytes = snprintf(ptr, len, "%d", start); 416*b885580bSAlexander Kolbasov 417*b885580bSAlexander Kolbasov if (nbytes <= 0) { 418*b885580bSAlexander Kolbasov len = 0; 419*b885580bSAlexander Kolbasov break; 420*b885580bSAlexander Kolbasov } 421*b885580bSAlexander Kolbasov 422*b885580bSAlexander Kolbasov /* 423*b885580bSAlexander Kolbasov * Advance position in the string 424*b885580bSAlexander Kolbasov */ 425*b885580bSAlexander Kolbasov ptr += nbytes; 426*b885580bSAlexander Kolbasov len -= nbytes; 427*b885580bSAlexander Kolbasov 428*b885580bSAlexander Kolbasov /* 429*b885580bSAlexander Kolbasov * Try finding consecutive range starting from current 430*b885580bSAlexander Kolbasov * ID. 431*b885580bSAlexander Kolbasov */ 432*b885580bSAlexander Kolbasov start = end = id; 433*b885580bSAlexander Kolbasov } 434*b885580bSAlexander Kolbasov } 435*b885580bSAlexander Kolbasov 436*b885580bSAlexander Kolbasov if (!first_value) { 437*b885580bSAlexander Kolbasov *ptr++ = ','; 438*b885580bSAlexander Kolbasov len--; 439*b885580bSAlexander Kolbasov } 440*b885580bSAlexander Kolbasov /* 441*b885580bSAlexander Kolbasov * Print last ID(s) 442*b885580bSAlexander Kolbasov */ 443*b885580bSAlexander Kolbasov if (len > 0) { 444*b885580bSAlexander Kolbasov if (end > start + 1) { 445*b885580bSAlexander Kolbasov (void) snprintf(ptr, len, "%d-%d", start, end); 446*b885580bSAlexander Kolbasov } else if (end != start) { 447*b885580bSAlexander Kolbasov (void) snprintf(ptr, len, "%d,%d", start, end); 448*b885580bSAlexander Kolbasov } else { 449*b885580bSAlexander Kolbasov (void) snprintf(ptr, len, "%d", start); 450*b885580bSAlexander Kolbasov } 451*b885580bSAlexander Kolbasov } 452*b885580bSAlexander Kolbasov 453*b885580bSAlexander Kolbasov return (buffer); 454*b885580bSAlexander Kolbasov } 455