xref: /illumos-gate/usr/src/lib/libmail/common/s_string.c (revision 8a2b682e57a046b828f37bcde1776f131ef4629f)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved  	*/
29 
30 #include <sys/types.h>
31 #include <stdio.h>
32 #include <ctype.h>
33 #include "s_string.h"
34 #include <stdlib.h>
35 
36 /* global to this file */
37 #define	STRLEN 128UL
38 #define	STRALLOC 128UL
39 #define	MAXINCR 250000UL
40 
41 /* buffer pool for allocating string structures */
42 typedef struct {
43 	string s[STRALLOC];
44 	size_t o;
45 } stralloc;
46 static stralloc *freep = NULL;
47 
48 /* pool of freed strings */
49 static string *freed = NULL;
50 static string *s_alloc(void);
51 static void s_simplegrow(string *, size_t);
52 
53 void
54 s_free(string *sp)
55 {
56 	if (sp != NULL) {
57 		sp->ptr = (char *)freed;
58 		freed = sp;
59 	}
60 }
61 
62 /* allocate a string head */
63 static string *
64 s_alloc(void)
65 {
66 	if (freep == NULL || freep->o >= STRALLOC) {
67 		freep = (stralloc *)malloc(sizeof (stralloc));
68 		if (freep == NULL) {
69 			perror("allocating string");
70 			exit(1);
71 		}
72 		freep->o = (size_t)0;
73 	}
74 	return (&(freep->s[freep->o++]));
75 }
76 
77 /* create a new `short' string */
78 string *
79 s_new(void)
80 {
81 	string *sp;
82 
83 	if (freed != NULL) {
84 		sp = freed;
85 		/*LINTED*/
86 		freed = (string *)(freed->ptr);
87 		sp->ptr = sp->base;
88 		return (sp);
89 	}
90 	sp = s_alloc();
91 	sp->base = sp->ptr = malloc(STRLEN);
92 	if (sp->base == NULL) {
93 		perror("allocating string");
94 		exit(1);
95 	}
96 	sp->end = sp->base + STRLEN;
97 	s_terminate(sp);
98 	return (sp);
99 }
100 
101 /* grow a string's allocation by at least `incr' bytes */
102 static void
103 s_simplegrow(string *sp, size_t incr)
104 {
105 	char *cp;
106 	size_t size;
107 
108 	/*
109 	 *  take a larger increment to avoid mallocing too often
110 	 */
111 	if (((sp->end - sp->base) < incr) && (MAXINCR < incr))
112 		size = (sp->end - sp->base) + incr;
113 	else if ((sp->end - sp->base) > MAXINCR)
114 		size = (sp->end - sp->base) + MAXINCR;
115 	else
116 		size = (size_t)2 * (sp->end - sp->base);
117 
118 	cp = realloc(sp->base, size);
119 	if (cp == NULL) {
120 		perror("string:");
121 		exit(1);
122 	}
123 	sp->ptr = (sp->ptr - sp->base) + cp;
124 	sp->end = cp + size;
125 	sp->base = cp;
126 }
127 
128 /* grow a string's allocation */
129 int
130 s_grow(string *sp, int c)
131 {
132 	s_simplegrow(sp, (size_t)2);
133 	s_putc(sp, c);
134 	return (c);
135 }
136 
137 /* return a string containing a character array (this had better not grow) */
138 string *
139 s_array(char *cp, size_t len)
140 {
141 	string *sp = s_alloc();
142 
143 	sp->base = sp->ptr = cp;
144 	sp->end = sp->base + len;
145 	return (sp);
146 }
147 
148 /* return a string containing a copy of the passed char array */
149 string*
150 s_copy(char *cp)
151 {
152 	string *sp;
153 	size_t len;
154 
155 	sp = s_alloc();
156 	len = strlen(cp)+1;
157 	sp->base = malloc(len);
158 	if (sp->base == NULL) {
159 		perror("string:");
160 		exit(1);
161 	}
162 	sp->end = sp->base + len;	/* point past end of allocation */
163 	(void) strcpy(sp->base, cp);
164 	sp->ptr = sp->end - (size_t)1;	/* point to NULL terminator */
165 	return (sp);
166 }
167 
168 /* convert string to lower case */
169 void
170 s_tolower(string *sp)
171 {
172 	char *cp;
173 
174 	for (cp = sp->ptr; *cp; cp++)
175 		*cp = tolower(*cp);
176 }
177 
178 void
179 s_skipwhite(string *sp)
180 {
181 	while (isspace(*sp->ptr))
182 		s_skipc(sp);
183 }
184 
185 /* append a char array to a string */
186 string *
187 s_append(string *to, char *from)
188 {
189 	if (to == NULL)
190 		to = s_new();
191 	if (from == NULL)
192 		return (to);
193 	for (; *from; from++)
194 		s_putc(to, (int)(unsigned int)*from);
195 	s_terminate(to);
196 	return (to);
197 }
198 
199 /*
200  * Append a logical input sequence into a string.  Ignore blank and
201  * comment lines.  Backslash preceding newline indicates continuation.
202  * The `lineortoken' variable indicates whether the sequence to beinput
203  * is a whitespace delimited token or a whole line.
204  *
205  *	FILE *fp;		stream to read from
206  *	string *to;		where to put token
207  *	int lineortoken;	how the sequence terminates
208  *
209  * Returns a pointer to the string or NULL. Trailing newline is stripped off.
210  */
211 string *
212 s_seq_read(FILE *fp, string *to, int lineortoken)
213 {
214 	int c;
215 	int done = 0;
216 
217 	if (feof(fp))
218 		return (NULL);
219 
220 	/* get rid of leading goo */
221 	do {
222 		c = getc(fp);
223 		switch (c) {
224 		case EOF:
225 			if (to != NULL)
226 				s_terminate(to);
227 			return (NULL);
228 		case '#':
229 			/*LINTED*/
230 			while ((c = getc(fp)) != '\n' && c != EOF)
231 				continue;
232 			break;
233 		case ' ':
234 		case '\t':
235 		case '\n':
236 		case '\r':
237 		case '\f':
238 			break;
239 		default:
240 			done = 1;
241 			break;
242 		}
243 	} while (!done);
244 
245 	if (to == NULL)
246 		to = s_new();
247 
248 	/* gather up a sequence */
249 	for (;;) {
250 		switch (c) {
251 		case '\\':
252 			c = getc(fp);
253 			if (c != '\n') {
254 				s_putc(to, (int)(unsigned int)'\\');
255 				s_putc(to, c);
256 			}
257 			break;
258 		case EOF:
259 		case '\r':
260 		case '\f':
261 		case '\n':
262 			s_terminate(to);
263 			return (to);
264 		case ' ':
265 		case '\t':
266 			if (lineortoken == TOKEN) {
267 				s_terminate(to);
268 				return (to);
269 			}
270 			/* fall through */
271 		default:
272 			s_putc(to, c);
273 			break;
274 		}
275 		c = getc(fp);
276 	}
277 }
278 
279 string *
280 s_tok(string *from, char *split)
281 {
282 	char *splitend = strpbrk(from->ptr, split);
283 
284 	if (splitend) {
285 		string *to = s_new();
286 		for (; from->ptr < splitend; ) {
287 			s_putc(to, (int)(unsigned int)*from->ptr);
288 			from->ptr++;
289 		}
290 		s_terminate(to);
291 		s_restart(to);
292 		/* LINT: warning due to lint bug */
293 		from->ptr += strspn(from->ptr, split);
294 		return (to);
295 	}
296 
297 	else if (from->ptr[0]) {
298 		string *to = s_clone(from);
299 		while (*from->ptr)
300 			from->ptr++;
301 		return (to);
302 	}
303 
304 	else
305 		return (NULL);
306 }
307 
308 /*
309  * Append an input line to a string.
310  *
311  * Returns a pointer to the string (or NULL).
312  * Trailing newline is left on.
313  */
314 char *
315 s_read_line(FILE *fp, string *to)
316 {
317 	int c;
318 	size_t len = 0;
319 
320 	s_terminate(to);
321 
322 	/* end of input */
323 	if (feof(fp) || (c = getc(fp)) == EOF)
324 		return (NULL);
325 
326 	/* gather up a line */
327 	for (; ; ) {
328 		len++;
329 		switch (c) {
330 		case EOF:
331 			s_terminate(to);
332 			return (to->ptr - len);
333 		case '\n':
334 			s_putc(to, (int)(unsigned int)'\n');
335 			s_terminate(to);
336 			return (to->ptr - len);
337 		default:
338 			s_putc(to, c);
339 			break;
340 		}
341 		c = getc(fp);
342 	}
343 }
344 
345 /*
346  * Read till eof
347  */
348 size_t
349 s_read_to_eof(FILE *fp, string *to)
350 {
351 	size_t got;
352 	size_t have;
353 
354 	s_terminate(to);
355 
356 	for (; ; ) {
357 		if (feof(fp))
358 			break;
359 		/* allocate room for a full buffer */
360 		have = to->end - to->ptr;
361 		if (have < 4096UL)
362 			s_simplegrow(to, (size_t)4096);
363 
364 		/* get a buffers worth */
365 		have = to->end - to->ptr;
366 		got = fread(to->ptr, (size_t)1, have, fp);
367 		if (got == (size_t)0)
368 			break;
369 		/* LINT: warning due to lint bug */
370 		to->ptr += got;
371 	}
372 
373 	/* null terminate the line */
374 	s_terminate(to);
375 	return (to->ptr - to->base);
376 }
377 
378 /*
379  * Get the next field from a string.  The field is delimited by white space,
380  * single or double quotes.
381  *
382  *	string *from;	string to parse
383  *	string *to;	where to put parsed token
384  */
385 string *
386 s_parse(string *from, string *to)
387 {
388 	while (isspace(*from->ptr))
389 		from->ptr++;
390 	if (*from->ptr == '\0')
391 		return (NULL);
392 	if (to == NULL)
393 		to = s_new();
394 	if (*from->ptr == '\'') {
395 		from->ptr++;
396 		for (; *from->ptr != '\'' && *from->ptr != '\0'; from->ptr++)
397 			s_putc(to, (int)(unsigned int)*from->ptr);
398 		if (*from->ptr == '\'')
399 			from->ptr++;
400 	} else if (*from->ptr == '"') {
401 		from->ptr++;
402 		for (; *from->ptr != '"' && *from->ptr != '\0'; from->ptr++)
403 			s_putc(to, (int)(unsigned int)*from->ptr);
404 		if (*from->ptr == '"')
405 			from->ptr++;
406 	} else {
407 		for (; !isspace(*from->ptr) && *from->ptr != '\0'; from->ptr++)
408 			s_putc(to, (int)(unsigned int)*from->ptr);
409 	}
410 	s_terminate(to);
411 
412 	return (to);
413 }
414