1 /*
2 * Copyright (c) 2013-2026 Devin Teske <dteske@FreeBSD.org>
3 * Copyright (c) 2021-2026 Faraz Vahedi <kfv@FreeBSD.org>
4 *
5 * SPDX-License-Identifier: BSD-2-Clause
6 */
7
8 /*
9 * Directive descriptions harvested from defaults-file comments (-d).
10 */
11
12 #include "sysconf_priv.h"
13
14 /*
15 * Directive descriptions harvested from the defaults file by
16 * load_descriptions() below.
17 */
18 struct descent {
19 char *name; /* directive name */
20 char *desc; /* description (may be empty) */
21 };
22
23 static struct descent *descs; /* set by load_descriptions() */
24 static size_t ndescs;
25 static size_t descsize;
26
27 /*
28 * Harvest every directive description from the defaults file, where a
29 * description is the inline comment trailing the directive's default
30 * assignment, continued across any subsequent lines of whitespace
31 * followed by a comment character. A commented-out default
32 * (`#comconsole_speed="115200" # Set the ...') yields its directive and
33 * description all the same -- deliberately unlike sysrc(8), which goes
34 * blind when the default itself is disabled. Comment prose is never
35 * mistaken for a directive: a name must directly abut the optional
36 * leading `#' and run unbroken to its `='.
37 */
38 void
load_descriptions(void)39 load_descriptions(void)
40 {
41 int current = -1; /* entry whose description is growing */
42 FILE *fp;
43 ssize_t linelen;
44 size_t len;
45 size_t linesize = 0;
46 size_t n;
47 char *defaults;
48 char *line = NULL;
49 char *p;
50 char *v;
51 struct descent *tmp;
52
53 if ((defaults = defaults_path()) == NULL)
54 errx(EXIT_FAILURE, "target has no defaults file");
55 if ((fp = fopen(defaults, "r")) == NULL)
56 err(EXIT_FAILURE, "%s", defaults);
57
58 while ((linelen = getline(&line, &linesize, fp)) != -1) {
59 int quoted = 0;
60
61 if (linelen > 0 && line[linelen - 1] == '\n')
62 line[--linelen] = '\0';
63
64 /* Whitespace then `#' continues the open description */
65 p = line;
66 if (*p == ' ' || *p == '\t') {
67 p += strspn(p, " \t");
68 if (*p == '#' && current >= 0) {
69 char *grown;
70
71 p++;
72 p += strspn(p, " \t");
73 if (*p == '\0')
74 continue;
75 len = strlen(descs[current].desc) +
76 strlen(p) + 2;
77 if ((grown = malloc(len)) == NULL)
78 err(EXIT_FAILURE, NULL);
79 snprintf(grown, len, "%s%s%s",
80 descs[current].desc,
81 *descs[current].desc != '\0' ? " " : "",
82 p);
83 free(descs[current].desc);
84 descs[current].desc = grown;
85 continue;
86 }
87 current = -1;
88 continue;
89 }
90 current = -1;
91
92 /* A commented-out default still describes its directive */
93 if (*p == '#')
94 p++;
95
96 /* A directive name runs unbroken to its `=' */
97 len = strcspn(p, " \t\"#=");
98 if (len == 0 || p[len] != '=')
99 continue;
100
101 /* First description wins on a repeated directive */
102 for (n = 0; n < ndescs; n++)
103 if (strncmp(descs[n].name, p, len) == 0 &&
104 descs[n].name[len] == '\0')
105 break;
106 if (n < ndescs)
107 continue;
108
109 /* Skip the value (quotes may hide a `#') */
110 for (v = p + len + 1; *v != '\0'; v++) {
111 if (*v == '"')
112 quoted = !quoted;
113 else if (*v == '#' && !quoted)
114 break;
115 }
116 if (*v == '#') {
117 v++;
118 v += strspn(v, " \t");
119 }
120
121 if (ndescs >= descsize) {
122 descsize = (descsize == 0) ? 64 : descsize << 1;
123 tmp = realloc(descs, descsize * sizeof(*descs));
124 if (tmp == NULL)
125 err(EXIT_FAILURE, NULL);
126 descs = tmp;
127 }
128 descs[ndescs].name = strndup(p, len);
129 descs[ndescs].desc = strdup(v);
130 if (descs[ndescs].name == NULL ||
131 descs[ndescs].desc == NULL)
132 err(EXIT_FAILURE, NULL);
133 current = (int)ndescs;
134 ndescs++;
135 }
136 free(line);
137 fclose(fp);
138 free(defaults);
139 }
140
141 /*
142 * Return the harvested description of `name' or NULL if the defaults file
143 * does not mention it.
144 */
145 static const char *
desc_find(const char * name)146 desc_find(const char *name)
147 {
148 size_t n;
149
150 for (n = 0; n < ndescs; n++)
151 if (strcmp(descs[n].name, name) == 0)
152 return (descs[n].desc);
153 return (NULL);
154 }
155
156 /*
157 * Print the description of `directive' according to the display flags: for
158 * the sysctl target, from the running kernel; otherwise from the table
159 * harvested off the defaults file (a directive the defaults never mention
160 * prints an empty description).
161 */
162 void
print_desc(const char * directive)163 print_desc(const char *directive)
164 {
165 const char *desc = NULL;
166 #if defined(__FreeBSD__)
167 char buf[BUFSIZ];
168
169 if (format == BSDCONF_FORMAT_SYSCTL) {
170 (void)sysctl_descr(directive, buf, sizeof(buf));
171 desc = buf;
172 }
173 #endif
174 if (desc == NULL && (desc = desc_find(directive)) == NULL)
175 desc = "";
176 if (name_only)
177 puts(directive);
178 else if (value_only)
179 puts(desc);
180 else
181 printf("%s: %s\n", directive, desc);
182 }
183
184 /*
185 * Process `-d' with explicit names for a defaults-backed target. Returns
186 * EXIT_SUCCESS or EXIT_FAILURE.
187 */
188 int
describe_defaults(void)189 describe_defaults(void)
190 {
191 int rv = EXIT_SUCCESS;
192 unsigned int n;
193
194 load_descriptions();
195 for (n = 0; n < nreqs; n++) {
196 if (desc_find(reqs[n].name) == NULL) {
197 if (ignore_unknown)
198 continue;
199 if (!quiet)
200 warnx("unknown directive '%s'",
201 reqs[n].name);
202 rv = EXIT_FAILURE;
203 continue;
204 }
205 print_desc(reqs[n].name);
206 }
207
208 return (rv);
209 }
210