]> git.ozlabs.org Git - ccan/blob - tools/ccanlint/tests/license_exists.c
af1bc52a07c85e875829f54824c56bdad9540256
[ccan] / tools / ccanlint / tests / license_exists.c
1 #include <tools/ccanlint/ccanlint.h>
2 #include <ccan/tal/tal.h>
3 #include <ccan/tal/str/str.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <fcntl.h>
7 #include <unistd.h>
8 #include <limits.h>
9 #include <errno.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <err.h>
13 #include <ccan/str/str.h>
14 #include <ccan/take/take.h>
15
16 /* We might need more ../ for nested modules. */
17 static const char *link_prefix(struct manifest *m)
18 {
19         char *prefix = tal_strdup(m, "../../");
20         unsigned int i;
21
22         for (i = 0; i < strcount(m->modname, "/"); i++)
23                 prefix = tal_strcat(m, take(prefix), "../");
24
25         return tal_strcat(m, take(prefix), "licenses/");
26 }
27
28 static const char *expected_link(const tal_t *ctx,
29                                  const char *prefix, enum license license)
30 {
31         const char *shortname;
32
33         switch (license) {
34         case LICENSE_LGPLv2_PLUS:
35         case LICENSE_LGPLv2:
36                 shortname = "LGPL-2.1";
37                 break;
38         case LICENSE_LGPLv3:
39         case LICENSE_LGPL:
40                 shortname = "LGPL-3";
41                 break;
42
43         case LICENSE_GPLv2_PLUS:
44         case LICENSE_GPLv2:
45                 shortname = "GPL-2";
46                 break;
47
48         case LICENSE_GPLv3:
49         case LICENSE_GPL:
50                 shortname = "GPL-3";
51                 break;
52
53         case LICENSE_BSD:
54                 shortname = "BSD-3CLAUSE";
55                 break;
56
57         case LICENSE_MIT:
58                 shortname = "BSD-MIT";
59                 break;
60
61         case LICENSE_CC0:
62                 shortname = "CC0";
63                 break;
64
65         default:
66                 return NULL;
67         }
68
69         return tal_strcat(ctx, prefix, shortname);
70 }
71
72 static void handle_license_link(struct manifest *m, struct score *score)
73 {
74         struct doc_section *d = find_license_tag(m);
75         const char *prefix = link_prefix(m);
76         const char *link = tal_fmt(m, "%s/LICENSE", m->dir);
77         const char *ldest = expected_link(score, prefix, m->license);
78         char *q;
79
80         printf(
81         "Most modules want a copy of their license, so usually we create a\n"
82         "LICENSE symlink into %s to avoid too many copies.\n", prefix);
83
84         /* FIXME: make ask printf-like */
85         q = tal_fmt(m, "Set up link to %s (license is %s)?",
86                     ldest, d->lines[0]);
87         if (ask(q)) {
88                 if (symlink(ldest, link) != 0)
89                         err(1, "Creating symlink %s -> %s", link, ldest);
90         }
91 }
92
93 extern struct ccanlint license_exists;
94
95 static void check_has_license(struct manifest *m,
96                               unsigned int *timeleft, struct score *score)
97 {
98         char buf[PATH_MAX];
99         ssize_t len;
100         char *license = tal_fmt(m, "%s/LICENSE", m->dir);
101         const char *expected;
102         struct doc_section *d;
103         const char *prefix = link_prefix(m);
104
105         d = find_license_tag(m);
106         if (!d) {
107                 score->error = tal_strdup(score, "No License: tag in _info");
108                 return;
109         }
110
111         m->license = which_license(d);
112         if (m->license == LICENSE_UNKNOWN) {
113                 score_file_error(score, m->info_file, d->srcline,
114                                  "WARNING: unknown License: in _info: %s",
115                                  d->lines[0]);
116                 /* FIXME: For historical reasons, don't fail here. */
117                 score->pass = true;
118                 return;
119         }
120
121         /* If they have a license tag at all, we pass. */
122         score->pass = true;
123
124         expected = expected_link(m, prefix, m->license);
125
126         len = readlink(license, buf, sizeof(buf));
127         if (len < 0) {
128                 /* Could be a real file... OK if not a standard license. */
129                 if (errno == EINVAL) {
130                         if (!expected) {
131                                 score->score = score->total;
132                                 return;
133                         }
134                         score->error
135                                 = tal_fmt(score,
136                                           "License in _info is '%s',"
137                                           " expect LICENSE symlink '%s'",
138                                           d->lines[0], expected);
139                         return;
140                 }
141                 if (errno == ENOENT) {
142                         /* Public domain doesn't really need a file. */
143                         if (m->license == LICENSE_PUBLIC_DOMAIN) {
144                                 score->score = score->total;
145                                 return;
146                         }
147                         score->error = tal_strdup(score,
148                                                      "LICENSE does not exist");
149                         if (expected)
150                                 license_exists.handle = handle_license_link;
151                         return;
152                 }
153                 err(1, "readlink on %s", license);
154         }
155         if (len >= sizeof(buf))
156                 errx(1, "Reading symlink %s gave huge result", license);
157
158         buf[len] = '\0';
159
160         if (!strstarts(buf, prefix)) {
161                 score->error = tal_fmt(score,
162                                        "Expected symlink into %s not %s",
163                                        prefix, buf);
164                 return;
165         }
166
167         if (!expected) {
168                 score->error = tal_fmt(score,
169                                        "License in _info is unknown '%s',"
170                                        " but LICENSE symlink is '%s'",
171                                        d->lines[0], buf);
172                 return;
173         }
174
175         if (!streq(buf, expected)) {
176                 score->error = tal_fmt(score,
177                                        "Expected symlink to %s not %s",
178                                        expected, buf);
179                 return;
180         }
181         score->pass = true;
182         score->score = score->total;
183 }
184
185 struct ccanlint license_exists = {
186         .key = "license_exists",
187         .name = "Module has License: entry in _info, and LICENSE symlink/file",
188         .check = check_has_license,
189         .needs = "info_exists"
190 };
191 REGISTER_TEST(license_exists);