root/libutil/strbuf.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. print_and_abort
  2. __strbuf_expandbuf
  3. strbuf_open
  4. strbuf_reset
  5. strbuf_clear
  6. strbuf_nputs
  7. strbuf_nputc
  8. strbuf_puts
  9. strbuf_puts_nl
  10. strbuf_putn
  11. strbuf_unputc
  12. strbuf_value
  13. strbuf_trim
  14. strbuf_fgets
  15. strbuf_sprintf
  16. strbuf_close
  17. strbuf_open_tempbuf
  18. strbuf_release_tempbuf

/*
 * Copyright (c) 1997, 1998, 1999, 2000, 2002, 2005, 2006
 *      Tama Communications Corporation
 *
 * This file is part of GNU GLOBAL.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <ctype.h>
#ifdef STDC_HEADERS
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif

#include "checkalloc.h"
#include "die.h"
#include "strbuf.h"

#ifndef isblank
#define isblank(c)      ((c) == ' ' || (c) == '\t')
#endif
/*

String buffer: usage and memory status

                                        [xxx]: string buffer
                                        'v': current pointer

Function call                           Memory status
----------------------------------------------------------
                                        (not exist)
                                         v
sb = strbuf_open(0);                    []
                                          v
strbuf_putc(sb, 'a');                   [a]
                                          v
char *s = strbuf_value(sb);             [a\0]           s == "a"
                                            v
strbuf_puts(sb, "bc");                  [abc]
                                            v
char *s = strbuf_value(sb);             [abc\0]         s == "abc"
                                            v
int len = strbuf_getlen(sb);            [abc\0]         len == 3
                                         v
strbuf_reset(sb);                       [abc\0]
                                         v
int len = strbuf_getlen(sb);            [abc\0]         len == 0
                                           v
strbuf_puts(sb, "XY");                  [XYc\0]
                                           v
char *s = strbuf_value(sb);             [XY\0]          s == "XY"

fp = fopen("/etc/passwd", "r");                                             v
char *s = strbuf_fgets(sb, fp, 0)       [root:*:0:0:Charlie &:/root:/bin/csh\0]
fclose(fp)                              s == "root:*:0:0:Charlie &:/root:/bin/csh"

strbuf_close(sb);                       (not exist)

*/

static void print_and_abort (void);
void (*strbuf_alloc_failed_handler) (void) = print_and_abort;

static void
print_and_abort(void)
{
        die("short of memory.");
}

/*
 * __strbuf_expandbuf: expand buffer so that afford to the length data at least.
 *
 *      i)      sb      STRBUF structure
 *      i)      length  required room
 */
void
__strbuf_expandbuf(STRBUF *sb, int length)
{
        int count = sb->curp - sb->sbuf;
        int newsize = sb->sbufsize + (length > EXPANDSIZE ? length : EXPANDSIZE);
        char *newbuf;

        if (sb->alloc_failed)
                return;
        newbuf = (char *)check_realloc(sb->sbuf, newsize + 1);
        sb->sbufsize = newsize;
        sb->sbuf = newbuf;

        sb->curp = sb->sbuf + count;
        sb->endp = sb->sbuf + sb->sbufsize;
}
/*
 * strbuf_open: open string buffer.
 *
 *      i)      init    initial buffer size
 *                      if 0 is specified then use default value.
 *      r)      sb      STRBUF structure
 */
STRBUF *
strbuf_open(int init)
{
        STRBUF *sb = (STRBUF *)check_calloc(sizeof(STRBUF), 1);

        sb->sbufsize = (init > 0) ? init : INITIALSIZE;
        sb->sbuf = (char *)check_malloc(sb->sbufsize + 1);
        sb->curp = sb->sbuf;
        sb->endp = sb->sbuf + sb->sbufsize;

        return sb;
}
/*
 * strbuf_reset: reset string buffer.
 *
 *      i)      sb      string buffer
 */
void
strbuf_reset(STRBUF *sb)
{
        sb->curp = sb->sbuf;
        sb->alloc_failed = 0;
}
/*
 * strbuf_clear: clear static string buffer.
 *
 *      i)      sb      statically defined string buffer
 *
 * This function is used for the initializing of static string buffer.
 * For the detail, see 'STATIC_STRBUF(sb)' macro in strbuf.h.
 */
