1 /* $NetBSD: pkg_io.c,v 1.1.1.8 2010/02/20 04:41:57 joerg Exp $ */ 2 /*- 3 * Copyright (c) 2008, 2009 Joerg Sonnenberger <joerg@NetBSD.org>. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in 14 * the documentation and/or other materials provided with the 15 * distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 21 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 27 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #if HAVE_CONFIG_H 32 #include "config.h" 33 #endif 34 #include <nbcompat.h> 35 #if HAVE_SYS_CDEFS_H 36 #include <sys/cdefs.h> 37 #endif 38 39 __RCSID("$NetBSD: pkg_io.c,v 1.1.1.8 2010/02/20 04:41:57 joerg Exp $"); 40 41 #include <archive.h> 42 #include <archive_entry.h> 43 #if HAVE_ERR_H 44 #include <err.h> 45 #endif 46 #if HAVE_ERRNO_H 47 #include <errno.h> 48 #endif 49 #include <fetch.h> 50 #include <stdlib.h> 51 52 #include "lib.h" 53 54 struct pkg_path { 55 TAILQ_ENTRY(pkg_path) pl_link; 56 char *pl_path; 57 }; 58 59 static char *orig_cwd, *last_toplevel; 60 static TAILQ_HEAD(, pkg_path) pkg_path = TAILQ_HEAD_INITIALIZER(pkg_path); 61 62 struct fetch_archive { 63 struct url *url; 64 fetchIO *fetch; 65 char buffer[32768]; 66 }; 67 68 static int 69 fetch_archive_open(struct archive *a, void *client_data) 70 { 71 struct fetch_archive *f = client_data; 72 73 f->fetch = fetchGet(f->url, fetch_flags); 74 if (f->fetch == NULL) 75 return ENOENT; 76 return 0; 77 } 78 79 static ssize_t 80 fetch_archive_read(struct archive *a, void *client_data, 81 const void **buffer) 82 { 83 struct fetch_archive *f = client_data; 84 85 *buffer = f->buffer; 86 return fetchIO_read(f->fetch, f->buffer, sizeof(f->buffer)); 87 } 88 89 static int 90 fetch_archive_close(struct archive *a, void *client_data) 91 { 92 struct fetch_archive *f = client_data; 93 94 if (f->fetch != NULL) 95 fetchIO_close(f->fetch); 96 free(f); 97 return 0; 98 } 99 100 static struct archive * 101 open_archive_by_url(struct url *url, char **archive_name) 102 { 103 struct fetch_archive *f; 104 struct archive *a; 105 106 f = xmalloc(sizeof(*f)); 107 f->url = url; 108 109 *archive_name = fetchStringifyURL(url); 110 111 a = archive_read_new(); 112 archive_read_support_compression_all(a); 113 archive_read_support_format_all(a); 114 if (archive_read_open(a, f, fetch_archive_open, fetch_archive_read, 115 fetch_archive_close)) { 116 free(*archive_name); 117 *archive_name = NULL; 118 archive_read_finish(a); 119 return NULL; 120 } 121 122 return a; 123 } 124 125 struct archive * 126 open_archive(const char *url, char **archive_name) 127 { 128 struct url *u; 129 struct archive *a; 130 131 *archive_name = NULL; 132 133 if (!IS_URL(url)) { 134 a = archive_read_new(); 135 archive_read_support_compression_all(a); 136 archive_read_support_format_all(a); 137 if (archive_read_open_filename(a, url, 1024)) { 138 archive_read_close(a); 139 return NULL; 140 } 141 *archive_name = xstrdup(url); 142 return a; 143 } 144 145 if ((u = fetchParseURL(url)) == NULL) 146 return NULL; 147 148 a = open_archive_by_url(u, archive_name); 149 150 fetchFreeURL(u); 151 return a; 152 } 153 154 static int 155 strip_suffix(char *filename) 156 { 157 size_t len; 158 159 len = strlen(filename); 160 if (len <= 4) 161 return 0; 162 if (strcmp(filename + len - 4, ".tgz") == 0 || 163 strcmp(filename + len - 4, ".tbz") == 0) { 164 filename[len - 4] = '\0'; 165 return 1; 166 } else 167 return 0; 168 } 169 170 static int 171 find_best_package_int(struct url *url, const char *pattern, 172 struct url **best_url) 173 { 174 char *cur_match, *url_pattern, *best_match = NULL; 175 struct url_list ue; 176 size_t i; 177 178 if (*best_url) { 179 if ((best_match = fetchUnquoteFilename(*best_url)) == NULL) 180 return -1; 181 } else 182 best_match = NULL; 183 184 if (best_match && strip_suffix(best_match) == 0) { 185 free(best_match); 186 return -1; 187 } 188 189 for (i = 0; pattern[i] != '\0'; ++i) { 190 if (!isalnum((unsigned char)(pattern[i])) && 191 (pattern[i]) != '-') 192 break; 193 } 194 url_pattern = xasprintf("%*.*s*", (int)i, (int)i, pattern); 195 196 fetchInitURLList(&ue); 197 if (fetchList(&ue, url, url_pattern, fetch_flags)) { 198 char *base_url; 199 base_url = fetchStringifyURL(url); 200 warnx("Can't process %s/%s: %s", base_url, url_pattern, 201 fetchLastErrString); 202 free(base_url); 203 free(url_pattern); 204 fetchFreeURLList(&ue); 205 return -1; 206 } 207 free(url_pattern); 208 209 for (i = 0; i < ue.length; ++i) { 210 cur_match = fetchUnquoteFilename(ue.urls + i); 211 212 if (cur_match == NULL) { 213 free(best_match); 214 fetchFreeURLList(&ue); 215 return -1; 216 } 217 if (strip_suffix(cur_match) == 0) { 218 free(cur_match); 219 continue; 220 } 221 if (pkg_order(pattern, cur_match, best_match) == 1) { 222 if (*best_url) 223 fetchFreeURL(*best_url); 224 *best_url = fetchCopyURL(ue.urls + i); 225 free(best_match); 226 best_match = cur_match; 227 cur_match = NULL; 228 if (*best_url == NULL) { 229 free(best_match); 230 return -1; 231 } 232 } 233 free(cur_match); 234 } 235 free(best_match); 236 fetchFreeURLList(&ue); 237 return 0; 238 } 239 240 void 241 process_pkg_path(void) 242 { 243 char cwd[PATH_MAX]; 244 int relative_path; 245 struct pkg_path *pl; 246 const char *start, *next; 247 size_t len; 248 249 if (getcwd(cwd, sizeof(cwd)) == NULL) 250 errx(EXIT_FAILURE, "getcwd failed"); 251 252 orig_cwd = xstrdup(cwd); 253 254 if (config_pkg_path == NULL) 255 return; 256 257 for (start = config_pkg_path; *start; start = next) { 258 len = strcspn(start, ";"); 259 if (*(next = start + len) != '\0') 260 ++next; 261 262 relative_path = !IS_FULLPATH(start) && !IS_URL(start); 263 pl = xmalloc(sizeof(*pl)); 264 pl->pl_path = xasprintf("%s%s%*.*s", 265 relative_path ? cwd : "", len && relative_path ? "/" : "", 266 (int)len, (int)len, start); 267 TAILQ_INSERT_TAIL(&pkg_path, pl, pl_link); 268 } 269 } 270 271 struct url * 272 find_best_package(const char *toplevel, const char *pattern, int do_path) 273 { 274 struct url *url, *best_match = NULL; 275 struct pkg_path *pl; 276 277 if (toplevel) { 278 url = fetchParseURL(last_toplevel); 279 if (url != NULL) { 280 find_best_package_int(url, pattern, &best_match); 281 /* XXX Check return value and complain */ 282 fetchFreeURL(url); 283 } 284 } 285 if (!do_path) 286 return best_match; 287 288 TAILQ_FOREACH(pl, &pkg_path, pl_link) { 289 url = fetchParseURL(pl->pl_path); 290 if (url != NULL) { 291 find_best_package_int(url, pattern, &best_match); 292 /* XXX Check return value and complain */ 293 fetchFreeURL(url); 294 } 295 } 296 297 return best_match; 298 } 299 300 struct archive * 301 find_archive(const char *fname, int top_level, char **archive_name) 302 { 303 struct archive *a; 304 struct url *best_match; 305 char *full_fname, *last_slash; 306 int search_path; 307 308 search_path = 0; 309 if (IS_FULLPATH(fname) || IS_URL(fname)) { 310 full_fname = xstrdup(fname); 311 } else { 312 if (strchr(fname, '/') == NULL) 313 search_path = 1; 314 full_fname = xasprintf("%s/%s", orig_cwd, fname); 315 } 316 317 last_slash = strrchr(full_fname, '/'); 318 if (top_level) { 319 free(last_toplevel); 320 *last_slash = '\0'; 321 last_toplevel = xstrdup(full_fname); 322 *last_slash = '/'; 323 } 324 325 a = open_archive(full_fname, archive_name); 326 if (a != NULL) { 327 free(full_fname); 328 return a; 329 } 330 331 fname = last_slash + 1; 332 *last_slash = '\0'; 333 334 best_match = find_best_package(full_fname, fname, 0); 335 336 if (search_path && best_match == NULL) 337 best_match = find_best_package(last_toplevel, fname, 1); 338 339 free(full_fname); 340 341 if (best_match == NULL) 342 return NULL; 343 a = open_archive_by_url(best_match, archive_name); 344 fetchFreeURL(best_match); 345 return a; 346 } 347