## 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] */