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