1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd. 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * All rights reserved. 5*0Sstevel@tonic-gate * 6*0Sstevel@tonic-gate * Permission is hereby granted, free of charge, to any person obtaining a 7*0Sstevel@tonic-gate * copy of this software and associated documentation files (the 8*0Sstevel@tonic-gate * "Software"), to deal in the Software without restriction, including 9*0Sstevel@tonic-gate * without limitation the rights to use, copy, modify, merge, publish, 10*0Sstevel@tonic-gate * distribute, and/or sell copies of the Software, and to permit persons 11*0Sstevel@tonic-gate * to whom the Software is furnished to do so, provided that the above 12*0Sstevel@tonic-gate * copyright notice(s) and this permission notice appear in all copies of 13*0Sstevel@tonic-gate * the Software and that both the above copyright notice(s) and this 14*0Sstevel@tonic-gate * permission notice appear in supporting documentation. 15*0Sstevel@tonic-gate * 16*0Sstevel@tonic-gate * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17*0Sstevel@tonic-gate * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18*0Sstevel@tonic-gate * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 19*0Sstevel@tonic-gate * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 20*0Sstevel@tonic-gate * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL 21*0Sstevel@tonic-gate * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING 22*0Sstevel@tonic-gate * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, 23*0Sstevel@tonic-gate * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 24*0Sstevel@tonic-gate * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 25*0Sstevel@tonic-gate * 26*0Sstevel@tonic-gate * Except as contained in this notice, the name of a copyright holder 27*0Sstevel@tonic-gate * shall not be used in advertising or otherwise to promote the sale, use 28*0Sstevel@tonic-gate * or other dealings in this Software without prior written authorization 29*0Sstevel@tonic-gate * of the copyright holder. 30*0Sstevel@tonic-gate */ 31*0Sstevel@tonic-gate 32*0Sstevel@tonic-gate /* 33*0Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 34*0Sstevel@tonic-gate * Use is subject to license terms. 35*0Sstevel@tonic-gate */ 36*0Sstevel@tonic-gate 37*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 38*0Sstevel@tonic-gate 39*0Sstevel@tonic-gate #include <stdlib.h> 40*0Sstevel@tonic-gate #include <stdio.h> 41*0Sstevel@tonic-gate #include <string.h> 42*0Sstevel@tonic-gate #include <errno.h> 43*0Sstevel@tonic-gate 44*0Sstevel@tonic-gate #include "freelist.h" 45*0Sstevel@tonic-gate #include "stringrp.h" 46*0Sstevel@tonic-gate 47*0Sstevel@tonic-gate /* 48*0Sstevel@tonic-gate * StringSegment objects store lots of small strings in larger 49*0Sstevel@tonic-gate * character arrays. Since the total length of all of the strings can't 50*0Sstevel@tonic-gate * be known in advance, an extensible list of large character arrays, 51*0Sstevel@tonic-gate * called string-segments are used. 52*0Sstevel@tonic-gate */ 53*0Sstevel@tonic-gate typedef struct StringSegment StringSegment; 54*0Sstevel@tonic-gate struct StringSegment { 55*0Sstevel@tonic-gate StringSegment *next; /* A pointer to the next segment in the list */ 56*0Sstevel@tonic-gate char *block; /* An array of characters to be shared between strings */ 57*0Sstevel@tonic-gate int unused; /* The amount of unused space at the end of block[] */ 58*0Sstevel@tonic-gate }; 59*0Sstevel@tonic-gate 60*0Sstevel@tonic-gate /* 61*0Sstevel@tonic-gate * StringGroup is typedef'd in stringrp.h. 62*0Sstevel@tonic-gate */ 63*0Sstevel@tonic-gate struct StringGroup { 64*0Sstevel@tonic-gate FreeList *node_mem; /* The StringSegment free-list */ 65*0Sstevel@tonic-gate int block_size; /* The dimension of each character array block */ 66*0Sstevel@tonic-gate StringSegment *head; /* The list of character arrays */ 67*0Sstevel@tonic-gate }; 68*0Sstevel@tonic-gate 69*0Sstevel@tonic-gate /* 70*0Sstevel@tonic-gate * Specify how many StringSegment's to allocate at a time. 71*0Sstevel@tonic-gate */ 72*0Sstevel@tonic-gate #define STR_SEG_BLK 20 73*0Sstevel@tonic-gate 74*0Sstevel@tonic-gate /*....................................................................... 75*0Sstevel@tonic-gate * Create a new StringGroup object. 76*0Sstevel@tonic-gate * 77*0Sstevel@tonic-gate * Input: 78*0Sstevel@tonic-gate * segment_size int The length of each of the large character 79*0Sstevel@tonic-gate * arrays in which multiple strings will be 80*0Sstevel@tonic-gate * stored. This sets the length of longest 81*0Sstevel@tonic-gate * string that can be stored, and for efficiency 82*0Sstevel@tonic-gate * should be at least 10 times as large as 83*0Sstevel@tonic-gate * the average string that will be stored. 84*0Sstevel@tonic-gate * Output: 85*0Sstevel@tonic-gate * return StringGroup * The new object, or NULL on error. 86*0Sstevel@tonic-gate */ 87*0Sstevel@tonic-gate StringGroup *_new_StringGroup(int segment_size) 88*0Sstevel@tonic-gate { 89*0Sstevel@tonic-gate StringGroup *sg; /* The object to be returned */ 90*0Sstevel@tonic-gate /* 91*0Sstevel@tonic-gate * Check the arguments. 92*0Sstevel@tonic-gate */ 93*0Sstevel@tonic-gate if(segment_size < 1) { 94*0Sstevel@tonic-gate errno = EINVAL; 95*0Sstevel@tonic-gate return NULL; 96*0Sstevel@tonic-gate }; 97*0Sstevel@tonic-gate /* 98*0Sstevel@tonic-gate * Allocate the container. 99*0Sstevel@tonic-gate */ 100*0Sstevel@tonic-gate sg = (StringGroup *) malloc(sizeof(StringGroup)); 101*0Sstevel@tonic-gate if(!sg) { 102*0Sstevel@tonic-gate errno = ENOMEM; 103*0Sstevel@tonic-gate return NULL; 104*0Sstevel@tonic-gate }; 105*0Sstevel@tonic-gate /* 106*0Sstevel@tonic-gate * Before attempting any operation that might fail, initialize the 107*0Sstevel@tonic-gate * container at least up to the point at which it can safely be passed 108*0Sstevel@tonic-gate * to _del_StringGroup(). 109*0Sstevel@tonic-gate */ 110*0Sstevel@tonic-gate sg->node_mem = NULL; 111*0Sstevel@tonic-gate sg->head = NULL; 112*0Sstevel@tonic-gate sg->block_size = segment_size; 113*0Sstevel@tonic-gate /* 114*0Sstevel@tonic-gate * Allocate the free list that is used to allocate list nodes. 115*0Sstevel@tonic-gate */ 116*0Sstevel@tonic-gate sg->node_mem = _new_FreeList(sizeof(StringSegment), STR_SEG_BLK); 117*0Sstevel@tonic-gate if(!sg->node_mem) 118*0Sstevel@tonic-gate return _del_StringGroup(sg); 119*0Sstevel@tonic-gate return sg; 120*0Sstevel@tonic-gate } 121*0Sstevel@tonic-gate 122*0Sstevel@tonic-gate /*....................................................................... 123*0Sstevel@tonic-gate * Delete a StringGroup object. 124*0Sstevel@tonic-gate * 125*0Sstevel@tonic-gate * Input: 126*0Sstevel@tonic-gate * sg StringGroup * The object to be deleted. 127*0Sstevel@tonic-gate * Output: 128*0Sstevel@tonic-gate * return StringGroup * The deleted object (always NULL). 129*0Sstevel@tonic-gate */ 130*0Sstevel@tonic-gate StringGroup *_del_StringGroup(StringGroup *sg) 131*0Sstevel@tonic-gate { 132*0Sstevel@tonic-gate if(sg) { 133*0Sstevel@tonic-gate StringSegment *node; 134*0Sstevel@tonic-gate /* 135*0Sstevel@tonic-gate * Delete the character arrays. 136*0Sstevel@tonic-gate */ 137*0Sstevel@tonic-gate for(node=sg->head; node; node=node->next) { 138*0Sstevel@tonic-gate if(node->block) 139*0Sstevel@tonic-gate free(node->block); 140*0Sstevel@tonic-gate node->block = NULL; 141*0Sstevel@tonic-gate }; 142*0Sstevel@tonic-gate /* 143*0Sstevel@tonic-gate * Delete the list nodes that contained the string segments. 144*0Sstevel@tonic-gate */ 145*0Sstevel@tonic-gate sg->node_mem = _del_FreeList(sg->node_mem, 1); 146*0Sstevel@tonic-gate sg->head = NULL; /* Already deleted by deleting sg->node_mem */ 147*0Sstevel@tonic-gate /* 148*0Sstevel@tonic-gate * Delete the container. 149*0Sstevel@tonic-gate */ 150*0Sstevel@tonic-gate free(sg); 151*0Sstevel@tonic-gate }; 152*0Sstevel@tonic-gate return NULL; 153*0Sstevel@tonic-gate } 154*0Sstevel@tonic-gate 155*0Sstevel@tonic-gate /*....................................................................... 156*0Sstevel@tonic-gate * Make a copy of a string in the specified string group, and return 157*0Sstevel@tonic-gate * a pointer to the copy. 158*0Sstevel@tonic-gate * 159*0Sstevel@tonic-gate * Input: 160*0Sstevel@tonic-gate * sg StringGroup * The group to store the string in. 161*0Sstevel@tonic-gate * string const char * The string to be recorded. 162*0Sstevel@tonic-gate * remove_escapes int If true, omit backslashes which escape 163*0Sstevel@tonic-gate * other characters when making the copy. 164*0Sstevel@tonic-gate * Output: 165*0Sstevel@tonic-gate * return char * The pointer to the copy of the string, 166*0Sstevel@tonic-gate * or NULL if there was insufficient memory. 167*0Sstevel@tonic-gate */ 168*0Sstevel@tonic-gate char *_sg_store_string(StringGroup *sg, const char *string, int remove_escapes) 169*0Sstevel@tonic-gate { 170*0Sstevel@tonic-gate char *copy; /* The recorded copy of string[] */ 171*0Sstevel@tonic-gate size_t len; 172*0Sstevel@tonic-gate /* 173*0Sstevel@tonic-gate * Check the arguments. 174*0Sstevel@tonic-gate */ 175*0Sstevel@tonic-gate if(!sg || !string) 176*0Sstevel@tonic-gate return NULL; 177*0Sstevel@tonic-gate /* 178*0Sstevel@tonic-gate * Get memory for the string. 179*0Sstevel@tonic-gate */ 180*0Sstevel@tonic-gate len = strlen(string); 181*0Sstevel@tonic-gate copy = _sg_alloc_string(sg, len); 182*0Sstevel@tonic-gate if(copy) { 183*0Sstevel@tonic-gate /* 184*0Sstevel@tonic-gate * If needed, remove backslash escapes while copying the input string 185*0Sstevel@tonic-gate * into the cache string. 186*0Sstevel@tonic-gate */ 187*0Sstevel@tonic-gate if(remove_escapes) { 188*0Sstevel@tonic-gate int escaped = 0; /* True if the next character should be */ 189*0Sstevel@tonic-gate /* escaped. */ 190*0Sstevel@tonic-gate const char *src = string; /* A pointer into the input string */ 191*0Sstevel@tonic-gate char *dst = copy; /* A pointer into the cached copy of the */ 192*0Sstevel@tonic-gate /* string. */ 193*0Sstevel@tonic-gate while(*src) { 194*0Sstevel@tonic-gate if(!escaped && *src == '\\') { 195*0Sstevel@tonic-gate escaped = 1; 196*0Sstevel@tonic-gate src++; 197*0Sstevel@tonic-gate } else { 198*0Sstevel@tonic-gate escaped = 0; 199*0Sstevel@tonic-gate *dst++ = *src++; 200*0Sstevel@tonic-gate }; 201*0Sstevel@tonic-gate }; 202*0Sstevel@tonic-gate *dst = '\0'; 203*0Sstevel@tonic-gate /* 204*0Sstevel@tonic-gate * If escapes have already been removed, copy the input string directly 205*0Sstevel@tonic-gate * into the cache. 206*0Sstevel@tonic-gate */ 207*0Sstevel@tonic-gate } else { 208*0Sstevel@tonic-gate strlcpy(copy, string, len + 1); 209*0Sstevel@tonic-gate }; 210*0Sstevel@tonic-gate }; 211*0Sstevel@tonic-gate /* 212*0Sstevel@tonic-gate * Return a pointer to the copy of the string (or NULL if the allocation 213*0Sstevel@tonic-gate * failed). 214*0Sstevel@tonic-gate */ 215*0Sstevel@tonic-gate return copy; 216*0Sstevel@tonic-gate } 217*0Sstevel@tonic-gate 218*0Sstevel@tonic-gate /*....................................................................... 219*0Sstevel@tonic-gate * Allocate memory for a string of a given length. 220*0Sstevel@tonic-gate * 221*0Sstevel@tonic-gate * Input: 222*0Sstevel@tonic-gate * sg StringGroup * The group to store the string in. 223*0Sstevel@tonic-gate * length int The required length of the string. 224*0Sstevel@tonic-gate * Output: 225*0Sstevel@tonic-gate * return char * The pointer to the copy of the string, 226*0Sstevel@tonic-gate * or NULL if there was insufficient memory. 227*0Sstevel@tonic-gate */ 228*0Sstevel@tonic-gate char *_sg_alloc_string(StringGroup *sg, int length) 229*0Sstevel@tonic-gate { 230*0Sstevel@tonic-gate StringSegment *node; /* A node of the list of string segments */ 231*0Sstevel@tonic-gate char *copy; /* The allocated string */ 232*0Sstevel@tonic-gate /* 233*0Sstevel@tonic-gate * If the string is longer than block_size, then we can't record it. 234*0Sstevel@tonic-gate */ 235*0Sstevel@tonic-gate if(length > sg->block_size || length < 0) 236*0Sstevel@tonic-gate return NULL; 237*0Sstevel@tonic-gate /* 238*0Sstevel@tonic-gate * See if there is room to record the string in one of the existing 239*0Sstevel@tonic-gate * string segments. Do this by advancing the node pointer until we find 240*0Sstevel@tonic-gate * a node with length+1 bytes unused, or we get to the end of the list. 241*0Sstevel@tonic-gate */ 242*0Sstevel@tonic-gate for(node=sg->head; node && node->unused <= length; node=node->next) 243*0Sstevel@tonic-gate ; 244*0Sstevel@tonic-gate /* 245*0Sstevel@tonic-gate * If there wasn't room, allocate a new string segment. 246*0Sstevel@tonic-gate */ 247*0Sstevel@tonic-gate if(!node) { 248*0Sstevel@tonic-gate node = (StringSegment *) _new_FreeListNode(sg->node_mem); 249*0Sstevel@tonic-gate if(!node) 250*0Sstevel@tonic-gate return NULL; 251*0Sstevel@tonic-gate /* 252*0Sstevel@tonic-gate * Initialize the segment. 253*0Sstevel@tonic-gate */ 254*0Sstevel@tonic-gate node->next = NULL; 255*0Sstevel@tonic-gate node->block = NULL; 256*0Sstevel@tonic-gate node->unused = sg->block_size; 257*0Sstevel@tonic-gate /* 258*0Sstevel@tonic-gate * Attempt to allocate the string segment character array. 259*0Sstevel@tonic-gate */ 260*0Sstevel@tonic-gate node->block = (char *) malloc(sg->block_size); 261*0Sstevel@tonic-gate if(!node->block) 262*0Sstevel@tonic-gate return NULL; 263*0Sstevel@tonic-gate /* 264*0Sstevel@tonic-gate * Prepend the node to the list. 265*0Sstevel@tonic-gate */ 266*0Sstevel@tonic-gate node->next = sg->head; 267*0Sstevel@tonic-gate sg->head = node; 268*0Sstevel@tonic-gate }; 269*0Sstevel@tonic-gate /* 270*0Sstevel@tonic-gate * Get memory for the string. 271*0Sstevel@tonic-gate */ 272*0Sstevel@tonic-gate copy = node->block + sg->block_size - node->unused; 273*0Sstevel@tonic-gate node->unused -= length + 1; 274*0Sstevel@tonic-gate /* 275*0Sstevel@tonic-gate * Return a pointer to the string memory. 276*0Sstevel@tonic-gate */ 277*0Sstevel@tonic-gate return copy; 278*0Sstevel@tonic-gate } 279*0Sstevel@tonic-gate 280*0Sstevel@tonic-gate /*....................................................................... 281*0Sstevel@tonic-gate * Delete all of the strings that are currently stored by a specified 282*0Sstevel@tonic-gate * StringGroup object. 283*0Sstevel@tonic-gate * 284*0Sstevel@tonic-gate * Input: 285*0Sstevel@tonic-gate * sg StringGroup * The group of strings to clear. 286*0Sstevel@tonic-gate */ 287*0Sstevel@tonic-gate void _clr_StringGroup(StringGroup *sg) 288*0Sstevel@tonic-gate { 289*0Sstevel@tonic-gate StringSegment *node; /* A node in the list of string segments */ 290*0Sstevel@tonic-gate /* 291*0Sstevel@tonic-gate * Mark all of the string segments as unoccupied. 292*0Sstevel@tonic-gate */ 293*0Sstevel@tonic-gate for(node=sg->head; node; node=node->next) 294*0Sstevel@tonic-gate node->unused = sg->block_size; 295*0Sstevel@tonic-gate return; 296*0Sstevel@tonic-gate } 297