Fix maintainer address.
[anag] / util.c
CommitLineData
6e403221 1/* -*-c-*-
2 *
94ed9b30 3 * $Id$
6e403221 4 *
5 * Various useful utilities, stolen from mLib
6 *
7 * (c) 2001 Mark Wooding
8 */
9
10/*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of Anag: a simple wordgame helper.
13 *
14 * Anag is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * Anag is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with Anag; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
6e403221 29/*----- Header files ------------------------------------------------------*/
30
31#include "anag.h"
32
33/*----- Static variables --------------------------------------------------*/
34
35static const char *quis = "<unset>";
36
37/*----- Error reporting ---------------------------------------------------*/
38
39/* --- @ego@ --- *
40 *
41 * Arguments: @const char *p@ = pointer to program name
42 *
43 * Returns: ---
44 *
45 * Use: Stores what the program's name is.
46 */
47
48#ifndef PATHSEP
49# if defined(__riscos)
50# define PATHSEP '.'
51# elif defined(__unix) || defined(unix)
52# define PATHSEP '/'
53# else
54# define PATHSEP '\\'
55# endif
56#endif
57
58void ego(const char *p)
59{
60 const char *q = p;
61 while (*q) {
62 if (*q++ == PATHSEP)
63 p = q;
64 }
65 if (*p == '-')
66 p++;
67 quis = p;
68}
69
70#undef PATHSEP
71
72/* --- @pquis@ --- *
73 *
74 * Arguments: @FILE *fp@ = output stream to write on
75 * @const char *p@ = pointer to string to write
76 *
77 * Returns: Zero if everything worked, EOF if not.
78 *
79 * Use: Writes the string @p@ to the output stream @fp@. Occurrences
80 * of the character `$' in @p@ are replaced by the program name
81 * as reported by @quis@. A `$$' is replaced by a single `$'
82 * sign.
83 */
84
85int pquis(FILE *fp, const char *p)
86{
87 size_t sz;
88
89 while (*p) {
90 sz = strcspn(p, "$");
91 if (sz) {
92 if (fwrite(p, 1, sz, fp) < sz)
93 return (EOF);
94 p += sz;
95 }
96 if (*p == '$') {
97 p++;
98 if (*p == '$') {
99 if (fputc('$', fp) == EOF)
100 return (EOF);
101 p++;
102 } else {
103 if (fputs(quis, fp) == EOF)
104 return (EOF);
105 }
106 }
107 }
108 return (0);
109}
110
111/* --- @die@ --- *
112 *
113 * Arguments: @const char *f@ = a @printf@-style format string
114 * @...@ = other arguments
115 *
116 * Returns: Never.
117 *
118 * Use: Reports an error and exits.
119 */
120
121void die(const char *f, ...)
122{
123 va_list ap;
124 va_start(ap, f);
125 fprintf(stderr, "%s: ", quis);
126 vfprintf(stderr, f, ap);
127 va_end(ap);
128 putc('\n', stderr);
94ed9b30 129 exit(EX_FAIL);
6e403221 130}
131
132/*----- Memory allocation -------------------------------------------------*/
133
134/* --- @xmalloc@ --- *
135 *
136 * Arguments: @size_t sz@ = size of block to allocate
137 *
138 * Returns: Pointer to allocated block.
139 *
140 * Use: Allocates memory. If there's not enough memory, the
141 * program exits.
142 */
143
144void *xmalloc(size_t sz)
145{
146 void *p = malloc(sz);
147 if (!p)
148 die("not enough memory");
149 return (p);
150}
151
152/* --- @xrealloc@ --- *
153 *
154 * Arguments: @void *p@ = a pointer to allocated memory
155 * @size_t sz@ = new size of block wanted
156 *
157 * Returns: Pointer to resized block.
158 *
159 * Use: Resizes an allocated block. If there's not enough memory,
160 * the program exits.
161 */
162
163void *xrealloc(void *p, size_t sz)
164{
165 p = realloc(p, sz);
166 if (!p)
167 die("not enough memory");
168 return (p);
169}
170
171/*----- Dynamic string handling -------------------------------------------*/
172
173#define DSTR_INITSZ 64
174
175/* --- @dstr_destroy@ --- *
176 *
177 * Arguments: @dstr *d@ = pointer to a dynamic string block
178 *
179 * Returns: ---
180 *
181 * Use: Reclaims the space used by a dynamic string.
182 */
183
184void dstr_destroy(dstr *d) { free(d->buf); d->len = 0; d->sz = 0; }
185
186/* --- @dstr_reset@ --- *
187 *
188 * Arguments: @dstr *d@ = pointer to a dynamic string block
189 *
190 * Returns: ---
191 *
192 * Use: Resets a string so that new data gets put at the beginning.
193 */
194
195void dstr_reset(dstr *d) { d->len = 0; }
196
197/* --- @dstr_ensure@ --- *
198 *
199 * Arguments: @dstr *d@ = pointer to a dynamic string block
200 * @size_t sz@ = amount of free space to ensure
201 *
202 * Returns: ---
203 *
204 * Use: Ensures that at least @sz@ bytes are available in the
205 * given string.
206 */
207
208void dstr_ensure(dstr *d, size_t sz)
209{
210 size_t rq = d->len + sz;
211 size_t nsz;
212
213 /* --- If we have enough space, just leave it --- */
214
215 if (rq <= d->sz)
216 return;
217
218 /* --- Grow the buffer --- */
219
220 nsz = d->sz;
221
222 if (nsz == 0)
223 nsz = (DSTR_INITSZ >> 1);
224 do nsz <<= 1; while (nsz < rq);
225
226 if (d->buf)
227 d->buf = xrealloc(d->buf, nsz);
228 else
229 d->buf = xmalloc(nsz);
230 d->sz = nsz;
231}
232
233/* --- @dstr_putline@ --- *
234 *
235 * Arguments: @dstr *d@ = pointer to a dynamic string block
236 * @FILE *fp@ = a stream to read from
237 *
238 * Returns: The number of characters read into the buffer, or @EOF@ if
239 * end-of-file was reached before any characters were read.
240 *
241 * Use: Appends the next line from the given input stream to the
242 * string. A trailing newline is not added; a trailing null
243 * byte is appended, as for @dstr_putz@.
244 */
245
246int dstr_putline(dstr *d, FILE *fp)
247{
248 size_t left = d->sz - d->len;
249 size_t off = d->len;
250 int rd = 0;
251 int ch;
252
253 for (;;) {
254
255 /* --- Read the next byte --- */
256
257 ch = getc(fp);
258
259 /* --- End-of-file when no characters read is special --- */
260
261 if (ch == EOF && !rd)
262 return (EOF);
263
264 /* --- Make sure there's some buffer space --- */
265
266 if (!left) {
267 d->len = off;
268 dstr_ensure(d, 1);
269 left = d->sz - off;
270 }
271
272 /* --- End-of-file or newline ends the loop --- */
273
274 if (ch == EOF || ch == '\n') {
275 d->buf[off] = 0;
276 d->len = off;
277 return rd;
278 }
279
280 /* --- Append the character and continue --- */
281
282 d->buf[off++] = ch;
283 left--; rd++;
284 }
285}
286
287/*----- That's all, folks -------------------------------------------------*/