root/usr.sbin/sysinst/txtwalk.c
/*      $NetBSD: txtwalk.c,v 1.5 2021/01/31 22:45:47 rillig Exp $       */

/*
 * Copyright 1997 Piermont Information Systems Inc.
 * All rights reserved.
 *
 * Written by Philip A. Nelson for Piermont Information Systems Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, item list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, item list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of Piermont Information Systems Inc. may not be used to endorse
 *    or promote products derived from item software without specific prior
 *    written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

/*
 *      walk a text buffer, processing matched lines
 *
 *      Written by Philip A. Nelson.
 *      7/29/97
 *
 */

#undef DEBUG

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

#include "txtwalk.h"

/* prototypes */

static int process(const struct lookfor *, char *);
static int match(char *, const struct lookfor *, size_t);
static int finddata(const struct lookfor *, char *, struct data *, size_t *);

/*
 * Walk the buffer, call match for each line.
 */
int
walk(char *buffer, size_t size, const struct lookfor *these, size_t numthese)
{
        size_t i = 0;
        size_t len;
        int line = 1;
        int error;

        while (i < size) {
                /* Ignore zero characters. */
                if (*buffer == '\0') {
                        buffer++;
                        i++;
                } else {
                        /* Assume item starts a line. */
                        len = 0;
                        while (buffer[len] != '\n' && buffer[len] != '\0')
                                len++;
                        buffer[len] = '\0';
#ifdef DEBUG
                        printf ("%5d: %s\n", line, buffer);
#endif
                        error = match(buffer, these, numthese);
                        if (error != 0)
                                return error;
                        buffer += len+1;
                        i += len+1;
                        line++;
                }
        }
        return 0;
}

/*
 * Match the current line with a string of interest.
 * For each match in these, process the match.
 */
static int
match(char *line, const struct lookfor *these, size_t numthese)
{
        size_t linelen;         /* Line length */
        size_t patlen;          /* Pattern length */
        size_t which;           /* Which pattern we are using */
        int error;

        linelen = strlen(line);

        for (which = 0; which < numthese; which++) {
                patlen = strlen(these[which].head);
                if (linelen < patlen)
                        continue;
                if (strncmp(these[which].head, line, patlen) == 0) {
                        error = process(&these[which], line);
                        if (error != 0)
                                return error;
                }
        }
        return 0;
}


/* process the matched line. */
static int
process(const struct lookfor *item, char *line)
{
        struct data found[MAXDATA];
        size_t numfound = 0;
        const char *p;
        char *np;
        size_t  i, j;
        int error;

        if (finddata(item, line, found, &numfound)) {
#ifdef DEBUG
                printf("process: \"%s\"\n", line);
                for (i = 0; i < numfound; i++) {
                        printf ("\t%d: ", i);
                        switch (found[i].what) {
                        case INT:
                                printf ("%d\n", found[i].u.i_val);
                                break;
                        case STR:
                                printf ("'%s'\n", found[i].u.s_val);
                                break;
                        }
                }
#endif
                /* Process the stuff. */
                switch (item->todo[0]) {
                case 'a':  /* Assign data */
                        p = item->todo;
                        j = 0;
                        while (*p && *p != '$')
                                p++;
                        if (*p)
                                p++;
                        for (;;) {
                                i = strtoul(p, &np, 10);
                                if (p == np)
                                    break;
                                p = np;
                                switch (found[i].what) {
                                case INT:
                                        *((int *)item->var+j)
                                                = found[i].u.i_val;
                                        break;
                                case STR:
                                        strlcpy(*((char **)item->var+j),
                                                found[i].u.s_val,
                                                item->size);
                                        break;
                                }
                                while (*p && *p != '$')
                                        p++;
                                if (*p)
                                        p++;
                                j++;
                                if (j >= item->nument)
                                        break;
                        }
                        break;
                case 'c':  /* Call a function with data. */
                        error = (*item->func)(found, numfound, item);
                        if (error != 0)
                                return error;
                        break;
                }
        }
        return 0;
}

/*
 * find the expected data.  Return 1 if successful, return 0 if not.
 * Successful means running into the end of the expect string before
 * running out of line data or encountering other bad data.
 *
 * Side Effect -- sets numfound and found.
 */
static int
finddata(const struct lookfor *item, char *line, struct data *found, size_t *numfound)
{
        const char *fmt;
        size_t len;
        char *np;
        int i;

        *numfound = 0;
        for (fmt = item->fmt; *fmt; fmt++) {
                if (!*line && *fmt)
                        return 0;
                if (*fmt == '%') {
                        fmt++;
                        if (!*fmt)
                                return 0;
                        switch (*fmt) {
                        case '%':  /* The char %. */
                                if (*line != '%')
                                        return 0;
                                line++;
                                break;
                        case 'i':  /* Ignore characters */
                                if (!fmt[1])
                                        return 1;
                                if (fmt[1] == ' ')
                                        while (*line && !isspace((unsigned char)*line))
                                                line++;
                                else
                                        while (*line && *line != fmt[1])
                                                line++;
                                break;
                        case 'd':  /* Nextoken should be an integer. */
                                i = strtoul(line, &np, 10);
                                if (line == np)
                                        return 0;
                                found[*numfound].what = INT;
                                found[(*numfound)++].u.i_val = i;
                                line = np;
                                break;
                        case 's':  /* Matches a 'space' separated string. */
                                len = 0;
                                while (line[len]
                                    && !isspace((unsigned char)line[len])
                                    && line[len] != fmt[1]) {
                                        if (line[len] == '\\'
                                            && line[len+1] != 0)
                                                len++;
                                        len++;
                                }
                                found[*numfound].what = STR;
                                found[(*numfound)++].u.s_val = line;
                                line[len] = 0;
                                line += len + 1;
                                break;
                        default:
                                return 0;
                        }
                        continue;

                }
                if (*fmt == ' ') {
                        while (isspace((unsigned char)*line))
                                line++;
                        continue;
                }
                if (*line == *fmt) {
                        line++;
                        continue;
                }
                /* Mis match! */
                return 0;
        }

        /* Ran out of fmt. */
        return 1;
}