void
strbuf_clear(STRBUF *sb)
{
        if (sb == NULL)
                die("NULL string buffer. (strbuf_clear)");
        if (strbuf_empty(sb)) {
                sb->sbufsize = INITIALSIZE;
                sb->sbuf = (char *)check_malloc(sb->sbufsize + 1);
                sb->curp = sb->sbuf;
                sb->endp = sb->sbuf + sb->sbufsize;
        } else {
                strbuf_reset(sb);
        }
}
/*
 * strbuf_nputs: Put string with length
 *
 *      i)      sb      string buffer
 *      i)      s       string
 *      i)      len     length of string
 */
void
strbuf_nputs(STRBUF *sb, const char *s, int len)
{
        if (!sb->alloc_failed && len > 0) {
                if (sb->curp + len > sb->endp)
                        __strbuf_expandbuf(sb, len);
                while (len-- > 0)
                        *sb->curp++ = *s++;
        }
}
/*
 * strbuf_nputc: Put characters with length
 *
 *      i)      sb      string buffer
 *      i)      c       character
 *      i)      len     length of string
 */
void
strbuf_nputc(STRBUF *sb, int c, int len)
{
        if (!sb->alloc_failed && len > 0) {
                if (sb->curp + len > sb->endp)
                        __strbuf_expandbuf(sb, len);
                while (len-- > 0)
                        *sb->curp++ = c;
        }
}
/*
 * strbuf_puts: Put string
 *
 *      i)      sb      string buffer
 *      i)      s       string
 */
void
strbuf_puts(STRBUF *sb, const char *s)
{
        if (!sb->alloc_failed) {
                while (*s) {
                        if (sb->curp >= sb->endp)
                                __strbuf_expandbuf(sb, 0);
                        *sb->curp++ = *s++;
                }
        }
}
/*
 * strbuf_puts_nl: Put string with a new line
 *
 *      i)      sb      string buffer
 *      i)      s       string
 */
void
strbuf_puts_nl(STRBUF *sb, const char *s)
{
        if (!sb->alloc_failed) {
                while (*s) {
                        if (sb->curp >= sb->endp)
                                __strbuf_expandbuf(sb, 0);
                        *sb->curp++ = *s++;
                }
                if (sb->curp >= sb->endp)
                        __strbuf_expandbuf(sb, 0);
                *sb->curp++ = '\n';
        }
}
/*
 * strbuf_putn: put digit string at the last of buffer.
 *
 *      i)      sb      STRBUF structure
 *      i)      n       number
 */
void
strbuf_putn(STRBUF *sb, int n)
{
        if (n == 0) {
                strbuf_putc(sb, '0');
        } else {
                char num[128];
                int i = 0;

                while (n) {
                        if (i >= sizeof(num))
                                die("Too big integer value.");
                        num[i++] = n % 10 + '0';
                        n = n / 10;
                }
                while (--i >= 0)
                        strbuf_putc(sb, num[i]);
        }
}
/*
 * strbuf_unputc: remove specified char from the last of buffer
 *
 *      i)      sb      STRBUF structure
 *      i)      c       character
 *      r)              0: do nothing, 1: removed
 */
int
strbuf_unputc(STRBUF *sb, int c)
{
        if (sb->curp > sb->sbuf && *(sb->curp - 1) == c) {
                sb->curp--;
                return 1;
        }
        return 0;
}
/*
 * strbuf_value: return the content of string buffer.
 *
 *      i)      sb      STRBUF structure
 *      r)              string
 */
char *
strbuf_value(STRBUF *sb)
{
        *sb->curp = 0;
        return sb->sbuf;
}
/*
 * strbuf_trim: trim following blanks.
 *
 *      i)      sb      STRBUF structure
 */
void
strbuf_trim(STRBUF *sb)
{
        char *p = sb->curp;

        while (p > sb->sbuf && isblank(*(p - 1)))
                *--p = 0;
        sb->curp = p;
}
/*
 * strbuf_fgets: read whole record into string buffer
 *
 *      o)      sb      string buffer
 *      i)      ip      input stream
 *      i)      flags   flags
 *                      STRBUF_NOCRLF   remove last '\n' if exist.
 *                      STRBUF_APPEND   append next record to existing data
 *      r)              record buffer (NULL at end of file)
 *
 * Returned buffer has whole record.
 * The buffer end with '\0'.If STRBUF_NOCRLF is set then buffer doesn't
 * include '\r' and '\n'.
 */
