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 }