From e3016cb009d362235765af8b857ade754a97756f Mon Sep 17 00:00:00 2001 From: Xin Ouyang Date: Thu, 23 May 2013 17:12:13 +0800 Subject: [PATCH] libselinux: Revert libpcre for old refpolicy compatible This reverts upstream libpcre commits. libselinux 2.1.12 uses libpcre to do file path matching instead of glibc regex. Because there are some differences between glibc regex and pcre functions, this will cause wrong security contexts for files while using old refpolicy. This patch should be dropped while refpolicy is upreved to 2.20120725+. Upstream-Status: Inappropriate [for old refpolicy] Signed-off-by: Xin Ouyang --- src/Makefile | 2 - src/label_file.c | 481 ++++++++++++++++++++++++++++++++------------ src/label_file.h | 273 ------------------------- 3 files changed, 347 insertions(+), 409 deletions(-) delete mode 100644 src/label_file.h diff --git a/src/Makefile b/src/Makefile index ac019df..74e53bb 100644 --- a/src/Makefile +++ b/src/Makefile @@ -20,8 +20,6 @@ RUBYINC ?= $(shell pkg-config --cflags ruby-$(RUBYLIBVER)) RUBYINSTALL ?= $(LIBDIR)/ruby/site_ruby/$(RUBYLIBVER)/$(RUBYPLATFORM) LIBBASE=$(shell basename $(LIBDIR)) -LDFLAGS ?= -lpcre - VERSION = $(shell cat ../VERSION) LIBVERSION = 1 diff --git a/src/label_file.c b/src/label_file.c index 02b3cd2..7bc46cc 100644 --- a/src/label_file.c +++ b/src/label_file.c @@ -16,18 +16,71 @@ #include #include #include -#include +#include #include #include #include #include "callbacks.h" #include "label_internal.h" -#include "label_file.h" /* * Internals, mostly moved over from matchpathcon.c */ +/* A file security context specification. */ +typedef struct spec { + struct selabel_lookup_rec lr; /* holds contexts for lookup result */ + char *regex_str; /* regular expession string for diagnostics */ + char *type_str; /* type string for diagnostic messages */ + regex_t regex; /* compiled regular expression */ + char regcomp; /* regex_str has been compiled to regex */ + mode_t mode; /* mode format value */ + int matches; /* number of matching pathnames */ + int hasMetaChars; /* regular expression has meta-chars */ + int stem_id; /* indicates which stem-compression item */ +} spec_t; + +/* A regular expression stem */ +typedef struct stem { + char *buf; + int len; +} stem_t; + +/* Our stored configuration */ +struct saved_data { + /* + * The array of specifications, initially in the same order as in + * the specification file. Sorting occurs based on hasMetaChars. + */ + spec_t *spec_arr; + unsigned int nspec; + unsigned int ncomp; + + /* + * The array of regular expression stems. + */ + stem_t *stem_arr; + int num_stems; + int alloc_stems; +}; + +/* Return the length of the text that can be considered the stem, returns 0 + * if there is no identifiable stem */ +static int get_stem_from_spec(const char *const buf) +{ + const char *tmp = strchr(buf + 1, '/'); + const char *ind; + + if (!tmp) + return 0; + + for (ind = buf; ind < tmp; ind++) { + if (strchr(".^$?*+|[({", (int)*ind)) + return 0; + } + return tmp - buf; +} + /* return the length of the text that is the stem of a file name */ static int get_stem_from_file_name(const char *const buf) { @@ -38,6 +91,41 @@ static int get_stem_from_file_name(const char *const buf) return tmp - buf; } +/* find the stem of a file spec, returns the index into stem_arr for a new + * or existing stem, (or -1 if there is no possible stem - IE for a file in + * the root directory or a regex that is too complex for us). */ +static int find_stem_from_spec(struct saved_data *data, const char *buf) +{ + int i, num = data->num_stems; + int stem_len = get_stem_from_spec(buf); + + if (!stem_len) + return -1; + for (i = 0; i < num; i++) { + if (stem_len == data->stem_arr[i].len + && !strncmp(buf, data->stem_arr[i].buf, stem_len)) + return i; + } + if (data->alloc_stems == num) { + stem_t *tmp_arr; + data->alloc_stems = data->alloc_stems * 2 + 16; + tmp_arr = realloc(data->stem_arr, + sizeof(stem_t) * data->alloc_stems); + if (!tmp_arr) + return -1; + data->stem_arr = tmp_arr; + } + data->stem_arr[num].len = stem_len; + data->stem_arr[num].buf = malloc(stem_len + 1); + if (!data->stem_arr[num].buf) + return -1; + memcpy(data->stem_arr[num].buf, buf, stem_len); + data->stem_arr[num].buf[stem_len] = '\0'; + data->num_stems++; + buf += stem_len; + return num; +} + /* find the stem of a file name, returns the index into stem_arr (or -1 if * there is no match - IE for a file in the root directory or a regex that is * too complex for us). Makes buf point to the text AFTER the stem. */ @@ -94,17 +182,59 @@ static int nodups_specs(struct saved_data *data, const char *path) return rc; } -static int compile_regex(struct saved_data *data, struct spec *spec, const char **errbuf) +/* Determine if the regular expression specification has any meta characters. */ +static void spec_hasMetaChars(struct spec *spec) +{ + char *c; + int len; + char *end; + + c = spec->regex_str; + len = strlen(spec->regex_str); + end = c + len; + + spec->hasMetaChars = 0; + + /* Look at each character in the RE specification string for a + * meta character. Return when any meta character reached. */ + while (c != end) { + switch (*c) { + case '.': + case '^': + case '$': + case '?': + case '*': + case '+': + case '|': + case '[': + case '(': + case '{': + spec->hasMetaChars = 1; + return; + case '\\': /* skip the next character */ + c++; + break; + default: + break; + + } + c++; + } + return; +} + +static int compile_regex(struct saved_data *data, spec_t *spec, char **errbuf) { - const char *tmperrbuf; char *reg_buf, *anchored_regex, *cp; - struct stem *stem_arr = data->stem_arr; + stem_t *stem_arr = data->stem_arr; size_t len; - int erroff; + int regerr; if (spec->regcomp) return 0; /* already done */ + data->ncomp++; /* how many compiled regexes required */ + /* Skip the fixed stem. */ reg_buf = spec->regex_str; if (spec->stem_id >= 0) @@ -115,7 +245,6 @@ static int compile_regex(struct saved_data *data, struct spec *spec, const char cp = anchored_regex = malloc(len + 3); if (!anchored_regex) return -1; - /* Create ^...$ regexp. */ *cp++ = '^'; cp = mempcpy(cp, reg_buf, len); @@ -123,20 +252,21 @@ static int compile_regex(struct saved_data *data, struct spec *spec, const char *cp = '\0'; /* Compile the regular expression. */ - spec->regex = pcre_compile(anchored_regex, 0, &tmperrbuf, &erroff, NULL); - free(anchored_regex); - if (!spec->regex) { - if (errbuf) - *errbuf=tmperrbuf; - return -1; - } - - spec->sd = pcre_study(spec->regex, 0, &tmperrbuf); - if (!spec->sd) { - if (errbuf) - *errbuf=tmperrbuf; + regerr = regcomp(&spec->regex, anchored_regex, + REG_EXTENDED | REG_NOSUB); + if (regerr != 0) { + size_t errsz = 0; + errsz = regerror(regerr, &spec->regex, NULL, 0); + if (errsz && errbuf) + *errbuf = malloc(errsz); + if (errbuf && *errbuf) + (void)regerror(regerr, &spec->regex, + *errbuf, errsz); + + free(anchored_regex); return -1; } + free(anchored_regex); /* Done. */ spec->regcomp = 1; @@ -144,16 +274,16 @@ static int compile_regex(struct saved_data *data, struct spec *spec, const char return 0; } + static int process_line(struct selabel_handle *rec, const char *path, const char *prefix, - char *line_buf, unsigned lineno) + char *line_buf, int pass, unsigned lineno) { - int items, len, rc; + int items, len; char *buf_p, *regex, *type, *context; struct saved_data *data = (struct saved_data *)rec->data; - struct spec *spec_arr; + spec_t *spec_arr = data->spec_arr; unsigned int nspec = data->nspec; - const char *errbuf = NULL; len = strlen(line_buf); if (line_buf[len - 1] == '\n') @@ -188,91 +318,77 @@ static int process_line(struct selabel_handle *rec, return 0; } - rc = grow_specs(data); - if (rc) - return rc; - - spec_arr = data->spec_arr; - - /* process and store the specification in spec. */ - spec_arr[nspec].stem_id = find_stem_from_spec(data, regex); - spec_arr[nspec].regex_str = regex; - if (rec->validating && compile_regex(data, &spec_arr[nspec], &errbuf)) { - COMPAT_LOG(SELINUX_WARNING, "%s: line %d has invalid regex %s: %s\n", - path, lineno, regex, (errbuf ? errbuf : "out of memory")); - } - - /* Convert the type string to a mode format */ - spec_arr[nspec].type_str = type; - spec_arr[nspec].mode = 0; - if (type) { - mode_t mode = string_to_mode(type); - if (mode == -1) { - COMPAT_LOG(SELINUX_WARNING, "%s: line %d has invalid file type %s\n", - path, lineno, type); - mode = 0; + if (pass == 1) { + /* On the second pass, process and store the specification in spec. */ + char *errbuf = NULL; + spec_arr[nspec].stem_id = find_stem_from_spec(data, regex); + spec_arr[nspec].regex_str = regex; + if (rec->validating && compile_regex(data, &spec_arr[nspec], &errbuf)) { + COMPAT_LOG(SELINUX_WARNING, + "%s: line %d has invalid regex %s: %s\n", + path, lineno, regex, + (errbuf ? errbuf : "out of memory")); } - spec_arr[nspec].mode = mode; - } - - spec_arr[nspec].lr.ctx_raw = context; - - /* Determine if specification has - * any meta characters in the RE */ - spec_hasMetaChars(&spec_arr[nspec]); - if (strcmp(context, "<>") && rec->validating) - compat_validate(rec, &spec_arr[nspec].lr, path, lineno); - - data->nspec = ++nspec; - - return 0; -} - -static int process_file(const char *path, const char *suffix, struct selabel_handle *rec, const char *prefix) -{ - FILE *fp; - struct stat sb; - unsigned int lineno; - size_t line_len; - char *line_buf = NULL; - int rc; - char stack_path[PATH_MAX + 1]; - - /* append the path suffix if we have one */ - if (suffix) { - rc = snprintf(stack_path, sizeof(stack_path), "%s.%s", path, suffix); - if (rc >= sizeof(stack_path)) { - errno = ENAMETOOLONG; - return -1; + /* Convert the type string to a mode format */ + spec_arr[nspec].type_str = type; + spec_arr[nspec].mode = 0; + if (!type) + goto skip_type; + len = strlen(type); + if (type[0] != '-' || len != 2) { + COMPAT_LOG(SELINUX_WARNING, + "%s: line %d has invalid file type %s\n", + path, lineno, type); + return 0; + } + switch (type[1]) { + case 'b': + spec_arr[nspec].mode = S_IFBLK; + break; + case 'c': + spec_arr[nspec].mode = S_IFCHR; + break; + case 'd': + spec_arr[nspec].mode = S_IFDIR; + break; + case 'p': + spec_arr[nspec].mode = S_IFIFO; + break; + case 'l': + spec_arr[nspec].mode = S_IFLNK; + break; + case 's': + spec_arr[nspec].mode = S_IFSOCK; + break; + case '-': + spec_arr[nspec].mode = S_IFREG; + break; + default: + COMPAT_LOG(SELINUX_WARNING, + "%s: line %d has invalid file type %s\n", + path, lineno, type); + return 0; } - path = stack_path; - } - /* Open the specification file. */ - if ((fp = fopen(path, "r")) == NULL) - return -1; - __fsetlocking(fp, FSETLOCKING_BYCALLER); + skip_type: + spec_arr[nspec].lr.ctx_raw = context; - if (fstat(fileno(fp), &sb) < 0) - return -1; - if (!S_ISREG(sb.st_mode)) { - errno = EINVAL; - return -1; - } + /* Determine if specification has + * any meta characters in the RE */ + spec_hasMetaChars(&spec_arr[nspec]); - /* - * The do detailed validation of the input and fill the spec array - */ - lineno = 0; - while (getline(&line_buf, &line_len, fp) > 0) { - rc = process_line(rec, path, prefix, line_buf, ++lineno); - if (rc) - return rc; + if (strcmp(context, "<>") && rec->validating) + compat_validate(rec, &spec_arr[nspec].lr, path, lineno); } - free(line_buf); - fclose(fp); + data->nspec = ++nspec; + if (pass == 0) { + free(regex); + if (type) + free(type); + free(context); + } return 0; } @@ -282,8 +398,18 @@ static int init(struct selabel_handle *rec, struct selinux_opt *opts, struct saved_data *data = (struct saved_data *)rec->data; const char *path = NULL; const char *prefix = NULL; + FILE *fp; + FILE *localfp = NULL; + FILE *homedirfp = NULL; + char local_path[PATH_MAX + 1]; + char homedir_path[PATH_MAX + 1]; char subs_file[PATH_MAX + 1]; + char *line_buf = NULL; + size_t line_len = 0; + unsigned int lineno, pass, i, j, maxnspec; + spec_t *spec_copy = NULL; int status = -1, baseonly = 0; + struct stat sb; /* Process arguments */ while (n--) @@ -303,7 +429,6 @@ static int init(struct selabel_handle *rec, struct selinux_opt *opts, if (!path) { rec->subs = selabel_subs_init(selinux_file_context_subs_dist_path(), rec->subs); rec->subs = selabel_subs_init(selinux_file_context_subs_path(), rec->subs); - path = selinux_file_context_path(); } else { snprintf(subs_file, sizeof(subs_file), "%s.subs_dist", path); rec->subs = selabel_subs_init(subs_file, rec->subs); @@ -311,37 +436,126 @@ static int init(struct selabel_handle *rec, struct selinux_opt *opts, rec->subs = selabel_subs_init(subs_file, rec->subs); } + /* Open the specification file. */ + if (!path) + path = selinux_file_context_path(); + if ((fp = fopen(path, "r")) == NULL) + return -1; + __fsetlocking(fp, FSETLOCKING_BYCALLER); + + if (fstat(fileno(fp), &sb) < 0) + return -1; + if (!S_ISREG(sb.st_mode)) { + errno = EINVAL; + return -1; + } + + if (!baseonly) { + snprintf(homedir_path, sizeof(homedir_path), "%s.homedirs", + path); + homedirfp = fopen(homedir_path, "r"); + if (homedirfp != NULL) + __fsetlocking(homedirfp, FSETLOCKING_BYCALLER); + + snprintf(local_path, sizeof(local_path), "%s.local", path); + localfp = fopen(local_path, "r"); + if (localfp != NULL) + __fsetlocking(localfp, FSETLOCKING_BYCALLER); + } rec->spec_file = strdup(path); /* - * The do detailed validation of the input and fill the spec array + * Perform two passes over the specification file. + * The first pass counts the number of specifications and + * performs simple validation of the input. At the end + * of the first pass, the spec array is allocated. + * The second pass performs detailed validation of the input + * and fills in the spec array. */ - status = process_file(path, NULL, rec, prefix); - if (status) - goto finish; + maxnspec = UINT_MAX / sizeof(spec_t); + for (pass = 0; pass < 2; pass++) { + data->nspec = 0; + data->ncomp = 0; + + lineno = 0; + while (getline(&line_buf, &line_len, fp) > 0) { + if (data->nspec >= maxnspec) + break; + status = process_line(rec, path, prefix, line_buf, pass, ++lineno); + if (status) + goto finish; + } - if (rec->validating) { - status = nodups_specs(data, path); - if (status) - goto finish; - } + if (pass == 1 && rec->validating) { + status = nodups_specs(data, path); + if (status) + goto finish; + } - if (!baseonly) { - status = process_file(path, "homedirs", rec, prefix); - if (status && errno != ENOENT) - goto finish; + lineno = 0; + if (homedirfp) + while (getline(&line_buf, &line_len, homedirfp) > 0) { + if (data->nspec >= maxnspec) + break; + status = process_line(rec, homedir_path, prefix, line_buf, pass, ++lineno); + if (status) + goto finish; + } - status = process_file(path, "local", rec, prefix); - if (status && errno != ENOENT) - goto finish; + lineno = 0; + if (localfp) + while (getline(&line_buf, &line_len, localfp) > 0) { + if (data->nspec >= maxnspec) + break; + status = process_line(rec, local_path, prefix, line_buf, pass, ++lineno); + if (status) + goto finish; + } + + if (pass == 0) { + if (data->nspec == 0) { + status = 0; + goto finish; + } + data->spec_arr = calloc(data->nspec, sizeof(spec_t)); + if (!data->spec_arr) + goto finish; + + maxnspec = data->nspec; + rewind(fp); + if (homedirfp) + rewind(homedirfp); + if (localfp) + rewind(localfp); + } } + free(line_buf); - status = sort_specs(data); + /* Move exact pathname specifications to the end. */ + spec_copy = malloc(sizeof(spec_t) * data->nspec); + if (!spec_copy) + goto finish; + j = 0; + for (i = 0; i < data->nspec; i++) + if (data->spec_arr[i].hasMetaChars) + memcpy(&spec_copy[j++], + &data->spec_arr[i], sizeof(spec_t)); + for (i = 0; i < data->nspec; i++) + if (!data->spec_arr[i].hasMetaChars) + memcpy(&spec_copy[j++], + &data->spec_arr[i], sizeof(spec_t)); + free(data->spec_arr); + data->spec_arr = spec_copy; status = 0; finish: - if (status) + fclose(fp); + if (data->spec_arr != spec_copy) free(data->spec_arr); + if (homedirfp) + fclose(homedirfp); + if (localfp) + fclose(localfp); return status; } @@ -361,10 +575,7 @@ static void closef(struct selabel_handle *rec) free(spec->type_str); free(spec->lr.ctx_raw); free(spec->lr.ctx_trans); - if (spec->regcomp) { - pcre_free(spec->regex); - pcre_free_study(spec->sd); - } + regfree(&spec->regex); } for (i = 0; i < (unsigned int)data->num_stems; i++) { @@ -384,7 +595,7 @@ static struct selabel_lookup_rec *lookup(struct selabel_handle *rec, const char *key, int type) { struct saved_data *data = (struct saved_data *)rec->data; - struct spec *spec_arr = data->spec_arr; + spec_t *spec_arr = data->spec_arr; int i, rc, file_stem; mode_t mode = (mode_t)type; const char *buf; @@ -423,24 +634,26 @@ static struct selabel_lookup_rec *lookup(struct selabel_handle *rec, * the last matching specification is used. */ for (i = data->nspec - 1; i >= 0; i--) { - struct spec *spec = &spec_arr[i]; /* if the spec in question matches no stem or has the same * stem as the file AND if the spec in question has no mode * specified or if the mode matches the file mode then we do * a regex check */ - if ((spec->stem_id == -1 || spec->stem_id == file_stem) && - (!mode || !spec->mode || mode == spec->mode)) { - if (compile_regex(data, spec, NULL) < 0) + if ((spec_arr[i].stem_id == -1 + || spec_arr[i].stem_id == file_stem) + && (!mode || !spec_arr[i].mode + || mode == spec_arr[i].mode)) { + if (compile_regex(data, &spec_arr[i], NULL) < 0) goto finish; - if (spec->stem_id == -1) - rc = pcre_exec(spec->regex, get_pcre_extra(spec), key, strlen(key), 0, 0, NULL, 0); + if (spec_arr[i].stem_id == -1) + rc = regexec(&spec_arr[i].regex, key, 0, 0, 0); else - rc = pcre_exec(spec->regex, get_pcre_extra(spec), buf, strlen(buf), 0, 0, NULL, 0); + rc = regexec(&spec_arr[i].regex, buf, 0, 0, 0); if (rc == 0) { - spec->matches++; + spec_arr[i].matches++; break; - } else if (rc == PCRE_ERROR_NOMATCH) + } + if (rc == REG_NOMATCH) continue; /* else it's an error */ goto finish; @@ -464,7 +677,7 @@ static void stats(struct selabel_handle *rec) { struct saved_data *data = (struct saved_data *)rec->data; unsigned int i, nspec = data->nspec; - struct spec *spec_arr = data->spec_arr; + spec_t *spec_arr = data->spec_arr; for (i = 0; i < nspec; i++) { if (spec_arr[i].matches == 0) { diff --git a/src/label_file.h b/src/label_file.h deleted file mode 100644 index cb5633b..0000000 --- a/src/label_file.h +++ /dev/null @@ -1,273 +0,0 @@ -#ifndef _SELABEL_FILE_H_ -#define _SELABEL_FILE_H_ - -#include - -#include "label_internal.h" - -/* A file security context specification. */ -struct spec { - struct selabel_lookup_rec lr; /* holds contexts for lookup result */ - char *regex_str; /* regular expession string for diagnostics */ - char *type_str; /* type string for diagnostic messages */ - pcre *regex; /* compiled regular expression */ - pcre_extra *sd; /* extra compiled stuff */ - mode_t mode; /* mode format value */ - int matches; /* number of matching pathnames */ - int stem_id; /* indicates which stem-compression item */ - char hasMetaChars; /* regular expression has meta-chars */ - char regcomp; /* regex_str has been compiled to regex */ -}; - -/* A regular expression stem */ -struct stem { - char *buf; - int len; -}; - -/* Our stored configuration */ -struct saved_data { - /* - * The array of specifications, initially in the same order as in - * the specification file. Sorting occurs based on hasMetaChars. - */ - struct spec *spec_arr; - unsigned int nspec; - unsigned int alloc_specs; - - /* - * The array of regular expression stems. - */ - struct stem *stem_arr; - int num_stems; - int alloc_stems; -}; - -static inline pcre_extra *get_pcre_extra(struct spec *spec) -{ - return spec->sd; -} - -static inline mode_t string_to_mode(char *mode) -{ - size_t len; - - if (!mode) - return 0; - len = strlen(mode); - if (mode[0] != '-' || len != 2) - return -1; - switch (mode[1]) { - case 'b': - return S_IFBLK; - case 'c': - return S_IFCHR; - case 'd': - return S_IFDIR; - case 'p': - return S_IFIFO; - case 'l': - return S_IFLNK; - case 's': - return S_IFSOCK; - case '-': - return S_IFREG; - default: - return -1; - } - /* impossible to get here */ - return 0; -} - -static inline int grow_specs(struct saved_data *data) -{ - struct spec *specs; - size_t new_specs, total_specs; - - if (data->nspec < data->alloc_specs) - return 0; - - new_specs = data->nspec + 16; - total_specs = data->nspec + new_specs; - - specs = realloc(data->spec_arr, total_specs * sizeof(*specs)); - if (!specs) { - perror("realloc"); - return -1; - } - - /* blank the new entries */ - memset(&specs[data->nspec], 0, new_specs * sizeof(*specs)); - - data->spec_arr = specs; - data->alloc_specs = total_specs; - return 0; -} - -/* Determine if the regular expression specification has any meta characters. */ -static inline void spec_hasMetaChars(struct spec *spec) -{ - char *c; - int len; - char *end; - - c = spec->regex_str; - len = strlen(spec->regex_str); - end = c + len; - - spec->hasMetaChars = 0; - - /* Look at each character in the RE specification string for a - * meta character. Return when any meta character reached. */ - while (c < end) { - switch (*c) { - case '.': - case '^': - case '$': - case '?': - case '*': - case '+': - case '|': - case '[': - case '(': - case '{': - spec->hasMetaChars = 1; - return; - case '\\': /* skip the next character */ - c++; - break; - default: - break; - - } - c++; - } - return; -} - -/* Move exact pathname specifications to the end. */ -static inline int sort_specs(struct saved_data *data) -{ - struct spec *spec_copy; - struct spec spec; - int i; - int front, back; - size_t len = sizeof(*spec_copy); - - spec_copy = malloc(len * data->nspec); - if (!spec_copy) - return -1; - - /* first move the exact pathnames to the back */ - front = 0; - back = data->nspec - 1; - for (i = 0; i < data->nspec; i++) { - if (data->spec_arr[i].hasMetaChars) - memcpy(&spec_copy[front++], &data->spec_arr[i], len); - else - memcpy(&spec_copy[back--], &data->spec_arr[i], len); - } - - /* - * now the exact pathnames are at the end, but they are in the reverse order. - * since 'front' is now the first of the 'exact' we can run that part of the - * array switching the front and back element. - */ - back = data->nspec - 1; - while (front < back) { - /* save the front */ - memcpy(&spec, &spec_copy[front], len); - /* move the back to the front */ - memcpy(&spec_copy[front], &spec_copy[back], len); - /* put the old front in the back */ - memcpy(&spec_copy[back], &spec, len); - front++; - back--; - } - - free(data->spec_arr); - data->spec_arr = spec_copy; - - return 0; -} - -/* Return the length of the text that can be considered the stem, returns 0 - * if there is no identifiable stem */ -static inline int get_stem_from_spec(const char *const buf) -{ - const char *tmp = strchr(buf + 1, '/'); - const char *ind; - - if (!tmp) - return 0; - - for (ind = buf; ind < tmp; ind++) { - if (strchr(".^$?*+|[({", (int)*ind)) - return 0; - } - return tmp - buf; -} - -/* - * return the stemid given a string and a length - */ -static inline int find_stem(struct saved_data *data, const char *buf, int stem_len) -{ - int i; - - for (i = 0; i < data->num_stems; i++) { - if (stem_len == data->stem_arr[i].len && - !strncmp(buf, data->stem_arr[i].buf, stem_len)) - return i; - } - - return -1; -} - -/* returns the index of the new stored object */ -static inline int store_stem(struct saved_data *data, char *buf, int stem_len) -{ - int num = data->num_stems; - - if (data->alloc_stems == num) { - struct stem *tmp_arr; - - data->alloc_stems = data->alloc_stems * 2 + 16; - tmp_arr = realloc(data->stem_arr, - sizeof(*tmp_arr) * data->alloc_stems); - if (!tmp_arr) - return -1; - data->stem_arr = tmp_arr; - } - data->stem_arr[num].len = stem_len; - data->stem_arr[num].buf = buf; - data->num_stems++; - - return num; -} - -/* find the stem of a file spec, returns the index into stem_arr for a new - * or existing stem, (or -1 if there is no possible stem - IE for a file in - * the root directory or a regex that is too complex for us). */ -static inline int find_stem_from_spec(struct saved_data *data, const char *buf) -{ - int stem_len = get_stem_from_spec(buf); - int stemid; - char *stem; - - if (!stem_len) - return -1; - - stemid = find_stem(data, buf, stem_len); - if (stemid >= 0) - return stemid; - - /* not found, allocate a new one */ - stem = strndup(buf, stem_len); - if (!stem) - return -1; - - return store_stem(data, stem, stem_len); -} - -#endif /* _SELABEL_FILE_H_ */ -- 1.8.1.2