xref: /illumos-gate/usr/src/cmd/tnf/prex/spec.c (revision b40273e5)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 1994, by Sun Microsytems, Inc.
24  */
25 
26 /*
27  * Includes
28  */
29 
30 /* we need to define this to get strtok_r from string.h */
31 /* SEEMS LIKE A BUG TO ME */
32 #define	_REENTRANT
33 
34 #ifndef DEBUG
35 #define	NDEBUG	1
36 #endif
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <libintl.h>
42 #include <regexpr.h>
43 #include <assert.h>
44 #include <sys/types.h>
45 #include "spec.h"
46 #include "new.h"
47 #include "source.h"
48 
49 
50 static boolean_t spec_match(spec_t * spec_p, char *str);
51 
52 /*
53  * Globals
54  */
55 
56 
57 
58 /*
59  * spec() - builds a spec
60  */
61 
62 spec_t		 *
spec(char * str_p,spec_type_t type)63 spec(char *str_p,
64 	spec_type_t type)
65 {
66 	spec_t		 *new_p;
67 
68 	new_p = new(spec_t);
69 	queue_init(&new_p->qn);
70 	new_p->str = str_p;
71 	new_p->type = type;
72 	new_p->regexp_p = NULL;
73 
74 	if (type == SPEC_REGEXP) {
75 		new_p->regexp_p = compile(str_p, NULL, NULL);
76 		if (!new_p->regexp_p) {
77 			semantic_err(gettext("invalid regular expression"));
78 			free(new_p);
79 			return (NULL);
80 		}
81 	}
82 	return (new_p);
83 
84 }				/* end spec */
85 
86 
87 /*
88  * spec_dup() - duplicates a spec, NOT A SPEC LIST!
89  */
90 
91 spec_t		 *
spec_dup(spec_t * spec_p)92 spec_dup(spec_t * spec_p)
93 {
94 	spec_t		 *new_p;
95 
96 	new_p = spec(strdup(spec_p->str), spec_p->type);
97 
98 	return (new_p);
99 
100 }				/* end spec_dup */
101 
102 
103 /*
104  * spec_destroy() - destroys a spec list
105  */
106 
107 void
spec_destroy(spec_t * list_p)108 spec_destroy(spec_t * list_p)
109 {
110 	spec_t		 *spec_p;
111 
112 	while ((spec_p = (spec_t *) queue_next(&list_p->qn, &list_p->qn))) {
113 		(void) queue_remove(&spec_p->qn);
114 
115 		if (spec_p->str)
116 			free(spec_p->str);
117 		if (spec_p->regexp_p)
118 			free(spec_p->regexp_p);
119 		free(spec_p);
120 	}
121 
122 	if (list_p->str)
123 		free(list_p->str);
124 	if (list_p->regexp_p)
125 		free(list_p->regexp_p);
126 	free(list_p);
127 
128 }				/* end spec_destroy */
129 
130 
131 /*
132  * spec_list() - append a spec_t to a list
133  */
134 
135 spec_t		 *
spec_list(spec_t * h,spec_t * f)136 spec_list(spec_t * h,
137 	spec_t * f)
138 {
139 	/* queue append handles the NULL cases OK */
140 	return ((spec_t *) queue_append(&h->qn, &f->qn));
141 
142 }				/* end spec_list */
143 
144 
145 /*
146  * spec_print() - pretty prints a speclist
147  */
148 
149 void
spec_print(FILE * stream,spec_t * list_p)150 spec_print(FILE * stream,
151 	spec_t * list_p)
152 {
153 	spec_t		 *spec_p = NULL;
154 
155 	while ((spec_p = (spec_t *) queue_next(&list_p->qn, &spec_p->qn))) {
156 		switch (spec_p->type) {
157 		case SPEC_EXACT:
158 			(void) fprintf(stream, "'%s'", spec_p->str);
159 			break;
160 		case SPEC_REGEXP:
161 			(void) fprintf(stream, "/%s/", spec_p->str);
162 			break;
163 		}
164 	}
165 
166 }				/* end spec_print */
167 
168 
169 /*
170  * spec_match() - called with a spec and a string, returns whether they
171  * match.
172  */
173 
174 static boolean_t
spec_match(spec_t * spec_p,char * str)175 spec_match(spec_t * spec_p,
176 	char *str)
177 {
178 	if (!spec_p)
179 		return (B_FALSE);
180 
181 	switch (spec_p->type) {
182 	case SPEC_EXACT:
183 		return ((strcmp(spec_p->str, str) == 0));
184 
185 	case SPEC_REGEXP:
186 		return ((step(str, spec_p->regexp_p) != 0));
187 	}
188 
189 	return (B_FALSE);
190 
191 }				/* end spec_match */
192 
193 
194 /*
195  * spec_attrtrav() - traverse an attribute list, calling the supplied
196  * function on each matching attribute.
197  */
198 
199 void
spec_attrtrav(spec_t * spec_p,char * attrs,spec_attr_fun_t fun,void * calldatap)200 spec_attrtrav(spec_t * spec_p,
201 	char *attrs,
202 	spec_attr_fun_t fun,
203 	void *calldatap)
204 {
205 	char		   *lasts;
206 	char		   *refptr = NULL;
207 	char		   *escptr = NULL;
208 	char		   *pair;
209 	char		   *s;
210 	boolean_t	   inquote = B_FALSE;
211 
212 	/*
213 	 * * STRATEGY - we make two copies of the attr string.  In one *
214 	 * string we escape (translate) all relevant quoted characters to * a
215 	 * non-significant character.  We use this string to feed to * strtok
216 	 * to do the parsing. * Once strtok has parsed the string, we use the
217 	 * same fragement * positions from the unescaped string to pass to
218 	 * the next level.
219 	 */
220 
221 	/* make two copies of the string */
222 	refptr = strdup(attrs);
223 	escptr = strdup(attrs);
224 
225 	/* escape any quoted ';'s in the escptr string */
226 	for (s = escptr; *s; s++) {
227 		switch (*s) {
228 		case ';':
229 			if (inquote)
230 				*s = '#';
231 			break;
232 
233 		case '\'':
234 			inquote = (inquote) ? B_FALSE : B_TRUE;
235 			break;
236 
237 		default:
238 			/* nothing on purpose */
239 			break;
240 		}
241 	}
242 
243 	/* loop over each attribute section separated by ';' */
244 	for (pair = strtok_r(escptr, ";", &lasts); pair;
245 		pair = strtok_r(NULL, ";", &lasts)) {
246 		char		   *escattr;
247 		char		   *escvals;
248 		char		   *refattr;
249 		char		   *refvals;
250 		char			emptystr[1];
251 
252 		escattr = strtok_r(pair, " \t", &escvals);
253 
254 		/*
255 		 * setup the ref pointers to the same locations as the esc
256 		 * ptrs
257 		 */
258 		/*
259 		 * null the reference string in the same spots as the esc
260 		 * string
261 		 */
262 		refattr = (refptr + (escattr - escptr));
263 		refattr[strlen(escattr)] = '\0';
264 
265 		if (escvals && *escvals) {
266 			refvals = (refptr + (escvals - escptr));
267 			refvals[strlen(escvals)] = '\0';
268 		} else {
269 			refvals = NULL;
270 			emptystr[0] = '\0';
271 		}
272 
273 		if (spec_match(spec_p, refattr)) {
274 			if (refvals)
275 				(*fun) (spec_p, refattr, refvals, calldatap);
276 			else
277 				(*fun) (spec_p, refattr, emptystr, calldatap);
278 		}
279 	}
280 
281 alldone:
282 	if (refptr)
283 		free(refptr);
284 	if (escptr)
285 		free(escptr);
286 
287 }				/* end spec_attrtrav */
288 
289 
290 /*
291  * spec_valtrav() - traverse an value list, calling the supplied function on
292  * each matching value.
293  */
294 
295 void
spec_valtrav(spec_t * spec_p,char * valstr,spec_val_fun_t fun,void * calldatap)296 spec_valtrav(spec_t * spec_p,
297 	char *valstr,
298 	spec_val_fun_t fun,
299 	void *calldatap)
300 {
301 	char		   *s0;
302 	char		   *s;
303 	boolean_t	   intoken = B_FALSE;
304 	boolean_t	   inquote = B_FALSE;
305 
306 	/* return immeadiatly on null pointers */
307 	if (!valstr)
308 		return;
309 
310 	/* special case, match once on empty string */
311 	if (!*valstr) {
312 		if (spec_match(spec_p, valstr))
313 			(*fun) (spec_p, valstr, calldatap);
314 		return;
315 	}
316 	for (s = s0 = valstr; ; s++) {
317 		switch (*s) {
318 		case '\0':
319 			if (intoken) {
320 				if (spec_match(spec_p, s0))
321 					(*fun) (spec_p, s0, calldatap);
322 			}
323 			return;	/* ALL DONE */
324 
325 		case '\'':
326 			if (inquote) {
327 				/* end a quoted string */
328 				inquote = B_FALSE;
329 				intoken = B_FALSE;
330 				*s = '\0';
331 				if (spec_match(spec_p, s0))
332 					(*fun) (spec_p, s0, calldatap);
333 				/* next string starts past the quote */
334 				s0 = s + 1;
335 			} else {
336 				/* start a quoted string */
337 				inquote = B_TRUE;
338 				intoken = B_TRUE;
339 				s0 = s + 1;	/* point past the quote */
340 			}
341 			break;
342 
343 		case ' ':
344 		case '\t':
345 			/* ignore whitespace in quoted strings */
346 			if (inquote)
347 				break;
348 
349 			if (intoken) {
350 				/* whitespace ended this token */
351 				intoken = B_FALSE;
352 				*s = '\0';
353 				if (spec_match(spec_p, s0))
354 					(*fun) (spec_p, s0, calldatap);
355 				/* next string starts past the whitespace */
356 				s0 = s + 1;
357 			}
358 			break;
359 
360 		default:
361 			/* characters all OK inside quoted string */
362 			if (inquote)
363 				break;
364 
365 			if (!intoken) {
366 				/* start of unquoted token */
367 				intoken = B_TRUE;
368 				s0 = s;	/* token starts here */
369 			}
370 			break;
371 		}
372 	}
373 
374 
375 #ifdef TOOSIMPLE
376 	char		   *v;
377 	char		   *ls;
378 
379 	/*
380 	 * #### MISSING - need to handle quoted value strings * containing
381 	 * whitespace.
382 	 */
383 
384 	for (v = strtok_r(valstr, " \t", &ls); v;
385 		v = strtok_r(NULL, " \t", &ls)) {
386 		if (spec_match(spec_p, v)) {
387 			(*fun) (spec_p, v, calldatap);
388 		}
389 	}
390 #endif
391 
392 }				/* end spec_valtrav */
393