387 lines
8.6 KiB
C
387 lines
8.6 KiB
C
/* $NetBSD: pkg_io.c,v 1.11 2010/04/20 00:39:13 joerg Exp $ */
|
|
/*-
|
|
* Copyright (c) 2008, 2009 Joerg Sonnenberger <joerg@NetBSD.org>.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in
|
|
* the documentation and/or other materials provided with the
|
|
* distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
* COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
|
|
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#if HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
#ifndef __minix
|
|
#include <nbcompat.h>
|
|
#endif
|
|
#if HAVE_SYS_CDEFS_H
|
|
#include <sys/cdefs.h>
|
|
#endif
|
|
|
|
#ifndef __minix
|
|
__RCSID("$NetBSD: pkg_io.c,v 1.11 2010/04/20 00:39:13 joerg Exp $");
|
|
#endif
|
|
|
|
#include <archive.h>
|
|
#include <archive_entry.h>
|
|
#if HAVE_ERR_H
|
|
#include <err.h>
|
|
#endif
|
|
#if HAVE_ERRNO_H
|
|
#include <errno.h>
|
|
#endif
|
|
#include <fetch.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "lib.h"
|
|
|
|
struct pkg_path {
|
|
TAILQ_ENTRY(pkg_path) pl_link;
|
|
char *pl_path;
|
|
};
|
|
|
|
static char *orig_cwd, *last_toplevel;
|
|
static TAILQ_HEAD(, pkg_path) pkg_path = TAILQ_HEAD_INITIALIZER(pkg_path);
|
|
|
|
struct fetch_archive {
|
|
struct url *url;
|
|
fetchIO *fetch;
|
|
char buffer[32768];
|
|
off_t size;
|
|
int restart;
|
|
};
|
|
|
|
static int
|
|
fetch_archive_open(struct archive *a, void *client_data)
|
|
{
|
|
struct fetch_archive *f = client_data;
|
|
struct url_stat us;
|
|
|
|
f->fetch = fetchXGet(f->url, &us, fetch_flags);
|
|
if (f->fetch == NULL)
|
|
return ENOENT;
|
|
f->size = us.size;
|
|
f->restart = 1;
|
|
f->url->offset = 0;
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t
|
|
fetch_archive_read(struct archive *a, void *client_data,
|
|
const void **buffer)
|
|
{
|
|
struct fetch_archive *f = client_data;
|
|
struct url_stat us;
|
|
ssize_t rv;
|
|
|
|
*buffer = f->buffer;
|
|
rv = fetchIO_read(f->fetch, f->buffer, sizeof(f->buffer));
|
|
if (rv > 0) {
|
|
f->url->offset += rv;
|
|
return rv;
|
|
}
|
|
if (f->restart == 0)
|
|
return rv;
|
|
if (rv == 0) {
|
|
if (f->size == -1)
|
|
return 0;
|
|
if (f->url->offset == f->size)
|
|
return 0;
|
|
}
|
|
f->restart = 0;
|
|
if (1) {
|
|
char *url = fetchStringifyURL(f->url);
|
|
fprintf(stderr, "Trying to reconnect %s\n", url);
|
|
free(url);
|
|
}
|
|
fetchIO_close(f->fetch);
|
|
f->fetch = fetchXGet(f->url, &us, fetch_flags);
|
|
if (f->fetch == NULL)
|
|
return -1;
|
|
if (us.size != f->size)
|
|
return -1;
|
|
rv = fetchIO_read(f->fetch, f->buffer, sizeof(f->buffer));
|
|
if (rv > 0)
|
|
f->url->offset += rv;
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
fetch_archive_close(struct archive *a, void *client_data)
|
|
{
|
|
struct fetch_archive *f = client_data;
|
|
|
|
if (f->fetch != NULL)
|
|
fetchIO_close(f->fetch);
|
|
fetchFreeURL(f->url);
|
|
free(f);
|
|
return 0;
|
|
}
|
|
|
|
static struct archive *
|
|
open_archive_by_url(struct url *url, char **archive_name)
|
|
{
|
|
struct fetch_archive *f;
|
|
struct archive *a;
|
|
|
|
f = xmalloc(sizeof(*f));
|
|
f->url = fetchCopyURL(url);
|
|
|
|
*archive_name = fetchStringifyURL(url);
|
|
|
|
a = archive_read_new();
|
|
archive_read_support_compression_all(a);
|
|
archive_read_support_format_all(a);
|
|
if (archive_read_open(a, f, fetch_archive_open, fetch_archive_read,
|
|
fetch_archive_close)) {
|
|
free(*archive_name);
|
|
*archive_name = NULL;
|
|
archive_read_finish(a);
|
|
return NULL;
|
|
}
|
|
|
|
return a;
|
|
}
|
|
|
|
struct archive *
|
|
open_archive(const char *url, char **archive_name)
|
|
{
|
|
struct url *u;
|
|
struct archive *a;
|
|
|
|
*archive_name = NULL;
|
|
|
|
if (!IS_URL(url)) {
|
|
a = archive_read_new();
|
|
archive_read_support_compression_all(a);
|
|
archive_read_support_format_all(a);
|
|
if (archive_read_open_filename(a, url, 1024)) {
|
|
archive_read_close(a);
|
|
return NULL;
|
|
}
|
|
*archive_name = xstrdup(url);
|
|
return a;
|
|
}
|
|
|
|
if ((u = fetchParseURL(url)) == NULL)
|
|
return NULL;
|
|
|
|
a = open_archive_by_url(u, archive_name);
|
|
|
|
fetchFreeURL(u);
|
|
return a;
|
|
}
|
|
|
|
static int
|
|
strip_suffix(char *filename)
|
|
{
|
|
size_t len;
|
|
|
|
len = strlen(filename);
|
|
if (len <= 4)
|
|
return 0;
|
|
if (strcmp(filename + len - 4, ".tgz") == 0 ||
|
|
strcmp(filename + len - 4, ".tbz") == 0) {
|
|
filename[len - 4] = '\0';
|
|
return 1;
|
|
} else
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
find_best_package_int(struct url *url, const char *pattern,
|
|
struct url **best_url)
|
|
{
|
|
char *cur_match, *url_pattern, *best_match = NULL;
|
|
struct url_list ue;
|
|
size_t i;
|
|
|
|
if (*best_url) {
|
|
if ((best_match = fetchUnquoteFilename(*best_url)) == NULL)
|
|
return -1;
|
|
} else
|
|
best_match = NULL;
|
|
|
|
if (best_match && strip_suffix(best_match) == 0) {
|
|
free(best_match);
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; pattern[i] != '\0'; ++i) {
|
|
if (!isalnum((unsigned char)(pattern[i])) &&
|
|
(pattern[i]) != '-')
|
|
break;
|
|
}
|
|
url_pattern = xasprintf("%*.*s*", (int)i, (int)i, pattern);
|
|
|
|
fetchInitURLList(&ue);
|
|
if (fetchList(&ue, url, url_pattern, fetch_flags)) {
|
|
char *base_url;
|
|
base_url = fetchStringifyURL(url);
|
|
warnx("Can't process %s/%s: %s", base_url, url_pattern,
|
|
fetchLastErrString);
|
|
free(base_url);
|
|
free(url_pattern);
|
|
fetchFreeURLList(&ue);
|
|
return -1;
|
|
}
|
|
free(url_pattern);
|
|
|
|
for (i = 0; i < ue.length; ++i) {
|
|
cur_match = fetchUnquoteFilename(ue.urls + i);
|
|
|
|
if (cur_match == NULL) {
|
|
free(best_match);
|
|
fetchFreeURLList(&ue);
|
|
return -1;
|
|
}
|
|
if (strip_suffix(cur_match) == 0) {
|
|
free(cur_match);
|
|
continue;
|
|
}
|
|
if (pkg_order(pattern, cur_match, best_match) == 1) {
|
|
if (*best_url)
|
|
fetchFreeURL(*best_url);
|
|
*best_url = fetchCopyURL(ue.urls + i);
|
|
free(best_match);
|
|
best_match = cur_match;
|
|
cur_match = NULL;
|
|
if (*best_url == NULL) {
|
|
free(best_match);
|
|
return -1;
|
|
}
|
|
}
|
|
free(cur_match);
|
|
}
|
|
free(best_match);
|
|
fetchFreeURLList(&ue);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
process_pkg_path(void)
|
|
{
|
|
char cwd[PATH_MAX];
|
|
int relative_path;
|
|
struct pkg_path *pl;
|
|
const char *start, *next;
|
|
size_t len;
|
|
|
|
if (getcwd(cwd, sizeof(cwd)) == NULL)
|
|
errx(EXIT_FAILURE, "getcwd failed");
|
|
|
|
orig_cwd = xstrdup(cwd);
|
|
|
|
if (config_pkg_path == NULL)
|
|
return;
|
|
|
|
for (start = config_pkg_path; *start; start = next) {
|
|
len = strcspn(start, ";");
|
|
if (*(next = start + len) != '\0')
|
|
++next;
|
|
|
|
relative_path = !IS_FULLPATH(start) && !IS_URL(start);
|
|
pl = xmalloc(sizeof(*pl));
|
|
pl->pl_path = xasprintf("%s%s%*.*s",
|
|
relative_path ? cwd : "", len && relative_path ? "/" : "",
|
|
(int)len, (int)len, start);
|
|
TAILQ_INSERT_TAIL(&pkg_path, pl, pl_link);
|
|
}
|
|
}
|
|
|
|
struct url *
|
|
find_best_package(const char *toplevel, const char *pattern, int do_path)
|
|
{
|
|
struct url *url, *best_match = NULL;
|
|
struct pkg_path *pl;
|
|
|
|
if (toplevel) {
|
|
url = fetchParseURL(last_toplevel);
|
|
if (url != NULL) {
|
|
find_best_package_int(url, pattern, &best_match);
|
|
/* XXX Check return value and complain */
|
|
fetchFreeURL(url);
|
|
}
|
|
}
|
|
if (!do_path)
|
|
return best_match;
|
|
|
|
TAILQ_FOREACH(pl, &pkg_path, pl_link) {
|
|
url = fetchParseURL(pl->pl_path);
|
|
if (url != NULL) {
|
|
find_best_package_int(url, pattern, &best_match);
|
|
/* XXX Check return value and complain */
|
|
fetchFreeURL(url);
|
|
}
|
|
}
|
|
|
|
return best_match;
|
|
}
|
|
|
|
struct archive *
|
|
find_archive(const char *fname, int top_level, char **archive_name)
|
|
{
|
|
struct archive *a;
|
|
struct url *best_match;
|
|
char *full_fname, *last_slash;
|
|
int search_path;
|
|
|
|
search_path = 0;
|
|
if (IS_FULLPATH(fname) || IS_URL(fname)) {
|
|
full_fname = xstrdup(fname);
|
|
} else {
|
|
if (strchr(fname, '/') == NULL)
|
|
search_path = 1;
|
|
full_fname = xasprintf("%s/%s", orig_cwd, fname);
|
|
}
|
|
|
|
last_slash = strrchr(full_fname, '/');
|
|
if (top_level) {
|
|
free(last_toplevel);
|
|
*last_slash = '\0';
|
|
last_toplevel = xstrdup(full_fname);
|
|
*last_slash = '/';
|
|
}
|
|
|
|
a = open_archive(full_fname, archive_name);
|
|
if (a != NULL) {
|
|
free(full_fname);
|
|
return a;
|
|
}
|
|
|
|
fname = last_slash + 1;
|
|
*last_slash = '\0';
|
|
|
|
best_match = find_best_package(full_fname, fname, 0);
|
|
|
|
if (search_path && best_match == NULL)
|
|
best_match = find_best_package(last_toplevel, fname, 1);
|
|
|
|
free(full_fname);
|
|
|
|
if (best_match == NULL)
|
|
return NULL;
|
|
a = open_archive_by_url(best_match, archive_name);
|
|
fetchFreeURL(best_match);
|
|
return a;
|
|
}
|