1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2015 Joyent, Inc.
14 */
15
16 /*
17 * Take the error number definitions from a foreign system and generate a
18 * translation table that converts illumos native error numbers to foreign
19 * system error numbers.
20 */
21
22 #include <ctype.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <err.h>
28 #include <sys/sysmacros.h>
29 #include <libcmdutils.h>
30 #include <libnvpair.h>
31
32 nvlist_t *native_errors;
33 nvlist_t *foreign_errors;
34
35 struct override {
36 const char *ovr_from;
37 const char *ovr_to;
38 } overrides[] = {
39 { "ENOTSUP", "ENOSYS" },
40 { 0 }
41 };
42
43 static const char *
44 lookup_override(const char *from)
45 {
46 int i;
47
48 for (i = 0; overrides[i].ovr_from != NULL; i++) {
49 if (strcmp(overrides[i].ovr_from, from) == 0) {
50 return (overrides[i].ovr_to);
51 }
52 }
53
54 return (NULL);
55 }
56
57 static int
58 parse_int(const char *number, int *rval)
59 {
60 long n;
61 char *endpos;
62
63 errno = 0;
64 if ((n = strtol(number, &endpos, 10)) == 0 && errno != 0) {
65 return (-1);
66 }
67
68 if (endpos != NULL && *endpos != '\0') {
69 errno = EINVAL;
70 return (-1);
71 }
72
73 if (n > INT_MAX || n < INT_MIN) {
74 errno = EOVERFLOW;
75 return (-1);
76 }
77
78 *rval = (int)n;
79 return (0);
80 }
81
82 static int
83 errnum_add(nvlist_t *nvl, const char *name, const char *number)
84 {
85 int val;
86
87 if (nvlist_exists(nvl, name)) {
88 (void) fprintf(stderr, "ERROR: duplicate definition: %s -> "
89 "%s\n", name, number);
90 errno = EEXIST;
91 return (-1);
92 }
93
94 /*
95 * Try and parse the error number:
96 */
97 if (parse_int(number, &val) == 0) {
98 /*
99 * The name refers to a number.
100 */
101 if (nvlist_add_int32(nvl, name, val) != 0) {
102 (void) fprintf(stderr, "ERROR: nvlist_add_int32: %s\n",
103 strerror(errno));
104 return (-1);
105 }
106 } else {
107 /*
108 * The name refers to another definition.
109 */
110 if (nvlist_add_string(nvl, name, number) != 0) {
111 (void) fprintf(stderr, "ERROR: nvlist_add_string: %s\n",
112 strerror(errno));
113 return (-1);
114 }
115 }
116
117 return (0);
118 }
119
120 static int
121 errnum_max(nvlist_t *nvl)
122 {
123 int max = 0;
124 nvpair_t *nvp = NULL;
125
126 while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
127 if (nvpair_type(nvp) != DATA_TYPE_INT32) {
128 continue;
129 }
130
131 max = MAX(fnvpair_value_int32(nvp), max);
132 }
133
134 return (max);
135 }
136
137 static int
138 errname_by_num(nvlist_t *nvl, int num, const char **name)
139 {
140 nvpair_t *nvp = NULL;
141
142 while ((nvp = nvlist_next_nvpair(nvl, nvp)) != NULL) {
143 if (nvpair_type(nvp) != DATA_TYPE_INT32) {
144 continue;
145 }
146
147 if (fnvpair_value_int32(nvp) == num) {
148 *name = nvpair_name(nvp);
149 return (0);
150 }
151 }
152
153 errno = ENOENT;
154 return (-1);
155 }
156
157 static int
158 errno_by_name(nvlist_t *nvl, const char *name, int *rval, const char **rname)
159 {
160 nvpair_t *nvp = NULL;
161
162 if (nvlist_lookup_nvpair(nvl, name, &nvp) != 0) {
163 errno = ENOENT;
164 return (-1);
165 }
166
167 if (nvpair_type(nvp) == DATA_TYPE_STRING) {
168 return (errno_by_name(nvl, fnvpair_value_string(nvp), rval,
169 rname));
170 } else {
171 *rval = fnvpair_value_int32(nvp);
172 if (rname != NULL) {
173 *rname = name;
174 }
175 return (0);
176 }
177 }
178
179 static int
180 process_line(const char *line, nvlist_t *nvl)
181 {
182 custr_t *nam = NULL, *num = NULL;
183 const char *c = line;
184
185 if (custr_alloc(&nam) != 0 || custr_alloc(&num) != 0) {
186 int en = errno;
187
188 custr_free(nam);
189 custr_free(num);
190
191 errno = en;
192 return (-1);
193 }
194
195 /*
196 * Valid lines begin with "#define":
197 */
198 if (*c++ != '#' || *c++ != 'd' || *c++ != 'e' || *c++ != 'f' ||
199 *c++ != 'i' || *c++ != 'n' || *c++ != 'e') {
200 return (0);
201 }
202
203 /*
204 * Eat whitespace:
205 */
206 for (;;) {
207 if (*c == '\0') {
208 return (0);
209 }
210
211 if (*c != ' ' && *c != '\t') {
212 break;
213 }
214
215 c++;
216 }
217
218 /*
219 * Read error number token:
220 */
221 for (;;) {
222 if (*c == '\0') {
223 return (0);
224 }
225
226 if (*c == ' ' || *c == '\t') {
227 break;
228 }
229
230 if (custr_appendc(nam, *c) != 0) {
231 return (-1);
232 }
233
234 c++;
235 }
236
237 /*
238 * Eat whitespace:
239 */
240 for (;;) {
241 if (*c == '\0') {
242 return (0);
243 }
244
245 if (*c != ' ' && *c != '\t') {
246 break;
247 }
248
249 c++;
250 }
251
252 /*
253 * Read error number token:
254 */
255 for (;;) {
256 if (*c == '\0') {
257 break;
258 }
259
260 if (*c == ' ' || *c == '\t') {
261 break;
262 }
263
264 if (custr_appendc(num, *c) != 0) {
265 return (-1);
266 }
267
268 c++;
269 }
270
271 return (errnum_add(nvl, custr_cstr(nam), custr_cstr(num)));
272 }
273
274 static int
275 read_file_into_list(const char *path, nvlist_t *nvl)
276 {
277 int rval = 0, en = 0;
278 FILE *f;
279 custr_t *cu = NULL;
280
281 if (custr_alloc(&cu) != 0) {
282 return (-1);
283 }
284
285 if ((f = fopen(path, "r")) == NULL) {
286 custr_free(cu);
287 return (-1);
288 }
289
290 for (;;) {
291 int c;
292
293 errno = 0;
294 switch (c = fgetc(f)) {
295 case '\n':
296 case EOF:
297 if (errno != 0) {
298 en = errno;
299 rval = -1;
300 goto out;
301 }
302 if (process_line(custr_cstr(cu), nvl) != 0) {
303 en = errno;
304 rval = -1;
305 goto out;
306 }
307 custr_reset(cu);
308 if (c == EOF) {
309 goto out;
310 }
311 break;
312
313 case '\r':
314 case '\0':
315 /*
316 * Ignore these characters.
317 */
318 break;
319
320 default:
321 if (custr_appendc(cu, c) != 0) {
322 en = errno;
323 rval = -1;
324 goto out;
325 }
326 break;
327 }
328 }
329
330 out:
331 (void) fclose(f);
332 custr_free(cu);
333 errno = en;
334 return (rval);
335 }
336
337 int
338 main(int argc, char **argv)
339 {
340 int max;
341 int fval;
342 int c;
343
344 if (nvlist_alloc(&native_errors, NV_UNIQUE_NAME, 0) != 0 ||
345 nvlist_alloc(&foreign_errors, NV_UNIQUE_NAME, 0) != 0) {
346 err(1, "could not allocate memory");
347 }
348
349 while ((c = getopt(argc, argv, ":N:F:")) != -1) {
350 switch (c) {
351 case 'N':
352 if (read_file_into_list(optarg, native_errors) != 0) {
353 err(1, "could not read file: %s", optarg);
354 }
355 break;
356
357 case 'F':
358 if (read_file_into_list(optarg, foreign_errors) != 0) {
359 err(1, "could not read file: %s", optarg);
360 }
361 break;
362
363 case ':':
364 errx(1, "option -%c requires an operand", c);
365 break;
366
367 case '?':
368 errx(1, "option -%c unrecognised", c);
369 break;
370 }
371 }
372
373 /*
374 * Print an array entry for each error number:
375 */
376 max = errnum_max(native_errors);
377 for (fval = 0; fval <= max; fval++) {
378 const char *fname;
379 const char *tname = NULL;
380 int32_t tval;
381 const char *msg = NULL;
382 const char *comma = (fval != max) ? "," : "";
383
384 if (errname_by_num(native_errors, fval, &fname) == -1) {
385 fname = NULL;
386 }
387
388 if (fval == 0) {
389 /*
390 * The error number "0" is special: it means no worries.
391 */
392 msg = "No Error";
393 tval = 0;
394 } else if (fname == NULL) {
395 /*
396 * There is no defined name for this error number; it
397 * is unused.
398 */
399 msg = "Unused Number";
400 tval = -1;
401 } else {
402 /*
403 * Check if we want to override the name of this error
404 * in the foreign error number lookup:
405 */
406 const char *oname = lookup_override(fname);
407
408 /*
409 * Do the lookup:
410 */
411 if (errno_by_name(foreign_errors, oname != NULL ?
412 oname : fname, &tval, &tname) != 0) {
413 /*
414 * There was no foreign error number by that
415 * name.
416 */
417 tname = "No Analogue";
418 tval = -2;
419 }
420 }
421
422 if (msg == NULL) {
423 size_t flen = strlen(fname);
424 size_t tlen = strlen(tname);
425 const char *t = flen > 7 ? "\t" : "\t\t";
426 const char *tt = tlen < 7 ? "\t\t\t" : tlen < 15 ?
427 "\t\t" : "\t";
428
429 (void) fprintf(stdout, "\t%d%s\t/* %3d: %s%s--> %3d: "
430 "%s%s*/\n", tval, comma, fval, fname, t, tval,
431 tname, tt);
432 } else {
433 const char *t = "\t\t\t\t\t";
434
435 (void) fprintf(stdout, "\t%d%s\t/* %3d: %s%s*/\n", tval,
436 comma, fval, msg, t);
437 }
438 }
439
440 (void) nvlist_free(native_errors);
441 (void) nvlist_free(foreign_errors);
442
443 return (0);
444 }