xref: /onnv-gate/usr/src/lib/libtecla/common/stringrp.c (revision 0:68f95e015346)
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  */
_new_StringGroup(int segment_size)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  */
_del_StringGroup(StringGroup * sg)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  */
_sg_store_string(StringGroup * sg,const char * string,int remove_escapes)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  */
_sg_alloc_string(StringGroup * sg,int length)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  */
_clr_StringGroup(StringGroup * sg)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