1 /* $NetBSD: license.c,v 1.14 2025/05/09 13:26:38 wiz Exp $ */ 2 3 /*- 4 * Copyright (c) 2009 Joerg Sonnenberger <joerg (at) 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 "afl-3.0 " 50 "apache-1.1 apache-2.0 " 51 "arphic-public " 52 "artistic artistic-2.0 " 53 "beer-ware " 54 "boost-license " 55 "cc-by-sa-v3.0 " 56 "cc-by-sa-v4.0 " 57 "cc-by-v4.0 " 58 "cc0-1.0-universal " 59 "cddl-1.0 " 60 "cecill-2.1 " 61 "cecill-b-v1 " 62 "cecill-c-v1 " 63 "cpl-1.0 " 64 "epl-v1.0 epl-v2.0 " 65 "eupl-v1.1 " 66 "eupl-v1.2 " 67 "gfsl " 68 "gnu-fdl-v1.1 gnu-fdl-v1.2 gnu-fdl-v1.3 " 69 "gnu-gpl-v1 " 70 "gnu-gpl-v2 gnu-lgpl-v2 gnu-lgpl-v2.1 " 71 "gnu-gpl-v3 gnu-lgpl-v3 " 72 "happy " 73 "hpnd " 74 "icu " 75 "ijg " 76 "info-zip " 77 "ipafont " 78 "ipl-1.0 " 79 "isc " 80 "lppl-1.0 lppl-1.2 lppl-1.3c " 81 "lucent " 82 "miros " 83 "mit " 84 "mpl-1.0 mpl-1.1 mpl-2.0 " 85 "mplusfont " 86 "ms-pl " 87 "odbl-v1 " 88 "ofl-v1.0 ofl-v1.1 " 89 "openssl " 90 "original-bsd modified-bsd 2-clause-bsd 0-clause-bsd " 91 "osl " 92 "paratype " 93 "php " 94 "png-license " 95 "postgresql-license " 96 "public-domain " 97 "python-software-foundation " 98 "qhull " 99 "qpl-v1.0 " 100 "sgi-free-software-b-v2.0 " 101 "sissl-1.1 " 102 "sleepycat-public " 103 "unicode " 104 "unicode-v3 " 105 "unlicense " 106 "upl-1.0 " 107 "vera-ttf-license " 108 "w3c " 109 "x11 " 110 "zlib " 111 "zpl-2.0 zpl-2.1 " 112 "zsh"; 113 114 #ifdef DEBUG 115 static size_t hash_collisions; 116 #endif 117 118 static char **license_hash[HASH_SIZE]; 119 static const char license_spaces[] = " \t\n"; 120 static const char license_chars[] = 121 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-."; 122 123 static size_t 124 hash_license(const char *license, size_t len) 125 { 126 size_t hash; 127 128 for (hash = 0; *license && len; ++license, --len) 129 hash = *license + hash * 32; 130 return hash % HASH_SIZE; 131 } 132 133 static void 134 add_license_internal(const char *license, size_t len) 135 { 136 char *new_license; 137 size_t slot, i; 138 139 slot = hash_license(license, len); 140 141 new_license = malloc(len + 1); 142 memcpy(new_license, license, len); 143 new_license[len] = '\0'; 144 145 if (license_hash[slot] == NULL) { 146 license_hash[slot] = calloc(sizeof(char *), 2); 147 license_hash[slot][0] = new_license; 148 } else { 149 for (i = 0; license_hash[slot][i]; ++i) { 150 if (!memcmp(license_hash[slot][i], license, len) && 151 license_hash[slot][i][len] == '\0') { 152 free(new_license); 153 return; 154 } 155 } 156 157 #ifdef DEBUG 158 ++hash_collisions; 159 #endif 160 161 license_hash[slot] = realloc(license_hash[slot], 162 sizeof(char *) * (i + 2)); 163 license_hash[slot][i] = new_license; 164 license_hash[slot][i + 1] = NULL; 165 } 166 } 167 168 int 169 add_licenses(const char *line) 170 { 171 const char *next; 172 173 if (line == NULL) 174 return 0; 175 176 for (line += strspn(line, license_spaces); line; ) { 177 next = line + strspn(line, license_chars); 178 if (next == line) 179 return *line ? -1 : 0; 180 add_license_internal(line, next - line); 181 line = next + strspn(next, license_spaces); 182 if (next == line) 183 return *line ? -1 : 0; 184 } 185 return 0; 186 } 187 188 static int 189 acceptable_license_internal(const char *license, size_t len) 190 { 191 size_t slot, i; 192 193 slot = hash_license(license, len); 194 195 if (license_hash[slot] == NULL) 196 return 0; 197 198 for (i = 0; license_hash[slot][i]; ++i) { 199 if (strncmp(license_hash[slot][i], license, len) == 0 && 200 license_hash[slot][i][len] == '\0') 201 return 1; 202 } 203 204 return 0; 205 } 206 207 int 208 acceptable_license(const char *license) 209 { 210 size_t len; 211 212 len = strlen(license); 213 if (strspn(license, license_chars) != len) { 214 warnx("Invalid character in license name at position %" PRIzu, len); 215 return -1; 216 } 217 218 return acceptable_license_internal(license, len); 219 } 220 221 static int 222 acceptable_pkg_license_internal(const char **licensep, int toplevel, const char *start) 223 { 224 const char *license = *licensep; 225 int need_parenthesis, is_true = 0; 226 int expr_type = 0; /* 0: unset, 1: or, 2: and */ 227 size_t len; 228 229 license += strspn(license, license_spaces); 230 231 if (*license == '(' && !toplevel) { 232 need_parenthesis = 1; 233 ++license; 234 license += strspn(license, license_spaces); 235 } else { 236 need_parenthesis = 0; 237 } 238 239 for (;;) { 240 if (*license == '(') { 241 switch (acceptable_pkg_license_internal(&license, 0, start)) { 242 case -1: 243 return -1; 244 case 0: 245 if (expr_type == 2) 246 is_true = 0; 247 break; 248 case 1: 249 is_true = 1; 250 break; 251 } 252 license += strspn(license, license_spaces); 253 } else { 254 len = strspn(license, license_chars); 255 if (len == 0) { 256 warnx("Invalid character in license name at position %" PRIzu, license - start + 1); 257 return -1; 258 } 259 260 if (acceptable_license_internal(license, len)) { 261 if (expr_type != 2) 262 is_true = 1; 263 } else if (expr_type == 2) { 264 is_true = 0; 265 } 266 267 license += len; 268 269 len = strspn(license, license_spaces); 270 if (len == 0 && *license && *license != ')') { 271 warnx("Missing space at position %" PRIzu, license - start + 1); 272 return -1; 273 } 274 license += len; 275 } 276 277 if (*license == ')') { 278 if (!need_parenthesis) { 279 warnx("Missing open parenthesis at position %" PRIzu, license - start + 1); 280 return -1; 281 } 282 *licensep = license + 1; 283 return is_true; 284 } 285 if (*license == '\0') { 286 if (need_parenthesis) { 287 warnx("Unbalanced parenthesis at position %" PRIzu, license - start + 1); 288 return -1; 289 } 290 *licensep = license; 291 return is_true; 292 } 293 294 if (strncmp(license, "AND", 3) == 0) { 295 if (expr_type == 1) { 296 warnx("Invalid operator in OR expression at position %" PRIzu, license - start + 1); 297 return -1; 298 } 299 expr_type = 2; 300 license += 3; 301 } else if (strncmp(license, "OR", 2) == 0) { 302 if (expr_type == 2) { 303 warnx("Invalid operator in AND expression at position %" PRIzu, license - start + 1); 304 return -1; 305 } 306 expr_type = 1; 307 license += 2; 308 } else { 309 warnx("Invalid operator at position %" PRIzu, license - start + 1); 310 return -1; 311 } 312 len = strspn(license, license_spaces); 313 if (len == 0 && *license != '(') { 314 warnx("Missing space at position %" PRIzu, license - start + 1); 315 return -1; 316 } 317 license += len; 318 } 319 } 320 321 int 322 acceptable_pkg_license(const char *license) 323 { 324 int ret; 325 326 ret = acceptable_pkg_license_internal(&license, 1, license); 327 if (ret == -1) 328 return -1; 329 license += strspn(license, license_spaces); 330 if (*license) { 331 warnx("Trailing garbage in license specification"); 332 return -1; 333 } 334 return ret; 335 } 336 337 void 338 load_license_lists(void) 339 { 340 if (add_licenses(getenv("PKGSRC_ACCEPTABLE_LICENSES"))) 341 errx(EXIT_FAILURE, "syntax error in PKGSRC_ACCEPTABLE_LICENSES"); 342 if (add_licenses(acceptable_licenses)) 343 errx(EXIT_FAILURE, "syntax error in ACCEPTABLE_LICENSES"); 344 if (add_licenses(getenv("PKGSRC_DEFAULT_ACCEPTABLE_LICENSES"))) 345 errx(EXIT_FAILURE, "syntax error in PKGSRC_DEFAULT_ACCEPTABLE_LICENSES"); 346 if (add_licenses(default_acceptable_licenses)) 347 errx(EXIT_FAILURE, "syntax error in DEFAULT_ACCEPTABLE_LICENSES"); 348 } 349