xref: /titanic_51/usr/src/lib/libtecla/common/stringrp.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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  */
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  */
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  */
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  */
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  */
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