/* */
This source file includes following definitions.
- print_and_abort
- __strbuf_expandbuf
- strbuf_open
- strbuf_reset
- strbuf_clear
- strbuf_nputs
- strbuf_nputc
- strbuf_puts
- strbuf_puts_withterm
- strbuf_puts_nl
- strbuf_putn
- strbuf_unputc
- strbuf_value
- strbuf_trim
- strbuf_fgets
- strbuf_sprintf
- strbuf_vsprintf
- strbuf_close
- strbuf_open_tempbuf
- strbuf_release_tempbuf
1 /*
2 * Copyright (c) 1997, 1998, 1999, 2000, 2002, 2005, 2006, 2010
3 * Tama Communications Corporation
4 *
5 * This file is part of GNU GLOBAL.
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <ctype.h>
25 #ifdef STDC_HEADERS
26 #include <stdlib.h>
27 #endif
28 #ifdef HAVE_STRING_H
29 #include <string.h>
30 #else
31 #include <strings.h>
32 #endif
33
34 #include "checkalloc.h"
35 #include "die.h"
36 #include "strbuf.h"
37
38 #ifndef isblank
39 #define isblank(c) ((c) == ' ' || (c) == '\t')
40 #endif
41 /**
42 @file
43
44 String buffer: usage and memory status
45
46 [xxx]: string buffer <br>
47 'v': current pointer
48
49 @code
50 Function call Memory status
51 ----------------------------------------------------------
52 (not exist)
53 v
54 sb = strbuf_open(0); []
55 v
56 strbuf_putc(sb, 'a'); [a]
57 v
58 char *s = strbuf_value(sb); [a\0] s == "a"
59 v
60 strbuf_puts(sb, "bc"); [abc]
61 v
62 char *s = strbuf_value(sb); [abc\0] s == "abc"
63 v
64 int len = strbuf_getlen(sb); [abc\0] len == 3
65 v
66 strbuf_reset(sb); [abc\0]
67 v
68 int len = strbuf_getlen(sb); [abc\0] len == 0
69 v
70 strbuf_puts(sb, "XY"); [XYc\0]
71 v
72 char *s = strbuf_value(sb); [XY\0] s == "XY"
73
74 fp = fopen("/etc/passwd", "r"); v
75 char *s = strbuf_fgets(sb, fp, 0) [root:*:0:0:Charlie &:/root:/bin/csh\0]
76 fclose(fp) s == "root:*:0:0:Charlie &:/root:/bin/csh"
77
78 strbuf_close(sb); (not exist)
79 @endcode
80 */
81
82 static void print_and_abort (void);
83 void (*strbuf_alloc_failed_handler) (void) = print_and_abort;
84
85 static void
86 print_and_abort(void)
87 {
88 die("short of memory.");
89 }
90
91 /**
92 * __strbuf_expandbuf: expand buffer so that afford to the length data at least.
93 *
94 * @param[in] sb #STRBUF structure
95 * @param[in] length required room
96 */
97 void
98 __strbuf_expandbuf(STRBUF *sb, int length)
99 {
100 int count = sb->curp - sb->sbuf;
101 int newsize = sb->sbufsize + (length > EXPANDSIZE ? length : EXPANDSIZE);
102 char *newbuf;
103
104 if (sb->alloc_failed)
105 return;
106 newbuf = (char *)check_realloc(sb->sbuf, newsize + 1);
107 sb->sbufsize = newsize;
108 sb->sbuf = newbuf;
109
110 sb->curp = sb->sbuf + count;
111 sb->endp = sb->sbuf + sb->sbufsize;
112 }
113 /**
114 * strbuf_open: open string buffer.
115 *
116 * @param[in] init initial buffer size <br>
117 * if 0 (zero) is specified then use default value (#INITIALSIZE).
118 * @return sb #STRBUF structure
119 */
120 STRBUF *
121 strbuf_open(int init)
122 {
123 STRBUF *sb = (STRBUF *)check_calloc(sizeof(STRBUF), 1);
124
125 sb->sbufsize = (init > 0) ? init : INITIALSIZE;
126 sb->sbuf = (char *)check_malloc(sb->sbufsize + 1);
127 sb->curp = sb->sbuf;
128 sb->endp = sb->sbuf + sb->sbufsize;
129
130 return sb;
131 }
132 /**
133 * strbuf_reset: reset string buffer.
134 *
135 * @param[in] sb string buffer
136 */
137 void
138 strbuf_reset(STRBUF *sb)
139 {
140 sb->curp = sb->sbuf;
141 sb->alloc_failed = 0;
142 }
143 /**
144 * strbuf_clear: clear static string buffer.
145 *
146 * @param[in] sb statically defined string buffer
147 *
148 * This function is used for the initializing of static string buffer.
149 * For the detail, see STATIC_STRBUF(sb) macro in @FILE{strbuf.h}.
150 */
151 void
152 strbuf_clear(STRBUF *sb)
153 {
154 if (sb == NULL)
155 die("NULL string buffer. (strbuf_clear)");
156 if (strbuf_empty(sb)) {
157 sb->sbufsize = INITIALSIZE;
158 sb->sbuf = (char *)check_malloc(sb->sbufsize + 1);
159 sb->curp = sb->sbuf;
160 sb->endp = sb->sbuf + sb->sbufsize;
161 } else {
162 strbuf_reset(sb);
163 }
164 }
165 /**
166 * strbuf_nputs: Put string with length
167 *
168 * @param[in] sb string buffer
169 * @param[in] s string
170 * @param[in] len length of string
171 */
172 void
173 strbuf_nputs(STRBUF *sb, const char *s, int len)
174 {
175 if (!sb->alloc_failed && len > 0) {
176 if (sb->curp + len > sb->endp)
177 __strbuf_expandbuf(sb, len);
178 while (len-- > 0)
179 *sb->curp++ = *s++;
180 }
181 }
182 /**
183 * strbuf_nputc: Put a character, @a len (number) times
184 *
185 * @param[in] sb string buffer
186 * @param[in] c character
187 * @param[in] len number of times to put @a c
188 *
189 * @see strbuf_putc()
190 */
191 void
192 strbuf_nputc(STRBUF *sb, int c, int len)
193 {
194 if (!sb->alloc_failed && len > 0) {
195 if (sb->curp + len > sb->endp)
196 __strbuf_expandbuf(sb, len);
197 while (len-- > 0)
198 *sb->curp++ = c;
199 }
200 }
201 /**
202 * strbuf_puts: Put string
203 *
204 * @param[in] sb string buffer
205 * @param[in] s string
206 */
207 void
208 strbuf_puts(STRBUF *sb, const char *s)
209 {
210 if (!sb->alloc_failed) {
211 while (*s) {
212 if (sb->curp >= sb->endp)
213 __strbuf_expandbuf(sb, 0);
214 *sb->curp++ = *s++;
215 }
216 }
217 }
218 /**
219 * strbuf_puts_withterm: Put string until the terminator
220 *
221 * @param[in] sb string buffer
222 * @param[in] s string
223 * @param[in] c terminator
224 * @return pointer to the terminator
225 */
226 void
227 strbuf_puts_withterm(STRBUF *sb, const char *s, int c)
228 {
229 if (!sb->alloc_failed) {
230 while (*s && *s != c) {
231 if (sb->curp >= sb->endp)
232 __strbuf_expandbuf(sb, 0);
233 *sb->curp++ = *s++;
234 }
235 }
236 }
237 /**
238 * strbuf_puts_nl: Put string with a new line
239 *
240 * @param[in] sb string buffer
241 * @param[in] s string
242 */
243 void
244 strbuf_puts_nl(STRBUF *sb, const char *s)
245 {
246 if (!sb->alloc_failed) {
247 while (*s) {
248 if (sb->curp >= sb->endp)
249 __strbuf_expandbuf(sb, 0);
250 *sb->curp++ = *s++;
251 }
252 if (sb->curp >= sb->endp)
253 __strbuf_expandbuf(sb, 0);
254 *sb->curp++ = '\n';
255 }
256 }
257 /**
258 * strbuf_putn: put digit string at the last of buffer.
259 *
260 * @param[in] sb #STRBUF structure
261 * @param[in] n number
262 */
263 void
264 strbuf_putn(STRBUF *sb, int n)
265 {
266 if (n == 0) {
267 strbuf_putc(sb, '0');
268 } else {
269 char num[128];
270 int i = 0;
271
272 while (n) {
273 if (i >= sizeof(num))
274 die("Too big integer value.");
275 num[i++] = n % 10 + '0';
276 n = n / 10;
277 }
278 while (--i >= 0)
279 strbuf_putc(sb, num[i]);
280 }
281 }
282 /**
283 * strbuf_unputc: remove specified char from the last of buffer
284 *
285 * @param[in] sb #STRBUF structure
286 * @param[in] c character
287 * @return 0: do nothing, 1: removed
288 */
289 int
290 strbuf_unputc(STRBUF *sb, int c)
291 {
292 if (sb->curp > sb->sbuf && *(sb->curp - 1) == c) {
293 sb->curp--;
294 return 1;
295 }
296 return 0;
297 }
298 /**
299 * strbuf_value: return the content of string buffer.
300 *
301 * @param[in] sb #STRBUF structure
302 * @return string
303 */
304 char *
305 strbuf_value(STRBUF *sb)
306 {
307 *sb->curp = 0;
308 return sb->sbuf;
309 }
310 /**
311 * strbuf_trim: trim following blanks.
312 *
313 * @param[in] sb #STRBUF structure
314 */
315 void
316 strbuf_trim(STRBUF *sb)
317 {
318 char *p = sb->curp;
319
320 while (p > sb->sbuf && isblank(*(p - 1)))
321 *--p = 0;
322 sb->curp = p;
323 }
324 /**
325 * strbuf_fgets: read whole record into string buffer
326 *
327 * @param[out] sb string buffer
328 * @param[in] ip input stream
329 * @param[in] flags flags <br>
330 * #STRBUF_NOCRLF: remove last @CODE{'\\n'} and/or @CODE{'\\r'} if exist. <br>
331 * #STRBUF_APPEND: append next record to existing data <br>
332 * #STRBUF_SHARPSKIP: skip lines which start with @CODE{'\#'}
333 * @return record buffer (@VAR{NULL} at end of file)
334 *
335 * Returned buffer has whole record. <br>
336 * The buffer end with @CODE{'\\0'}. If #STRBUF_NOCRLF is set then buffer doesn't
337 * include @CODE{'\\r'} and @CODE{'\\n'}.
338 */
339 char *
340 strbuf_fgets(STRBUF *sb, FILE *ip, int flags)
341 {
342 if (!(flags & STRBUF_APPEND))
343 strbuf_reset(sb);
344
345 if (sb->curp >= sb->endp)
346 __strbuf_expandbuf(sb, EXPANDSIZE); /* expand buffer */
347 if (sb->alloc_failed)
348 return sb->sbuf;
349
350 for (;;) {
351 if (!fgets(sb->curp, sb->endp - sb->curp, ip)) {
352 if (sb->curp == sb->sbuf)
353 return NULL;
354 break;
355 }
356 if (flags & STRBUF_SHARPSKIP && *(sb->curp) == '#')
357 continue;
358 sb->curp += strlen(sb->curp);
359 if (sb->curp > sb->sbuf && *(sb->curp - 1) == '\n')
360 break;
361 else if (feof(ip)) {
362 return sb->sbuf;
363 }
364 __strbuf_expandbuf(sb, EXPANDSIZE); /* expand buffer */
365 if (sb->alloc_failed)
366 return sb->sbuf;
367 }
368 if (flags & STRBUF_NOCRLF) {
369 if (*(sb->curp - 1) == '\n')
370 *(--sb->curp) = 0;
371 if (sb->curp > sb->sbuf && *(sb->curp - 1) == '\r')
372 *(--sb->curp) = 0;
373 }
374 return sb->sbuf;
375 }
376 /**
377 * strbuf_sprintf: do @NAME{sprintf} into string buffer.
378 *
379 * @param[in] sb #STRBUF structure
380 * @param[in] s similar to @NAME{sprintf()} <br>
381 * Currently the following format is supported. <br>
382 * @CODE{\%s, \%d, \%\<number\>d, \%\<number\>s, \%-\<number\>d, \%-\<number\>s}
383 */
384 void
385 strbuf_sprintf(STRBUF *sb, const char *s, ...)
386 {
387 va_list ap;
388
389 va_start(ap, s);
390 strbuf_vsprintf(sb, s, ap);
391 va_end(ap);
392 }
393 /**
394 * strbuf_vsprintf: do @NAME{vsprintf} into string buffer.
395 *
396 * @param[in] sb #STRBUF structure
397 * @param[in] s similar to @NAME{vsprintf()} <br>
398 * Currently the following format is supported. <br>
399 * @CODE{\%s, \%d, \%\<number\>d, \%\<number\>s, \%-\<number\>d, \%-\<number\>s}
400 * @param[in] ap <br>
401 */
402 void
403 strbuf_vsprintf(STRBUF *sb, const char *s, va_list ap)
404 {
405 if (sb->alloc_failed)
406 return;
407 for (; *s; s++) {
408 /*
409 * Put the before part of '%'.
410 */
411 {
412 const char *p;
413 for (p = s; *p && *p != '%'; p++)
414 ;
415 if (p > s) {
416 strbuf_nputs(sb, s, p - s);
417 s = p;
418 }
419 }
420 if (*s == '\0')
421 break;
422 if (*s == '%') {
423 int c = (unsigned char)*++s;
424 /*
425 * '%%' means '%'.
426 */
427 if (c == '%') {
428 strbuf_putc(sb, c);
429 }
430 /*
431 * If the optional number is specified then
432 * we forward the job to snprintf(3).
433 * o %<number>d
434 * o %<number>s
435 * o %-<number>d
436 * o %-<number>s
437 */
438 else if (isdigit(c) || (c == '-' && isdigit((unsigned char)*(s + 1)))) {
439 char format[32], buf[1024];
440 int i = 0;
441
442 format[i++] = '%';
443 if (c == '-')
444 format[i++] = *s++;
445 while (isdigit((unsigned char)*s))
446 format[i++] = *s++;
447 format[i++] = c = *s;
448 format[i] = '\0';
449 if (c == 'd' || c == 'x')
450 snprintf(buf, sizeof(buf), format, va_arg(ap, int));
451 else if (c == 's')
452 snprintf(buf, sizeof(buf), format, va_arg(ap, char *));
453 else
454 die("Unsupported control character '%c'.", c);
455 strbuf_puts(sb, buf);
456 } else if (c == 's') {
457 strbuf_puts(sb, va_arg(ap, char *));
458 } else if (c == 'd') {
459 strbuf_putn(sb, va_arg(ap, int));
460 } else {
461 die("Unsupported control character '%c'.", c);
462 }
463 }
464 }
465 }
466 /**
467 * strbuf_close: close string buffer.
468 *
469 * @param[in] sb #STRBUF structure
470 */
471 void
472 strbuf_close(STRBUF *sb)
473 {
474 if (sb->name)
475 (void)free(sb->name);
476 (void)free(sb->sbuf);
477 (void)free(sb);
478 }
479 /**
480 * @fn STRBUF *strbuf_open_tempbuf(void)
481 *
482 * Temporary string buffer for general purpose.
483 *
484 * @par Usage:
485 *
486 * @code
487 * STRBUF *sbt = strbuf_open_tempbuf();
488 * ....
489 * strbuf_puts(sbtemp, "xxx");
490 * ...
491 * strbuf_release_tempbuf(sbt);
492 * @endcode
493 *
494 */
495 static int used = 0;
496
497 STRBUF *
498 strbuf_open_tempbuf(void)
499 {
500 STATIC_STRBUF(sb);
501 if (used)
502 die("Internal error: temporary string buffer is already used.");
503 used = 1;
504 strbuf_clear(sb);
505 return sb;
506 }
507 void
508 strbuf_release_tempbuf(STRBUF *sb)
509 {
510 used = 0;
511 }
/* */