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