match.c revision 73134
1#ifndef lint
2static const char rcsid[] =
3  "$FreeBSD: head/usr.sbin/pkg_install/lib/match.c 73134 2001-02-27 09:00:18Z sobomax $";
4#endif
5
6/*
7 * FreeBSD install - a package for the installation and maintainance
8 * of non-core utilities.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * Maxim Sobolev
20 * 24 February 2001
21 *
22 * Routines used to query installed packages.
23 *
24 */
25
26#include "lib.h"
27
28#include <err.h>
29#include <fnmatch.h>
30#include <fts.h>
31#include <regex.h>
32
33/*
34 * Simple structure representing argv-like
35 * NULL-terminated list.
36 */
37struct store {
38    int currlen;
39    int used;
40    char **store;
41};
42
43static int rex_match(char *, char *);
44static void storeappend(struct store *, const char *);
45static int fname_cmp(const FTSENT **, const FTSENT **);
46
47/*
48 * Function to query names of installed packages.
49 * MatchType	- one of MATCH_ALL, MATCH_REGEX, MATCH_GLOB;
50 * patterns	- NULL-terminated list of glob or regex patterns
51 *		  (could be NULL for MATCH_ALL);
52 * retval	- return value (could be NULL if you don't want/need
53 *		  return value).
54 * Returns NULL-terminated list with matching names.
55 * Names in list returned are dynamically allocated and should
56 * not be altered by the caller.
57 */
58char **
59matchinstalled(match_t MatchType, char **patterns, int *retval)
60{
61    int i, matched, errcode;
62    char *tmp;
63    char *paths[2];
64    static struct store *store = NULL;
65    FTS *ftsp;
66    FTSENT *f;
67
68    if (store == NULL) {
69	store = malloc(sizeof *store);
70	store->currlen = 0;
71	store->store = NULL;
72    } else {
73	if (store->store != NULL) {
74	    /* Free previously allocated memory */
75	    for (i = 0; store->store[i] != NULL; i++)
76		free(store->store[i]);
77	}
78    }
79    store->used = 0;
80
81    if (retval != NULL)
82	*retval = 0;
83
84    tmp = getenv(PKG_DBDIR);
85    if (!tmp)
86	tmp = DEF_LOG_DIR;
87    if (!isdir(tmp)) {
88	if (retval != NULL)
89	    *retval = 1;
90	return NULL;
91	/* Not reached */
92    }
93
94    paths[0] = tmp;
95    paths[1] = NULL;
96    ftsp = fts_open(paths, FTS_LOGICAL | FTS_NOCHDIR | FTS_NOSTAT, fname_cmp);
97    if (ftsp != NULL) {
98	while ((f = fts_read(ftsp)) != NULL) {
99	    if (f->fts_info == FTS_D && f->fts_level == 1) {
100		fts_set(ftsp, f, FTS_SKIP);
101		if (MatchType == MATCH_ALL) {
102		    storeappend(store, f->fts_name);
103		    continue;
104		}
105		for (i = 0; patterns[i]; i++) {
106		    matched = 0;
107		    switch (MatchType) {
108		    case MATCH_REGEX:
109			errcode = rex_match(patterns[i], f->fts_name);
110			if (errcode == 1) {
111			    storeappend(store, f->fts_name);
112			    matched = 1;
113			} else if (errcode == -1) {
114			    if (retval != NULL)
115				*retval = 1;
116			    return NULL;
117			    /* Not reached */
118			}
119			break;
120		    case MATCH_GLOB:
121			if (fnmatch(patterns[i], f->fts_name, 0) == 0) {
122			    storeappend(store, f->fts_name);
123			    matched = 1;
124			}
125			break;
126		    default:
127			break;
128		    }
129		    if (matched == 1)
130			break;
131		}
132	    }
133	}
134	fts_close(ftsp);
135    }
136
137    if (store->used == 0)
138	return NULL;
139    else
140	return store->store;
141}
142
143/*
144 * Returns 1 if specified pkgname matches RE pattern.
145 * Otherwise returns 0 if doesn't match or -1 if RE
146 * engine reported an error (usually invalid syntax).
147 */
148static int
149rex_match(char *pattern, char *pkgname)
150{
151    char errbuf[128];
152    int errcode;
153    int retval;
154    regex_t rex;
155
156    retval = 0;
157
158    errcode = regcomp(&rex, pattern, REG_BASIC | REG_NOSUB);
159    if (errcode == 0)
160	errcode = regexec(&rex, pkgname, 0, NULL, 0);
161
162    if (errcode == 0) {
163	retval = 1;
164    } else if (errcode != REG_NOMATCH) {
165	regerror(errcode, &rex, errbuf, sizeof(errbuf));
166	warnx("%s: %s", pattern, errbuf);
167	retval = -1;
168    }
169
170    regfree(&rex);
171
172    return retval;
173}
174
175static void
176storeappend(struct store *store, const char *item)
177{
178    char **tmp;
179
180    if (store->used + 2 > store->currlen) {
181	tmp = store->store;
182	store->currlen += 16;
183	store->store = malloc(store->currlen * sizeof(*(store->store)));
184	memcpy(store->store, tmp, store->used * sizeof(*(store->store)));
185	free(tmp);
186    }
187
188    asprintf(&(store->store[store->used]), "%s", item);
189    store->used++;
190    store->store[store->used] = NULL;
191}
192
193static int
194fname_cmp(const FTSENT **a, const FTSENT **b)
195{
196    return strcmp((*a)->fts_name, (*b)->fts_name);
197}
198