match.c revision 118313
1/* 2 * FreeBSD install - a package for the installation and maintainance 3 * of non-core utilities. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * Maxim Sobolev 15 * 24 February 2001 16 * 17 * Routines used to query installed packages. 18 * 19 */ 20 21#include <sys/cdefs.h> 22__FBSDID("$FreeBSD: head/usr.sbin/pkg_install/lib/match.c 118313 2003-08-01 17:26:41Z bmilekic $"); 23 24#include "lib.h" 25#include <err.h> 26#include <fnmatch.h> 27#include <fts.h> 28#include <regex.h> 29 30/* 31 * Simple structure representing argv-like 32 * NULL-terminated list. 33 */ 34struct store { 35 int currlen; 36 int used; 37 char **store; 38}; 39 40static int rex_match(const char *, const char *); 41struct store *storecreate(struct store *); 42static int storeappend(struct store *, const char *); 43static int fname_cmp(const FTSENT * const *, const FTSENT * const *); 44 45/* 46 * Function to query names of installed packages. 47 * MatchType - one of MATCH_ALL, MATCH_REGEX, MATCH_GLOB; 48 * patterns - NULL-terminated list of glob or regex patterns 49 * (could be NULL for MATCH_ALL); 50 * retval - return value (could be NULL if you don't want/need 51 * return value). 52 * Returns NULL-terminated list with matching names. 53 * Names in list returned are dynamically allocated and should 54 * not be altered by the caller. 55 */ 56char ** 57matchinstalled(match_t MatchType, char **patterns, int *retval) 58{ 59 int i, errcode, len; 60 char *matched; 61 const char *paths[2] = {LOG_DIR, NULL}; 62 static struct store *store = NULL; 63 FTS *ftsp; 64 FTSENT *f; 65 Boolean *lmatched; 66 67 store = storecreate(store); 68 if (store == NULL) { 69 if (retval != NULL) 70 *retval = 1; 71 return NULL; 72 } 73 74 if (retval != NULL) 75 *retval = 0; 76 77 if (!isdir(paths[0])) { 78 if (retval != NULL) 79 *retval = 1; 80 return NULL; 81 /* Not reached */ 82 } 83 84 /* Count number of patterns */ 85 if (patterns != NULL) { 86 for (len = 0; patterns[len]; len++) {} 87 lmatched = alloca(sizeof(*lmatched) * len); 88 if (lmatched == NULL) { 89 warnx("%s(): alloca() failed", __func__); 90 if (retval != NULL) 91 *retval = 1; 92 return NULL; 93 } 94 } else 95 len = 0; 96 97 for (i = 0; i < len; i++) 98 lmatched[i] = FALSE; 99 100 ftsp = fts_open((char * const *)(uintptr_t)paths, FTS_LOGICAL | FTS_NOCHDIR | FTS_NOSTAT, fname_cmp); 101 if (ftsp != NULL) { 102 while ((f = fts_read(ftsp)) != NULL) { 103 if (f->fts_info == FTS_D && f->fts_level == 1) { 104 fts_set(ftsp, f, FTS_SKIP); 105 matched = NULL; 106 errcode = 0; 107 if (MatchType == MATCH_ALL) 108 matched = f->fts_name; 109 else 110 for (i = 0; patterns[i]; i++) { 111 switch (MatchType) { 112 case MATCH_REGEX: 113 errcode = rex_match(patterns[i], f->fts_name); 114 if (errcode == 1) { 115 matched = f->fts_name; 116 errcode = 0; 117 } 118 break; 119 case MATCH_GLOB: 120 if (fnmatch(patterns[i], f->fts_name, 0) == 0) { 121 matched = f->fts_name; 122 lmatched[i] = TRUE; 123 } 124 break; 125 default: 126 break; 127 } 128 if (matched != NULL || errcode != 0) 129 break; 130 } 131 if (errcode == 0 && matched != NULL) 132 errcode = storeappend(store, matched); 133 if (errcode != 0) { 134 if (retval != NULL) 135 *retval = 1; 136 return NULL; 137 /* Not reached */ 138 } 139 } 140 } 141 fts_close(ftsp); 142 } 143 144 if (MatchType == MATCH_GLOB) { 145 for (i = 0; i < len; i++) 146 if (lmatched[i] == FALSE) 147 storeappend(store, patterns[i]); 148 } 149 150 if (store->used == 0) 151 return NULL; 152 else 153 return store->store; 154} 155 156/* 157 * Synopsis is similar to matchinstalled(), but use origin 158 * as a key for matching packages. 159 */ 160char ** 161matchbyorigin(const char *origin, int *retval) 162{ 163 char **installed; 164 int i; 165 static struct store *store = NULL; 166 167 store = storecreate(store); 168 if (store == NULL) { 169 if (retval != NULL) 170 *retval = 1; 171 return NULL; 172 } 173 174 if (retval != NULL) 175 *retval = 0; 176 177 installed = matchinstalled(MATCH_ALL, NULL, retval); 178 if (installed == NULL) 179 return NULL; 180 181 for (i = 0; installed[i] != NULL; i++) { 182 FILE *fp; 183 char *cp, tmp[PATH_MAX]; 184 int cmd; 185 186 snprintf(tmp, PATH_MAX, "%s/%s", LOG_DIR, installed[i]); 187 /* 188 * SPECIAL CASE: ignore empty dirs, since we can can see them 189 * during port installation. 190 */ 191 if (isemptydir(tmp)) 192 continue; 193 snprintf(tmp, PATH_MAX, "%s/%s", tmp, CONTENTS_FNAME); 194 fp = fopen(tmp, "r"); 195 if (fp == NULL) { 196 warn("%s", tmp); 197 if (retval != NULL) 198 *retval = 1; 199 return NULL; 200 } 201 202 cmd = -1; 203 while (fgets(tmp, sizeof(tmp), fp)) { 204 int len = strlen(tmp); 205 206 while (len && isspace(tmp[len - 1])) 207 tmp[--len] = '\0'; 208 if (!len) 209 continue; 210 cp = tmp; 211 if (tmp[0] != CMD_CHAR) 212 continue; 213 cmd = plist_cmd(tmp + 1, &cp); 214 if (cmd == PLIST_ORIGIN) { 215 if (strncmp(origin, cp, strlen(origin)) == 0) 216 storeappend(store, installed[i]); 217 break; 218 } 219 } 220 if (cmd != PLIST_ORIGIN) 221 warnx("package %s has no origin recorded", installed[i]); 222 fclose(fp); 223 } 224 225 if (store->used == 0) 226 return NULL; 227 else 228 return store->store; 229} 230 231/* 232 * Return TRUE if the specified package is installed, 233 * or FALSE otherwise. 234 */ 235int 236isinstalledpkg(const char *name) 237{ 238 char buf[FILENAME_MAX]; 239 240 snprintf(buf, sizeof(buf), "%s/%s", LOG_DIR, name); 241 if (!isdir(buf) || access(buf, R_OK) == FAIL) 242 return FALSE; 243 244 snprintf(buf, sizeof(buf), "%s/%s", buf, CONTENTS_FNAME); 245 if (!isfile(buf) || access(buf, R_OK) == FAIL) 246 return FALSE; 247 248 return TRUE; 249} 250 251/* 252 * Returns 1 if specified pkgname matches RE pattern. 253 * Otherwise returns 0 if doesn't match or -1 if RE 254 * engine reported an error (usually invalid syntax). 255 */ 256static int 257rex_match(const char *pattern, const char *pkgname) 258{ 259 char errbuf[128]; 260 int errcode; 261 int retval; 262 regex_t rex; 263 264 retval = 0; 265 266 errcode = regcomp(&rex, pattern, REG_BASIC | REG_NOSUB); 267 if (errcode == 0) 268 errcode = regexec(&rex, pkgname, 0, NULL, 0); 269 270 if (errcode == 0) { 271 retval = 1; 272 } else if (errcode != REG_NOMATCH) { 273 regerror(errcode, &rex, errbuf, sizeof(errbuf)); 274 warnx("%s: %s", pattern, errbuf); 275 retval = -1; 276 } 277 278 regfree(&rex); 279 280 return retval; 281} 282 283/* 284 * Create an empty store, optionally deallocating 285 * any previously allocated space if store != NULL. 286 */ 287struct store * 288storecreate(struct store *store) 289{ 290 int i; 291 292 if (store == NULL) { 293 store = malloc(sizeof *store); 294 if (store == NULL) { 295 warnx("%s(): malloc() failed", __func__); 296 return NULL; 297 } 298 store->currlen = 0; 299 store->store = NULL; 300 } else if (store->store != NULL) { 301 /* Free previously allocated memory */ 302 for (i = 0; store->store[i] != NULL; i++) 303 free(store->store[i]); 304 store->store[0] = NULL; 305 } 306 store->used = 0; 307 308 return store; 309} 310 311/* 312 * Append specified element to the provided store. 313 */ 314static int 315storeappend(struct store *store, const char *item) 316{ 317 if (store->used + 2 > store->currlen) { 318 store->currlen += 16; 319 store->store = reallocf(store->store, 320 store->currlen * sizeof(*(store->store))); 321 if (store->store == NULL) { 322 store->currlen = 0; 323 warnx("%s(): reallocf() failed", __func__); 324 return 1; 325 } 326 } 327 328 asprintf(&(store->store[store->used]), "%s", item); 329 if (store->store[store->used] == NULL) { 330 warnx("%s(): malloc() failed", __func__); 331 return 1; 332 } 333 store->used++; 334 store->store[store->used] = NULL; 335 336 return 0; 337} 338 339static int 340fname_cmp(const FTSENT * const *a, const FTSENT * const *b) 341{ 342 return strcmp((*a)->fts_name, (*b)->fts_name); 343} 344