Skip to content
Snippets Groups Projects
parse_parentheses.c 6.74 KiB
/*
 * parser_parentheses.c
 *
 * Copyright (c) 2018-2023 Eric Vidal <eric@obarun.org>
 *
 * All rights reserved.
 *
 * This file is part of Obarun. It is subject to the license terms in
 * the LICENSE file found in the top-level directory of this
 * distribution.
 * This file may not be copied, modified, propagated, or distributed
 * except according to the terms contained in the LICENSE file./
 */

#include <string.h>
#include <stdint.h>

#include <oblibs/string.h>

#include <66/parse.h>

static char parse_char_next(char const *s, size_t slen, size_t *pos)
{
    char c = 0 ;
    if (*pos > slen) return -1 ;
    c = s[*pos] ;
    (*pos)++ ;
    return c ;
}

int parse_parentheses(char *store, char const *str, size_t *pos)
{
    int open = 0, close = 0, /* valid closed parentheses */ vp = 0, /* last valid closed parentheses position */ lvp = 0 ;
    uint8_t parentheses = 1 ;
    size_t o = 0, slen = strlen(str), e = 0 ;

    open = get_sep_before(str, '=', '(') ;
    if (open <= 0)
        return 0 ;
    close = get_sep_before(str, '(', ')') ;
    if (close < 0)
        return 0 ;
    open += get_len_until(str + open + 1, '(') ; // +1 remove '='
    open += 2 ; // +2 remove '('


    char line[slen + 1] ;
    auto_strings(line, str + open) ;
    size_t len = strlen(line) ;

    /** The general idea of this parsing process is to get
     * the same number of opened and closed parentheses.
     *
     * For intance, this function receive string starting with the key name field
     * at the very start of the string, '@execute=( ....'
     * we simply looking for corresponding closed parenthese and kept verbatim
     * of what we have been found between '(' and ')'.
    */
    while (parentheses && o < len) {

        char c = parse_char_next(line, len, &o) ;

        switch(c) {

            case '(':
                if (vp) {
                    vp-- ;
                    lvp = 0 ;
                }
                parentheses++ ;
                break ;

            case ')':
                {
                    if (vp) {
                        vp = 0 ;
                        lvp = 0 ;
                    }

                    if (parentheses - 1 == 0) {
                        /*
                         * This check the validity of the closed parenthese.
                         * For instance, a case...esac statement in scripts
                         * use a ')' without its corresponding reverse part.
                         *
                         * When examining the very first character of the next
                         * line and encountering either '#@' or '@', it determines
                         * the validity. If the validity check fails, it signifies
                         * that we remain within the script context*/

                        int /* last parenthese */ lp = o ;
                        e = 0 ;

                        e = get_len_until(line + o, '\n') + 1 ;

                        if (o + e >= len) {
                            // end of string. this validate the parenthese
                            parentheses-- ;
                            o = lp ;
                            vp = 0 ;
                            break ;
                        }
                        o += e ;

                        /** with empty line we need to go futher and
                         * make this current check with the next line.*/
                        if (line[o] == '\n') {
                            o++ ;
                            vp = 1 ;
                            lvp = lp ;
                            break ;
                        }

                        /** Outside of the context specified (e.g., @execute=()),
                         * only '#' and '@' character combinaison are considered valid to
                         * validate the parentheses. If neither of these is present,
                         * it signifies that we are inside a script.*/
                        if (line[o] != '#' && line[o] != '@')
                            break ;

                        if (line[o] == '#') {

                            if (line[o + 1] == '@') {
                                /** a commented key validates the parenthese */
                                o = lp ;
                                parentheses-- ;
                                vp = 0 ;
                            } else {
                                // this is a comment
                                e = get_len_until(line + o, '\n') + 1 ;
                                o += e ;
                                vp = 1 ;
                                lvp = lp ;
                            }
                            break ;
                        }
                        o = lp ;
                        parentheses-- ;
                        vp = 0 ;
                        break ;

                    } else
                       parentheses-- ;

                    break ;
                }

            case '#':

                if (vp) {

                    /** we previously coming from a comment.
                     * this validates the parenthese.*/
                    if (line[o + 1] == '@') {
                        o = lvp ;
                        parentheses-- ;
                        vp = 0 ;
                    } else {
                        /** another comment, continue the check at the
                         * next line */
                        e = get_len_until(line + o, '\n') + 1 ;
                        o += e ;
                    }
                }
                break ;

            case '@':
                /** we previously coming from a comment.
                 * this validates the parenthese.*/
                if (vp) {
                    o = lvp ;
                    parentheses-- ;
                    vp = 0 ;
                }
                break ;
            case '\n':
                if (vp) {
                    /** we previously coming from an empty line.
                     * continue the check at the next line*/
                    e = get_len_until(line + o, '\n') + 1 ;
                    o += e ;
                }
                break ;
            case -1:
                return 0 ;
            default:
                if (vp) {
                    /** this invalidate the parentheses*/
                    vp = 0 ;
                }
                break ;
        }
    }

    if (vp && (o == len)) {
        /** end of string. this validate the parenthese */
        parentheses-- ;
        o = lvp ;
    }

    if (parentheses)
        return 0 ;

    (*pos) = open + len ;

    open = get_len_until(str, '\n') ;
    if (open < 1)
        open = 0 ;

    (*pos) = open + 1 ; // +1 remove '\n'

    len = len - (len - o) - 1 ;
    memcpy(store, line, len) ;
    store[len] = 0 ;

    return 1 ;
}