xref: /netbsd-src/external/bsd/pkg_install/dist/lib/pkg_io.c (revision 4e6df137e8e14049b5a701d249962c480449c141)
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