match.c revision 74258
1#ifndef lint 2static const char rcsid[] = 3 "$FreeBSD: head/usr.sbin/pkg_install/lib/match.c 74258 2001-03-14 19:46:43Z 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 int 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, errcode, len; 62 char *tmp, *matched; 63 char *paths[2]; 64 static struct store *store = NULL; 65 FTS *ftsp; 66 FTSENT *f; 67 Boolean *lmatched; 68 69 if (store == NULL) { 70 store = malloc(sizeof *store); 71 if (store == NULL) { 72 warnx("%s(): malloc() failed", __FUNCTION__); 73 if (retval != NULL) 74 *retval = 1; 75 return NULL; 76 } 77 store->currlen = 0; 78 store->store = NULL; 79 } else 80 if (store->store != NULL) 81 /* Free previously allocated memory */ 82 for (i = 0; store->store[i] != NULL; i++) 83 free(store->store[i]); 84 store->used = 0; 85 86 if (retval != NULL) 87 *retval = 0; 88 89 tmp = getenv(PKG_DBDIR); 90 if (!tmp) 91 tmp = DEF_LOG_DIR; 92 if (!isdir(tmp)) { 93 if (retval != NULL) 94 *retval = 1; 95 return NULL; 96 /* Not reached */ 97 } 98 99 /* Count number of patterns */ 100 for (len = 0; patterns[len]; len++) {} 101 lmatched = alloca(sizeof(*lmatched) * len); 102 if (lmatched == NULL) { 103 warnx("%s(): alloca() failed", __FUNCTION__); 104 if (retval != NULL) 105 *retval = 1; 106 return NULL; 107 } 108 for (i = 0; i < len; i++) 109 lmatched[i] = FALSE; 110 111 paths[0] = tmp; 112 paths[1] = NULL; 113 ftsp = fts_open(paths, FTS_LOGICAL | FTS_NOCHDIR | FTS_NOSTAT, fname_cmp); 114 if (ftsp != NULL) { 115 while ((f = fts_read(ftsp)) != NULL) { 116 if (f->fts_info == FTS_D && f->fts_level == 1) { 117 fts_set(ftsp, f, FTS_SKIP); 118 matched = NULL; 119 errcode = 0; 120 if (MatchType == MATCH_ALL) 121 matched = f->fts_name; 122 else 123 for (i = 0; patterns[i]; i++) { 124 switch (MatchType) { 125 case MATCH_REGEX: 126 errcode = rex_match(patterns[i], f->fts_name); 127 if (errcode == 1) { 128 matched = f->fts_name; 129 errcode = 0; 130 } 131 break; 132 case MATCH_GLOB: 133 if (fnmatch(patterns[i], f->fts_name, 0) == 0) { 134 matched = f->fts_name; 135 lmatched[i] = TRUE; 136 } 137 break; 138 default: 139 break; 140 } 141 if (matched != NULL || errcode != 0) 142 break; 143 } 144 if (errcode == 0 && matched != NULL) 145 errcode = storeappend(store, matched); 146 if (errcode != 0) { 147 if (retval != NULL) 148 *retval = 1; 149 return NULL; 150 /* Not reached */ 151 } 152 } 153 } 154 fts_close(ftsp); 155 } 156 157 if (MatchType == MATCH_GLOB) { 158 for (i = 0; i < len; i++) 159 if (lmatched[i] == FALSE) 160 storeappend(store, patterns[i]); 161 } 162 163 if (store->used == 0) 164 return NULL; 165 else 166 return store->store; 167} 168 169/* 170 * Returns 1 if specified pkgname matches RE pattern. 171 * Otherwise returns 0 if doesn't match or -1 if RE 172 * engine reported an error (usually invalid syntax). 173 */ 174static int 175rex_match(char *pattern, char *pkgname) 176{ 177 char errbuf[128]; 178 int errcode; 179 int retval; 180 regex_t rex; 181 182 retval = 0; 183 184 errcode = regcomp(&rex, pattern, REG_BASIC | REG_NOSUB); 185 if (errcode == 0) 186 errcode = regexec(&rex, pkgname, 0, NULL, 0); 187 188 if (errcode == 0) { 189 retval = 1; 190 } else if (errcode != REG_NOMATCH) { 191 regerror(errcode, &rex, errbuf, sizeof(errbuf)); 192 warnx("%s: %s", pattern, errbuf); 193 retval = -1; 194 } 195 196 regfree(&rex); 197 198 return retval; 199} 200 201static int 202storeappend(struct store *store, const char *item) 203{ 204 char **tmp; 205 206 if (store->used + 2 > store->currlen) { 207 tmp = store->store; 208 store->currlen += 16; 209 store->store = malloc(store->currlen * sizeof(*(store->store))); 210 if (store->store == NULL) { 211 warnx("%s(): malloc() failed", __FUNCTION__); 212 return 1; 213 } 214 memcpy(store->store, tmp, store->used * sizeof(*(store->store))); 215 free(tmp); 216 } 217 218 asprintf(&(store->store[store->used]), "%s", item); 219 if (store->store[store->used] == NULL) { 220 warnx("%s(): malloc() failed", __FUNCTION__); 221 return 1; 222 } 223 store->used++; 224 store->store[store->used] = NULL; 225 226 return 0; 227} 228 229static int 230fname_cmp(const FTSENT **a, const FTSENT **b) 231{ 232 return strcmp((*a)->fts_name, (*b)->fts_name); 233} 234