1/*	$NetBSD: txtwalk.c,v 1.5 2021/01/31 22:45:47 rillig Exp $	*/
2
3/*
4 * Copyright 1997 Piermont Information Systems Inc.
5 * All rights reserved.
6 *
7 * Written by Philip A. Nelson for Piermont Information Systems Inc.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, item list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, item list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 * 3. The name of Piermont Information Systems Inc. may not be used to endorse
18 *    or promote products derived from item software without specific prior
19 *    written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY PIERMONT INFORMATION SYSTEMS INC. ``AS IS''
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL PIERMONT INFORMATION SYSTEMS INC. BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
31 * THE POSSIBILITY OF SUCH DAMAGE.
32 *
33 */
34
35/*
36 *	walk a text buffer, processing matched lines
37 *
38 *	Written by Philip A. Nelson.
39 *	7/29/97
40 *
41 */
42
43#undef DEBUG
44
45#include <stdio.h>
46#include <string.h>
47#include <ctype.h>
48#include <unistd.h>
49#include <stdlib.h>
50
51#include "txtwalk.h"
52
53/* prototypes */
54
55static int process(const struct lookfor *, char *);
56static int match(char *, const struct lookfor *, size_t);
57static int finddata(const struct lookfor *, char *, struct data *, size_t *);
58
59/*
60 * Walk the buffer, call match for each line.
61 */
62int
63walk(char *buffer, size_t size, const struct lookfor *these, size_t numthese)
64{
65	size_t i = 0;
66	size_t len;
67	int line = 1;
68	int error;
69
70	while (i < size) {
71		/* Ignore zero characters. */
72		if (*buffer == '\0') {
73			buffer++;
74			i++;
75		} else {
76			/* Assume item starts a line. */
77			len = 0;
78			while (buffer[len] != '\n' && buffer[len] != '\0')
79				len++;
80			buffer[len] = '\0';
81#ifdef DEBUG
82			printf ("%5d: %s\n", line, buffer);
83#endif
84			error = match(buffer, these, numthese);
85			if (error != 0)
86				return error;
87			buffer += len+1;
88			i += len+1;
89			line++;
90		}
91	}
92	return 0;
93}
94
95/*
96 * Match the current line with a string of interest.
97 * For each match in these, process the match.
98 */
99static int
100match(char *line, const struct lookfor *these, size_t numthese)
101{
102	size_t linelen;		/* Line length */
103	size_t patlen;		/* Pattern length */
104	size_t which;		/* Which pattern we are using */
105	int error;
106
107	linelen = strlen(line);
108
109	for (which = 0; which < numthese; which++) {
110		patlen = strlen(these[which].head);
111		if (linelen < patlen)
112			continue;
113		if (strncmp(these[which].head, line, patlen) == 0) {
114			error = process(&these[which], line);
115			if (error != 0)
116				return error;
117		}
118	}
119	return 0;
120}
121
122
123/* process the matched line. */
124static int
125process(const struct lookfor *item, char *line)
126{
127	struct data found[MAXDATA];
128	size_t numfound = 0;
129	const char *p;
130	char *np;
131	size_t  i, j;
132	int error;
133
134	if (finddata(item, line, found, &numfound)) {
135#ifdef DEBUG
136		printf("process: \"%s\"\n", line);
137		for (i = 0; i < numfound; i++) {
138			printf ("\t%d: ", i);
139			switch (found[i].what) {
140			case INT:
141				printf ("%d\n", found[i].u.i_val);
142				break;
143			case STR:
144				printf ("'%s'\n", found[i].u.s_val);
145				break;
146			}
147		}
148#endif
149		/* Process the stuff. */
150		switch (item->todo[0]) {
151		case 'a':  /* Assign data */
152			p = item->todo;
153			j = 0;
154			while (*p && *p != '$')
155				p++;
156			if (*p)
157				p++;
158			for (;;) {
159				i = strtoul(p, &np, 10);
160				if (p == np)
161				    break;
162				p = np;
163				switch (found[i].what) {
164				case INT:
165					*((int *)item->var+j)
166						= found[i].u.i_val;
167					break;
168				case STR:
169					strlcpy(*((char **)item->var+j),
170					        found[i].u.s_val,
171						item->size);
172					break;
173				}
174				while (*p && *p != '$')
175					p++;
176				if (*p)
177					p++;
178				j++;
179				if (j >= item->nument)
180					break;
181			}
182			break;
183		case 'c':  /* Call a function with data. */
184			error = (*item->func)(found, numfound, item);
185			if (error != 0)
186				return error;
187			break;
188		}
189	}
190	return 0;
191}
192
193/*
194 * find the expected data.  Return 1 if successful, return 0 if not.
195 * Successful means running into the end of the expect string before
196 * running out of line data or encountering other bad data.
197 *
198 * Side Effect -- sets numfound and found.
199 */
200static int
201finddata(const struct lookfor *item, char *line, struct data *found, size_t *numfound)
202{
203	const char *fmt;
204	size_t len;
205	char *np;
206	int i;
207
208	*numfound = 0;
209	for (fmt = item->fmt; *fmt; fmt++) {
210		if (!*line && *fmt)
211			return 0;
212		if (*fmt == '%') {
213			fmt++;
214			if (!*fmt)
215				return 0;
216			switch (*fmt) {
217			case '%':  /* The char %. */
218				if (*line != '%')
219					return 0;
220				line++;
221				break;
222			case 'i':  /* Ignore characters */
223				if (!fmt[1])
224					return 1;
225				if (fmt[1] == ' ')
226					while (*line && !isspace((unsigned char)*line))
227						line++;
228				else
229					while (*line && *line != fmt[1])
230						line++;
231				break;
232			case 'd':  /* Nextoken should be an integer. */
233				i = strtoul(line, &np, 10);
234				if (line == np)
235					return 0;
236				found[*numfound].what = INT;
237				found[(*numfound)++].u.i_val = i;
238				line = np;
239				break;
240			case 's':  /* Matches a 'space' separated string. */
241				len = 0;
242				while (line[len]
243				    && !isspace((unsigned char)line[len])
244				    && line[len] != fmt[1]) {
245					if (line[len] == '\\'
246					    && line[len+1] != 0)
247						len++;
248					len++;
249				}
250				found[*numfound].what = STR;
251				found[(*numfound)++].u.s_val = line;
252				line[len] = 0;
253				line += len + 1;
254				break;
255			default:
256				return 0;
257			}
258			continue;
259
260		}
261		if (*fmt == ' ') {
262			while (isspace((unsigned char)*line))
263				line++;
264			continue;
265		}
266		if (*line == *fmt) {
267			line++;
268			continue;
269		}
270		/* Mis match! */
271		return 0;
272	}
273
274	/* Ran out of fmt. */
275	return 1;
276}
277