char *
strbuf_fgets(STRBUF *sb, FILE *ip, int flags)
{
        if (!(flags & STRBUF_APPEND))
                strbuf_reset(sb);

        if (sb->curp >= sb->endp)
                __strbuf_expandbuf(sb, EXPANDSIZE);     /* expand buffer */
        if (sb->alloc_failed)
                return sb->sbuf;

        for (;;) {
                if (!fgets(sb->curp, sb->endp - sb->curp, ip)) {
                        if (sb->curp == sb->sbuf)
                                return NULL;
                        break;
                }
                sb->curp += strlen(sb->curp);
                if (sb->curp > sb->sbuf && *(sb->curp - 1) == '\n')
                        break;
                else if (feof(ip)) {
                        return sb->sbuf;
                }
                __strbuf_expandbuf(sb, EXPANDSIZE);     /* expand buffer */
                if (sb->alloc_failed)
                        return sb->sbuf;
        }
        if (flags & STRBUF_NOCRLF) {
                if (*(sb->curp - 1) == '\n')
                        *(--sb->curp) = 0;
                if (sb->curp > sb->sbuf && *(sb->curp - 1) == '\r')
                        *(--sb->curp) = 0;
        }
        return sb->sbuf;
}
/*
 * strbuf_sprintf: do sprintf into string buffer.
 *
 *      i)      sb      STRBUF structure
 *      i)      s       similar to sprintf()
 *                      Currently the following format is supported.
 *                      %s, %d, %<number>d, %<number>s, %-<number>d, %-<number>s
 */
void
strbuf_sprintf(STRBUF *sb, const char *s, ...)
{
        va_list ap;

        va_start(ap, s);
        if (sb->alloc_failed)
                return;
        for (; *s; s++) {
                /*
                 * Put the before part of '%'.
                 */
                {
                        const char *p;
                        for (p = s; *p && *p != '%'; p++)
                                ;
                        if (p > s) {
                                strbuf_nputs(sb, s, p - s);
                                s = p;
                        }
                }
                if (*s == '\0')
                        break;
                if (*s == '%') {
                        int c = (unsigned char)*++s;
                        /*
                         * '%%' means '%'.
                         */
                        if (c == '%') {
                                strbuf_putc(sb, c);
                        }
                        /*
                         * If the optional number is specified then
                         * we forward the job to snprintf(3).
                         * o %<number>d
                         * o %<number>s
                         * o %-<number>d
                         * o %-<number>s
                         */
                        else if (isdigit(c) || (c == '-' && isdigit((unsigned char)*(s + 1)))) {
                                char format[32], buf[1024];
                                int i = 0;

                                format[i++] = '%';
                                if (c == '-')
                                        format[i++] = *s++;
                                while (isdigit((unsigned char)*s))
                                        format[i++] = *s++;
                                format[i++] = c = *s;
                                format[i] = '\0';
                                if (c == 'd' || c == 'x')
                                        snprintf(buf, sizeof(buf), format, va_arg(ap, int));
                                else if (c == 's')
                                        snprintf(buf, sizeof(buf), format, va_arg(ap, char *));
                                else
                                        die("Unsupported control character '%c'.", c);
                                strbuf_puts(sb, buf);
                        } else if (c == 's') {
                                strbuf_puts(sb, va_arg(ap, char *));
                        } else if (c == 'd') {
                                strbuf_putn(sb, va_arg(ap, int));
                        } else {
                                die("Unsupported control character '%c'.", c);
                        }
                }
        }
        va_end(ap);
}
/*
 * strbuf_close: close string buffer.
 *
 *      i)      sb      STRBUF structure
 */
void
strbuf_close(STRBUF *sb)
{
        if (sb->name)
                (void)free(sb->name);
        (void)free(sb->sbuf);
        (void)free(sb);
}
/*
 * Temporary string buffer for general purpose.
 *
 * Usage:
 *
 *      STRBUF *sbt = strbuf_open_tempbuf();
 *      ....
 *      strbuf_puts(sbtemp, "xxx");
 *      ...
 *      strbuf_release_tempbuf(sbt);
 *
 */
int used = 0;

STRBUF *
strbuf_open_tempbuf(void)
{
        STATIC_STRBUF(sb);
        if (used)
                die("Internal error: temporary string buffer is already used.");
        used = 1;
        strbuf_clear(sb);
        return sb;
}
void
strbuf_release_tempbuf(STRBUF *sb)
{
        used = 0;
}

/* [<][>][^][v][top][bottom][index][help] */