xref: /netbsd-src/external/bsd/pkg_install/dist/lib/license.c (revision a5847cc334d9a7029f6352b847e9e8d71a0f9e0c)
1 /*	$NetBSD: license.c,v 1.2 2011/04/22 08:19:25 adam Exp $	*/
2 
3 /*-
4  * Copyright (c) 2009 Joerg Sonnenberger <joerg@NetBSD.org>.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
22  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
28  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #if HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35 
36 #include <nbcompat.h>
37 
38 #if HAVE_ERR_H
39 #include <err.h>
40 #endif
41 #include <stdlib.h>
42 #include <string.h>
43 
44 #include "lib.h"
45 
46 #define	HASH_SIZE	521
47 
48 const char *default_acceptable_licenses =
49     "public-domain "
50     "gnu-fdl-v1.1 gnu-fdl-v1.2 gnu-fdl-v1.3 "
51     "gnu-gpl-v2 gnu-lgpl-v2 gnu-lgpl-v2.1 "
52     "gnu-gpl-v3 gnu-lgpl-v3 "
53     "original-bsd modified-bsd 2-clause-bsd "
54     "x11 mit miros "
55     "apache-1.1 apache-2.0 "
56     "artistic artistic-2.0 "
57     "cddl-1.0 "
58     "cpl-1.0 "
59     "open-font-license "
60     "mpl-1.0 mpl-1.1 "
61     "png-license "
62     "postgresql-license "
63     "zpl "
64     "python-software-foundation "
65     "ipafont "
66     "isc "
67     "info-zip "
68     "boost-license";
69 
70 #ifdef DEBUG
71 static size_t hash_collisions;
72 #endif
73 
74 static char **license_hash[HASH_SIZE];
75 static const char license_spaces[] = " \t\n";
76 static const char license_chars[] =
77     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.";
78 
79 static size_t
80 hash_license(const char *license, size_t len)
81 {
82 	size_t hash;
83 
84 	for (hash = 0; *license && len; ++license, --len)
85 		hash = *license + hash * 32;
86 	return hash % HASH_SIZE;
87 }
88 
89 static void
90 add_license_internal(const char *license, size_t len)
91 {
92 	char *new_license;
93 	size_t slot, i;
94 
95 	slot = hash_license(license, len);
96 
97 	new_license = malloc(len + 1);
98 	memcpy(new_license, license, len);
99 	new_license[len] = '\0';
100 
101 	if (license_hash[slot] == NULL) {
102 		license_hash[slot] = calloc(sizeof(char *), 2);
103 		license_hash[slot][0] = new_license;
104 	} else {
105 		for (i = 0; license_hash[slot][i]; ++i) {
106 			if (!memcmp(license_hash[slot][i], license, len) &&
107 			    license_hash[slot][i][len] == '\0') {
108 				free(new_license);
109 				return;
110 			}
111 		}
112 
113 #ifdef DEBUG
114 		++hash_collisions;
115 #endif
116 
117 		license_hash[slot] = realloc(license_hash[slot],
118 		    sizeof(char *) * (i + 2));
119 		license_hash[slot][i] = new_license;
120 		license_hash[slot][i + 1] = NULL;
121 	}
122 }
123 
124 int
125 add_licenses(const char *line)
126 {
127 	const char *next;
128 
129 	if (line == NULL)
130 		return 0;
131 
132 	for (line += strspn(line, license_spaces); line; ) {
133 		next = line + strspn(line, license_chars);
134 		if (next == line)
135 			return *line ? -1 : 0;
136 		add_license_internal(line, next - line);
137 		line = next + strspn(next, license_spaces);
138 		if (next == line)
139 			return *line ? -1 : 0;
140 	}
141 	return 0;
142 }
143 
144 static int
145 acceptable_license_internal(const char *license, size_t len)
146 {
147 	size_t slot, i;
148 
149 	slot = hash_license(license, len);
150 
151 	if (license_hash[slot] == NULL)
152 		return 0;
153 
154 	for (i = 0; license_hash[slot][i]; ++i) {
155 		if (strncmp(license_hash[slot][i], license, len) == 0 &&
156 		    license_hash[slot][i][len] == '\0')
157 			return 1;
158 	}
159 
160 	return 0;
161 }
162 
163 int
164 acceptable_license(const char *license)
165 {
166 	size_t len;
167 
168 	len = strlen(license);
169 	if (strspn(license, license_chars) != len) {
170 		warnx("Invalid character in license name at position %" PRIzu, len);
171 		return -1;
172 	}
173 
174 	return acceptable_license_internal(license, len);
175 }
176 
177 static int
178 acceptable_pkg_license_internal(const char **licensep, int toplevel, const char *start)
179 {
180 	const char *license = *licensep;
181 	int need_parenthesis, is_true = 0;
182 	int expr_type = 0; /* 0: unset, 1: or, 2: and */
183 	size_t len;
184 
185 	license += strspn(license, license_spaces);
186 
187 	if (*license == '(' && !toplevel) {
188 		need_parenthesis = 1;
189 		++license;
190 		license += strspn(license, license_spaces);
191 	} else {
192 		need_parenthesis = 0;
193 	}
194 
195 	for (;;) {
196 		if (*license == '(') {
197 			switch (acceptable_pkg_license_internal(&license, 0, start)) {
198 			case -1:
199 				return -1;
200 			case 0:
201 				if (expr_type == 2)
202 					is_true = 0;
203 				break;
204 			case 1:
205 				is_true = 1;
206 				break;
207 			}
208 			license += strspn(license, license_spaces);
209 		} else {
210 			len = strspn(license, license_chars);
211 			if (len == 0) {
212 				warnx("Invalid character in license name at position %" PRIzu, license - start + 1);
213 				return -1;
214 			}
215 
216 			if (acceptable_license_internal(license, len)) {
217 				if (expr_type != 2)
218 					is_true = 1;
219 			} else if (expr_type == 2) {
220 				is_true = 0;
221 			}
222 
223 			license += len;
224 
225 			len = strspn(license, license_spaces);
226 			if (len == 0 && *license && *license  != ')') {
227 				warnx("Missing space at position %" PRIzu, license - start + 1);
228 				return -1;
229 			}
230 			license += len;
231 		}
232 
233 		if (*license == ')') {
234 			if (!need_parenthesis) {
235 				warnx("Missing open parenthesis at position %" PRIzu, license - start + 1);
236 				return -1;
237 			}
238 			*licensep = license + 1;
239 			return is_true;
240 		}
241 		if (*license == '\0') {
242 			if (need_parenthesis) {
243 				warnx("Unbalanced parenthesis at position %" PRIzu, license - start + 1);
244 				return -1;
245 			}
246 			*licensep = license;
247 			return is_true;
248 		}
249 
250 		if (strncmp(license, "AND", 3) == 0) {
251 			if (expr_type == 1) {
252 				warnx("Invalid operator in OR expression at position %" PRIzu, license - start + 1);
253 				return -1;
254 			}
255 			expr_type = 2;
256 			license += 3;
257 		} else if (strncmp(license, "OR", 2) == 0) {
258 			if (expr_type == 2) {
259 				warnx("Invalid operator in AND expression at position %" PRIzu, license - start + 1);
260 				return -1;
261 			}
262 			expr_type = 1;
263 			license += 2;
264 		} else {
265 			warnx("Invalid operator at position %" PRIzu, license - start + 1);
266 			return -1;
267 		}
268 		len = strspn(license, license_spaces);
269 		if (len == 0 && *license != '(') {
270 			warnx("Missing space at position %" PRIzu, license - start + 1);
271 			return -1;
272 		}
273 		license += len;
274 	}
275 }
276 
277 int
278 acceptable_pkg_license(const char *license)
279 {
280 	int ret;
281 
282 	ret = acceptable_pkg_license_internal(&license, 1, license);
283 	if (ret == -1)
284 		return -1;
285 	license += strspn(license, license_spaces);
286 	if (*license) {
287 		warnx("Trailing garbage in license specification");
288 		return -1;
289 	}
290 	return ret;
291 }
292 
293 void
294 load_license_lists(void)
295 {
296 	if (add_licenses(getenv("PKGSRC_ACCEPTABLE_LICENSES")))
297 		errx(EXIT_FAILURE, "syntax error in PKGSRC_ACCEPTABLE_LICENSES");
298 	if (add_licenses(acceptable_licenses))
299 		errx(EXIT_FAILURE, "syntax error in ACCEPTABLE_LICENSES");
300 	if (add_licenses(getenv("PKGSRC_DEFAULT_ACCEPTABLE_LICENSES")))
301 		errx(EXIT_FAILURE, "syntax error in PKGSRC_DEFAULT_ACCEPTABLE_LICENSES");
302 	if (add_licenses(default_acceptable_licenses))
303 		errx(EXIT_FAILURE, "syntax error in DEFAULT_ACCEPTABLE_LICENSES");
304 }
305