xref: /netbsd-src/external/bsd/pkg_install/dist/lib/license.c (revision a7e090f70e491979434963c9a27df4020fe0a18b)
1 /*	$NetBSD: license.c,v 1.1.1.7 2010/01/30 21:33:49 joerg 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     "zpl "
62     "python-software-foundation "
63     "info-zip";
64 
65 #ifdef DEBUG
66 static size_t hash_collisions;
67 #endif
68 
69 static char **license_hash[HASH_SIZE];
70 static const char license_spaces[] = " \t\n";
71 static const char license_chars[] =
72     "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.";
73 
74 static size_t
75 hash_license(const char *license, size_t len)
76 {
77 	size_t hash;
78 
79 	for (hash = 0; *license && len; ++license, --len)
80 		hash = *license + hash * 32;
81 	return hash % HASH_SIZE;
82 }
83 
84 static void
85 add_license_internal(const char *license, size_t len)
86 {
87 	char *new_license;
88 	size_t slot, i;
89 
90 	slot = hash_license(license, len);
91 
92 	new_license = malloc(len + 1);
93 	memcpy(new_license, license, len);
94 	new_license[len] = '\0';
95 
96 	if (license_hash[slot] == NULL) {
97 		license_hash[slot] = calloc(sizeof(char *), 2);
98 		license_hash[slot][0] = new_license;
99 	} else {
100 		for (i = 0; license_hash[slot][i]; ++i) {
101 			if (!memcmp(license_hash[slot][i], license, len) &&
102 			    license_hash[slot][i][len] == '\0') {
103 				free(new_license);
104 				return;
105 			}
106 		}
107 
108 #ifdef DEBUG
109 		++hash_collisions;
110 #endif
111 
112 		license_hash[slot] = realloc(license_hash[slot],
113 		    sizeof(char *) * (i + 2));
114 		license_hash[slot][i] = new_license;
115 		license_hash[slot][i + 1] = NULL;
116 	}
117 }
118 
119 int
120 add_licenses(const char *line)
121 {
122 	const char *next;
123 
124 	if (line == NULL)
125 		return 0;
126 
127 	for (line += strspn(line, license_spaces); line; ) {
128 		next = line + strspn(line, license_chars);
129 		if (next == line)
130 			return *line ? -1 : 0;
131 		add_license_internal(line, next - line);
132 		line = next + strspn(next, license_spaces);
133 		if (next == line)
134 			return *line ? -1 : 0;
135 	}
136 	return 0;
137 }
138 
139 static int
140 acceptable_license_internal(const char *license, size_t len)
141 {
142 	size_t slot, i;
143 
144 	slot = hash_license(license, len);
145 
146 	if (license_hash[slot] == NULL)
147 		return 0;
148 
149 	for (i = 0; license_hash[slot][i]; ++i) {
150 		if (strncmp(license_hash[slot][i], license, len) == 0 &&
151 		    license_hash[slot][i][len] == '\0')
152 			return 1;
153 	}
154 
155 	return 0;
156 }
157 
158 int
159 acceptable_license(const char *license)
160 {
161 	size_t len;
162 
163 	len = strlen(license);
164 	if (strspn(license, license_chars) != len) {
165 		warnx("Invalid character in license name at position %zu", len);
166 		return -1;
167 	}
168 
169 	return acceptable_license_internal(license, len);
170 }
171 
172 static int
173 acceptable_pkg_license_internal(const char **licensep, int toplevel, const char *start)
174 {
175 	const char *license = *licensep;
176 	int need_parenthesis, is_true = 0;
177 	int expr_type = 0; /* 0: unset, 1: or, 2: and */
178 	size_t len;
179 
180 	license += strspn(license, license_spaces);
181 
182 	if (*license == '(' && !toplevel) {
183 		need_parenthesis = 1;
184 		++license;
185 		license += strspn(license, license_spaces);
186 	} else {
187 		need_parenthesis = 0;
188 	}
189 
190 	for (;;) {
191 		if (*license == '(') {
192 			switch (acceptable_pkg_license_internal(&license, 0, start)) {
193 			case -1:
194 				return -1;
195 			case 0:
196 				if (expr_type == 2)
197 					is_true = 0;
198 				break;
199 			case 1:
200 				is_true = 1;
201 				break;
202 			}
203 			license += strspn(license, license_spaces);
204 		} else {
205 			len = strspn(license, license_chars);
206 			if (len == 0) {
207 				warnx("Invalid character in license name at position %zu", license - start + 1);
208 				return -1;
209 			}
210 
211 			if (acceptable_license_internal(license, len)) {
212 				if (expr_type != 2)
213 					is_true = 1;
214 			} else if (expr_type == 2) {
215 				is_true = 0;
216 			}
217 
218 			license += len;
219 
220 			len = strspn(license, license_spaces);
221 			if (len == 0 && *license && *license  != ')') {
222 				warnx("Missing space at position %zu", license - start + 1);
223 				return -1;
224 			}
225 			license += len;
226 		}
227 
228 		if (*license == ')') {
229 			if (!need_parenthesis) {
230 				warnx("Missing open parenthesis at position %zu", license - start + 1);
231 				return -1;
232 			}
233 			*licensep = license + 1;
234 			return is_true;
235 		}
236 		if (*license == '\0') {
237 			if (need_parenthesis) {
238 				warnx("Unbalanced parenthesis at position %zu", license - start + 1);
239 				return -1;
240 			}
241 			*licensep = license;
242 			return is_true;
243 		}
244 
245 		if (strncmp(license, "AND", 3) == 0) {
246 			if (expr_type == 1) {
247 				warnx("Invalid operator in OR expression at position %zu", license - start + 1);
248 				return -1;
249 			}
250 			expr_type = 2;
251 			license += 3;
252 		} else if (strncmp(license, "OR", 2) == 0) {
253 			if (expr_type == 2) {
254 				warnx("Invalid operator in AND expression at position %zu", license - start + 1);
255 				return -1;
256 			}
257 			expr_type = 1;
258 			license += 2;
259 		} else {
260 			warnx("Invalid operator at position %zu", license - start + 1);
261 			return -1;
262 		}
263 		len = strspn(license, license_spaces);
264 		if (len == 0 && *license != '(') {
265 			warnx("Missing space at position %zu", license - start + 1);
266 			return -1;
267 		}
268 		license += len;
269 	}
270 }
271 
272 int
273 acceptable_pkg_license(const char *license)
274 {
275 	int ret;
276 
277 	ret = acceptable_pkg_license_internal(&license, 1, license);
278 	if (ret == -1)
279 		return -1;
280 	license += strspn(license, license_spaces);
281 	if (*license) {
282 		warnx("Trailing garbage in license specification");
283 		return -1;
284 	}
285 	return ret;
286 }
287 
288 void
289 load_license_lists(void)
290 {
291 	if (add_licenses(getenv("PKGSRC_ACCEPTABLE_LICENSES")))
292 		errx(EXIT_FAILURE, "syntax error in PKGSRC_ACCEPTABLE_LICENSES");
293 	if (add_licenses(acceptable_licenses))
294 		errx(EXIT_FAILURE, "syntax error in ACCEPTABLE_LICENSES");
295 	if (add_licenses(getenv("PKGSRC_DEFAULT_ACCEPTABLE_LICENSES")))
296 		errx(EXIT_FAILURE, "syntax error in PKGSRC_DEFAULT_ACCEPTABLE_LICENSES");
297 	if (add_licenses(default_acceptable_licenses))
298 		errx(EXIT_FAILURE, "syntax error in DEFAULT_ACCEPTABLE_LICENSES");
299 }
300