/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 1994, by Sun Microsytems, Inc.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Includes
*/
/* we need to define this to get strtok_r from string.h */
/* SEEMS LIKE A BUG TO ME */
#define _REENTRANT
#ifndef DEBUG
#define NDEBUG 1
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libintl.h>
#include <regexpr.h>
#include <assert.h>
#include <sys/types.h>
#include "spec.h"
#include "new.h"
#include "source.h"
static boolean_t spec_match(spec_t * spec_p, char *str);
/*
* Globals
*/
/*
* spec() - builds a spec
*/
spec_t *
spec(char *str_p,
spec_type_t type)
{
spec_t *new_p;
new_p = new(spec_t);
queue_init(&new_p->qn);
new_p->str = str_p;
new_p->type = type;
new_p->regexp_p = NULL;
if (type == SPEC_REGEXP) {
new_p->regexp_p = compile(str_p, NULL, NULL);
if (!new_p->regexp_p) {
semantic_err(gettext("invalid regular expression"));
free(new_p);
return (NULL);
}
}
return (new_p);
} /* end spec */
/*
* spec_dup() - duplicates a spec, NOT A SPEC LIST!
*/
spec_t *
spec_dup(spec_t * spec_p)
{
spec_t *new_p;
new_p = spec(strdup(spec_p->str), spec_p->type);
return (new_p);
} /* end spec_dup */
/*
* spec_destroy() - destroys a spec list
*/
void
spec_destroy(spec_t * list_p)
{
spec_t *spec_p;
while ((spec_p = (spec_t *) queue_next(&list_p->qn, &list_p->qn))) {
(void) queue_remove(&spec_p->qn);
if (spec_p->str)
free(spec_p->str);
if (spec_p->regexp_p)
free(spec_p->regexp_p);
free(spec_p);
}
if (list_p->str)
free(list_p->str);
if (list_p->regexp_p)
free(list_p->regexp_p);
free(list_p);
} /* end spec_destroy */
/*
* spec_list() - append a spec_t to a list
*/
spec_t *
spec_list(spec_t * h,
spec_t * f)
{
/* queue append handles the NULL cases OK */
return ((spec_t *) queue_append(&h->qn, &f->qn));
} /* end spec_list */
/*
* spec_print() - pretty prints a speclist
*/
void
spec_print(FILE * stream,
spec_t * list_p)
{
spec_t *spec_p = NULL;
while ((spec_p = (spec_t *) queue_next(&list_p->qn, &spec_p->qn))) {
switch (spec_p->type) {
case SPEC_EXACT:
(void) fprintf(stream, "'%s'", spec_p->str);
break;
case SPEC_REGEXP:
(void) fprintf(stream, "/%s/", spec_p->str);
break;
}
}
} /* end spec_print */
/*
* spec_match() - called with a spec and a string, returns whether they
* match.
*/
static boolean_t
spec_match(spec_t * spec_p,
char *str)
{
if (!spec_p)
return (B_FALSE);
switch (spec_p->type) {
case SPEC_EXACT:
return ((strcmp(spec_p->str, str) == 0));
case SPEC_REGEXP:
return ((step(str, spec_p->regexp_p) != NULL));
}
return (B_FALSE);
} /* end spec_match */
/*
* spec_attrtrav() - traverse an attribute list, calling the supplied
* function on each matching attribute.
*/
void
spec_attrtrav(spec_t * spec_p,
char *attrs,
spec_attr_fun_t fun,
void *calldatap)
{
char *lasts;
char *refptr = NULL;
char *escptr = NULL;
char *pair;
char *s;
boolean_t inquote = B_FALSE;
/*
* * STRATEGY - we make two copies of the attr string. In one *
* string we escape (translate) all relevant quoted characters to * a
* non-significant character. We use this string to feed to * strtok
* to do the parsing. * Once strtok has parsed the string, we use the
* same fragement * positions from the unescaped string to pass to
* the next level.
*/
/* make two copies of the string */
refptr = strdup(attrs);
escptr = strdup(attrs);
/* escape any quoted ';'s in the escptr string */
for (s = escptr; *s; s++) {
switch (*s) {
case ';':
if (inquote)
*s = '#';
break;
case '\'':
inquote = (inquote) ? B_FALSE : B_TRUE;
break;
default:
/* nothing on purpose */
break;
}
}
/* loop over each attribute section separated by ';' */
for (pair = strtok_r(escptr, ";", &lasts); pair;
pair = strtok_r(NULL, ";", &lasts)) {
char *escattr;
char *escvals;
char *refattr;
char *refvals;
char emptystr[1];
escattr = strtok_r(pair, " \t", &escvals);
/*
* setup the ref pointers to the same locations as the esc
* ptrs
*/
/*
* null the reference string in the same spots as the esc
* string
*/
refattr = (refptr + (escattr - escptr));
refattr[strlen(escattr)] = '\0';
if (escvals && *escvals) {
refvals = (refptr + (escvals - escptr));
refvals[strlen(escvals)] = '\0';
} else {
refvals = NULL;
emptystr[0] = '\0';
}
if (spec_match(spec_p, refattr)) {
if (refvals)
(*fun) (spec_p, refattr, refvals, calldatap);
else
(*fun) (spec_p, refattr, emptystr, calldatap);
}
}
alldone:
if (refptr)
free(refptr);
if (escptr)
free(escptr);
} /* end spec_attrtrav */
/*
* spec_valtrav() - traverse an value list, calling the supplied function on
* each matching value.
*/
void
spec_valtrav(spec_t * spec_p,
char *valstr,
spec_val_fun_t fun,
void *calldatap)
{
char *s0;
char *s;
boolean_t intoken = B_FALSE;
boolean_t inquote = B_FALSE;
/* return immeadiatly on null pointers */
if (!valstr)
return;
/* special case, match once on empty string */
if (!*valstr) {
if (spec_match(spec_p, valstr))
(*fun) (spec_p, valstr, calldatap);
return;
}
for (s = s0 = valstr; ; s++) {
switch (*s) {
case NULL:
if (intoken) {
if (spec_match(spec_p, s0))
(*fun) (spec_p, s0, calldatap);
}
return; /* ALL DONE */
case '\'':
if (inquote) {
/* end a quoted string */
inquote = B_FALSE;
intoken = B_FALSE;
*s = '\0';
if (spec_match(spec_p, s0))
(*fun) (spec_p, s0, calldatap);
/* next string starts past the quote */
s0 = s + 1;
} else {
/* start a quoted string */
inquote = B_TRUE;
intoken = B_TRUE;
s0 = s + 1; /* point past the quote */
}
break;
case ' ':
case '\t':
/* ignore whitespace in quoted strings */
if (inquote)
break;
if (intoken) {
/* whitespace ended this token */
intoken = B_FALSE;
*s = '\0';
if (spec_match(spec_p, s0))
(*fun) (spec_p, s0, calldatap);
/* next string starts past the whitespace */
s0 = s + 1;
}
break;
default:
/* characters all OK inside quoted string */
if (inquote)
break;
if (!intoken) {
/* start of unquoted token */
intoken = B_TRUE;
s0 = s; /* token starts here */
}
break;
}
}
#ifdef TOOSIMPLE
char *v;
char *ls;
/*
* #### MISSING - need to handle quoted value strings * containing
* whitespace.
*/
for (v = strtok_r(valstr, " \t", &ls); v;
v = strtok_r(NULL, " \t", &ls)) {
if (spec_match(spec_p, v)) {
(*fun) (spec_p, v, calldatap);
}
}
#endif
} /* end spec_valtrav */