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