/*	$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;
}
