/* vi: set expandtab sw=4 sts=4: */
/* parse_util.c - the opkg package management system

   Copyright (C) 2009 Ubiq Technologies <graham.gower@gmail.com>

   Steven M. Ayer
   Copyright (C) 2002 Compaq Computer Corporation

   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 2, 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.
*/

#include "config.h"

#include <ctype.h>
#include <string.h>
#include <stdio.h>

#include "opkg_message.h"
#include "opkg_utils.h"
#include "xfuncs.h"

#include "parse_util.h"

int is_field(const char *type, const char *line)
{
    if (strncmp(line, type, strlen(type)) == 0)
        return 1;
    return 0;
}

char *parse_simple(const char *type, const char *line)
{
    return trim_xstrdup(line + strlen(type) + 1);
}

/*
 * Parse a comma separated string into an array.
 */
char **parse_list(const char *raw, unsigned int *count, const char sep,
                  int skip_field)
{
    char **depends = NULL;
    const char *start, *end;
    int line_count = 0;

    /* skip past the "Field:" marker */
    if (!skip_field) {
        while (*raw && *raw != ':')
            raw++;
        raw++;
    }

    if (line_is_blank(raw)) {
        *count = line_count;
        return NULL;
    }

    while (*raw) {
        depends = xrealloc(depends, sizeof(char *) * (line_count + 1));

        while (isspace(*raw))
            raw++;

        start = raw;
        while (*raw != sep && *raw)
            raw++;
        end = raw;

        while (end > start && isspace(*end))
            end--;

        if (sep == ' ')
            end++;

        depends[line_count] = xstrndup(start, end - start);

        line_count++;
        if (*raw == sep)
            raw++;
    }

    *count = line_count;
    return depends;
}

int parse_from_stream_nomalloc(parse_line_t parse_line, void *ptr, FILE * fp,
                               uint mask, char **buf0, size_t buf0len)
{
    int ret, lineno;
    char *buf, *nl;
    size_t buflen;
    char *s;
    int r;

    lineno = 1;
    ret = 0;

    buflen = buf0len;
    buf = *buf0;
    buf[0] = '\0';

    while (1) {
        s = fgets(buf, (int)buflen, fp);
        if (s == NULL) {
            if (ferror(fp)) {
                opkg_perror(ERROR, "fgets");
                ret = -1;
            } else if (strlen(*buf0) == buf0len - 1) {
                opkg_msg(ERROR,
                         "Missing new line character" " at end of file!\n");
                parse_line(ptr, *buf0, mask);
            }
            break;
        }

        nl = strchr(buf, '\n');
        if (nl == NULL) {
            if (strlen(buf) < buflen - 1) {
                /*
                 * Line could be exactly buflen-1 long and
                 * missing a newline, but we won't know until
                 * fgets fails to read more data.
                 */
                opkg_msg(ERROR,
                         "Missing new line character" " at end of file!\n");
                parse_line(ptr, *buf0, mask);
                break;
            }
            if (buf0len >= EXCESSIVE_LINE_LEN) {
                opkg_msg(ERROR,
                         "Excessively long line at " "%d. Corrupt file?\n",
                         lineno);
                ret = -1;
                break;
            }

            /*
             * Realloc and point buf past the data already read,
             * at the NULL terminator inserted by fgets.
             * |<--------------- buf0len ----------------->|
             * |                     |<------- buflen ---->|
             * |---------------------|---------------------|
             * buf0                   buf
             */
            buflen = buf0len + 1;
            buf0len *= 2;
            *buf0 = xrealloc(*buf0, buf0len);
            buf = *buf0 + buflen - 2;

            continue;
        }

        *nl = '\0';

        lineno++;

        r = parse_line(ptr, *buf0, mask);
        if (r != 0)
            break;

        buf = *buf0;
        buflen = buf0len;
        buf[0] = '\0';
    }

    return ret;
}
