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