1/*
2 * implement string functions for dc
3 *
4 * Copyright (C) 1994, 1997, 1998 Free Software Foundation, Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you can either send email to this
18 * program's author (see below) or write to:
19 *
20 *    The Free Software Foundation, Inc.
21 *    59 Temple Place, Suite 330
22 *    Boston, MA 02111 USA
23 */
24
25/* This should be the only module that knows the internals of type dc_string */
26
27#include "config.h"
28
29#include <stdio.h>
30#ifdef HAVE_STDDEF_H
31# include <stddef.h>	/* ptrdiff_t */
32#else
33# define ptrdiff_t	size_t
34#endif
35#ifdef HAVE_STDLIB_H
36# include <stdlib.h>
37#endif
38#ifdef HAVE_STRING_H
39# include <string.h>	/* memcpy */
40#else
41# ifdef HAVE_MEMORY_H
42#  include <memory.h>	/* memcpy, maybe */
43# else
44#  ifdef HAVE_STRINGS_H
45#   include <strings.h>	/* memcpy, maybe */
46#  endif
47# endif
48#endif
49#include "dc.h"
50#include "dc-proto.h"
51
52/* here is the completion of the dc_string type: */
53struct dc_string {
54	char *s_ptr;  /* pointer to base of string */
55	size_t s_len; /* length of counted string */
56	int  s_refs;  /* reference count to cut down on memory use by duplicates */
57};
58
59
60/* return a duplicate of the string in the passed value */
61/* The mismatched data types forces the caller to deal with
62 * bad dc_type'd dc_data values, and makes it more convenient
63 * for the caller to not have to do the grunge work of setting
64 * up a dc_type result.
65 */
66dc_data
67dc_dup_str DC_DECLARG((value))
68	dc_str value DC_DECLEND
69{
70	dc_data result;
71
72	++value->s_refs;
73	result.v.string = value;
74	result.dc_type = DC_STRING;
75	return result;
76}
77
78/* free an instance of a dc_str value */
79void
80dc_free_str DC_DECLARG((value))
81	dc_str *value DC_DECLEND
82{
83	struct dc_string *string = *value;
84
85	if (--string->s_refs < 1){
86		free(string->s_ptr);
87		free(string);
88	}
89}
90
91/* Output a dc_str value.
92 * Add a trailing newline if "newline" is set.
93 * Free the value after use if discard_flag is set.
94 */
95void
96dc_out_str DC_DECLARG((value, newline, discard_flag))
97	dc_str value DC_DECLSEP
98	dc_newline newline DC_DECLSEP
99	dc_discard discard_flag DC_DECLEND
100{
101	fwrite(value->s_ptr, value->s_len, sizeof *value->s_ptr, stdout);
102	if (newline == DC_WITHNL)
103		putchar('\n');
104	if (discard_flag == DC_TOSS)
105		dc_free_str(&value);
106}
107
108/* make a copy of a string (base s, length len)
109 * into a dc_str value; return a dc_data result
110 * with this value
111 */
112dc_data
113dc_makestring DC_DECLARG((s, len))
114	const char *s DC_DECLSEP
115	size_t len DC_DECLEND
116{
117	dc_data result;
118	struct dc_string *string;
119
120	string = dc_malloc(sizeof *string);
121	string->s_ptr = dc_malloc(len+1);
122	memcpy(string->s_ptr, s, len);
123	string->s_ptr[len] = '\0';	/* nul terminated for those who need it */
124	string->s_len = len;
125	string->s_refs = 1;
126	result.v.string = string;
127	result.dc_type = DC_STRING;
128	return result;
129}
130
131/* read a dc_str value from FILE *fp;
132 * if ldelim == rdelim, then read until a ldelim char or EOF is reached;
133 * if ldelim != rdelim, then read until a matching rdelim for the
134 * (already eaten) first ldelim is read.
135 * Return a dc_data result with the dc_str value as its contents.
136 */
137dc_data
138dc_readstring DC_DECLARG((fp, ldelim, rdelim))
139	FILE *fp DC_DECLSEP
140	int ldelim DC_DECLSEP
141	int rdelim DC_DECLEND
142{
143	static char *line_buf = NULL;	/* a buffer to build the string in */
144	static size_t buflen = 0;		/* the current size of line_buf */
145	int depth=1;
146	int c;
147	char *p;
148	const char *end;
149
150	if (!line_buf){
151		/* initial buflen should be large enough to handle most cases */
152		buflen = 2016;
153		line_buf = dc_malloc(buflen);
154	}
155	p = line_buf;
156	end = line_buf + buflen;
157	for (;;){
158		c = getc(fp);
159		if (c == EOF)
160			break;
161		else if (c == rdelim && --depth < 1)
162			break;
163		else if (c == ldelim)
164			++depth;
165		if (p >= end){
166			ptrdiff_t offset = p - line_buf;
167			/* buflen increment should be big enough
168			 * to avoid execessive reallocs:
169			 */
170			buflen += 2048;
171			line_buf = realloc(line_buf, buflen);
172			if (!line_buf)
173				dc_memfail();
174			p = line_buf + offset;
175			end = line_buf + buflen;
176		}
177		*p++ = c;
178	}
179	return dc_makestring(line_buf, (size_t)(p-line_buf));
180}
181
182/* return the base pointer of the dc_str value;
183 * This function is needed because no one else knows what dc_str
184 * looks like.
185 */
186const char *
187dc_str2charp DC_DECLARG((value))
188	dc_str value DC_DECLEND
189{
190	return value->s_ptr;
191}
192
193/* return the length of the dc_str value;
194 * This function is needed because no one else knows what dc_str
195 * looks like, and strlen(dc_str2charp(value)) won't work
196 * if there's an embedded '\0'.
197 */
198size_t
199dc_strlen DC_DECLARG((value))
200	dc_str value DC_DECLEND
201{
202	return value->s_len;
203}
204
205
206/* initialize the strings subsystem */
207void
208dc_string_init DC_DECLVOID()
209{
210	/* nothing to do for this implementation */
211}
212