/*- * See the file LICENSE for redistribution information. * * Copyright (c) 1996-2009 Oracle. All rights reserved. */ #include #include #include #include #include #include #include typedef struct { char *name; /* API name */ u_int used_mask; /* Bits used. */ } API; API **api_list, **api_end; typedef struct { char *name; /* Flag name */ int api_cnt; /* APIs that use this flag. */ API **api, **api_end; u_int value; /* Bit value */ } FLAG; FLAG **flag_list, **flag_end; int verbose; char *progname; int add_entry(char *, char *); void define_print(char *, u_int); void dump_api(void); void dump_flags(void); int flag_cmp_alpha(const void *, const void *); int flag_cmp_api_cnt(const void *, const void *); int generate_flags(void); int parse(void); void print_api_mask(void); void print_api_remainder(void); void print_flag_value(void); int syserr(void); int usage(void); int main(int argc, char *argv[]) { enum { API_MASK, API_REMAINDER, FLAG_VALUE } output; int ch; if ((progname = strrchr(argv[0], '/')) == NULL) progname = argv[0]; else ++progname; output = FLAG_VALUE; while ((ch = getopt(argc, argv, "mrv")) != EOF) switch (ch) { case 'm': output = API_MASK; break; case 'r': output = API_REMAINDER; break; case 'v': verbose = 1; break; case '?': default: return (usage()); } argc -= optind; argv += optind; if (parse() || generate_flags()) return (EXIT_FAILURE); switch (output) { case API_MASK: print_api_mask(); break; case API_REMAINDER: print_api_remainder(); break; case FLAG_VALUE: print_flag_value(); break; } if (verbose) { dump_api(); dump_flags(); } return (EXIT_SUCCESS); } int parse() { int lc; char *p, *api, buf[256]; api = NULL; /* * Read the method name/flag pairs. */ for (lc = 1; fgets(buf, sizeof(buf), stdin) != NULL; ++lc) { if ((p = strchr(buf, '\n')) != NULL) *p = '\0'; else { fprintf( stderr, "%s: %d: line too long\n", progname, lc); return (1); } /* Ignore any empty line or hash mark. */ if (buf[0] == '\0' || buf[0] == '#') continue; /* * A line without leading whitespace is an API name, a line * with leading whitespace is a flag name. */ if (isspace(buf[0])) { if ((p = strtok(buf, " \t")) == NULL || *p == '#') continue; /* A flag without an API makes no sense. */ if (api == NULL) goto format; /* Enter the pair into the array. */ if (add_entry(api, p)) return (1); } else { if ((p = strtok(buf, " \t")) == NULL) continue; if (api != NULL) free(api); if ((api = strdup(p)) == NULL) return (syserr()); } if ((p = strtok(NULL, " \t")) != NULL && *p != '#') goto format; } return (0); format: fprintf(stderr, "%s: format error: line %d\n", progname, lc); return (1); } int add_entry(char *api_name, char *flag_name) { FLAG **fpp, *fp; API **app, *ap, **p; u_int cnt; /* Search for this api's API structure. */ for (app = api_list; app != NULL && *app != NULL && app < api_end; ++app) if (strcmp(api_name, (*app)->name) == 0) break; /* Allocate new space in the API array if necessary. */ if (app == NULL || app == api_end) { cnt = app == NULL ? 100 : (u_int)(api_end - api_list) + 100; if ((api_list = realloc(api_list, sizeof(API *) * cnt)) == NULL) return (syserr()); api_end = api_list + cnt; app = api_list + (cnt - 100); memset(app, 0, (u_int)(api_end - app) * sizeof(API *)); } /* Allocate a new API structure and fill in the name if necessary. */ if (*app == NULL && ((*app = calloc(sizeof(API), 1)) == NULL || ((*app)->name = strdup(api_name)) == NULL)) return (syserr()); ap = *app; /* * There's a special keyword, "__MASK=" that sets the initial * flags value for an API, and so prevents those flag bits from being * chosen for that API's flags. */ if (strncmp(flag_name, "__MASK=", sizeof("__MASK=") - 1) == 0) { ap->used_mask |= strtoul(flag_name + sizeof("__MASK=") - 1, NULL, 0); return (0); } /* Search for this flag's FLAG structure. */ for (fpp = flag_list; fpp != NULL && *fpp != NULL && fpp < flag_end; ++fpp) if (strcmp(flag_name, (*fpp)->name) == 0) break; /* Realloc space in the FLAG array if necessary. */ if (fpp == NULL || fpp == flag_end) { cnt = fpp == NULL ? 100 : (u_int)(flag_end - flag_list) + 100; if ((flag_list = realloc(flag_list, sizeof(FLAG *) * cnt)) == NULL) return (syserr()); flag_end = flag_list + cnt; fpp = flag_list + (cnt - 100); memset(fpp, 0, (u_int)(flag_end - fpp) * sizeof(FLAG *)); } /* Allocate a new FLAG structure and fill in the name if necessary. */ if (*fpp == NULL && ((*fpp = calloc(sizeof(FLAG), 1)) == NULL || ((*fpp)->name = strdup(flag_name)) == NULL)) return (syserr()); fp = *fpp; ++fp->api_cnt; /* Check to see if this API is already listed for this flag. */ for (p = fp->api; p != NULL && *p != NULL && p < fp->api_end; ++p) if (strcmp(api_name, (*p)->name) == 0) { fprintf(stderr, "duplicate entry: %s / %s\n", api_name, flag_name); return (1); } /* Realloc space in the FLAG's API array if necessary. */ if (p == NULL || p == fp->api_end) { cnt = p == NULL ? 20 : (u_int)(fp->api_end - fp->api) + 20; if ((fp->api = realloc(fp->api, sizeof(API *) * cnt)) == NULL) return (syserr()); fp->api_end = fp->api + cnt; p = fp->api + (cnt - 20); memset(p, 0, (u_int)(fp->api_end - fp->api) * sizeof(API *)); } *p = ap; return (0); } void dump_api() { API **app; printf("=============================\nAPI:\n"); for (app = api_list; *app != NULL; ++app) printf("%s (%#x)\n", (*app)->name, (*app)->used_mask); } void dump_flags() { FLAG **fpp; API **api; char *sep; printf("=============================\nFLAGS:\n"); for (fpp = flag_list; *fpp != NULL; ++fpp) { printf("%s (%#x, %d): ", (*fpp)->name, (*fpp)->value, (*fpp)->api_cnt); sep = ""; for (api = (*fpp)->api; *api != NULL; ++api) { printf("%s%s", sep, (*api)->name); sep = ", "; } printf("\n"); } } int flag_cmp_api_cnt(const void *a, const void *b) { FLAG *af, *bf; af = *(FLAG **)a; bf = *(FLAG **)b; if (af == NULL) { if (bf == NULL) return (0); return (1); } if (bf == NULL) { if (af == NULL) return (0); return (-1); } if (af->api_cnt > bf->api_cnt) return (-1); if (af->api_cnt < bf->api_cnt) return (1); return (strcmp(af->name, bf->name)); } int generate_flags() { FLAG **fpp; API **api; u_int mask; /* Sort the FLAGS array by reference count, in reverse order. */ qsort(flag_list, (u_int)(flag_end - flag_list), sizeof(FLAG *), flag_cmp_api_cnt); /* * Here's the plan: walk the list of flags, allocating bits. For * each flag, we walk the list of APIs that use it and find a bit * none of them are using. That bit becomes the flag's value. */ for (fpp = flag_list; *fpp != NULL; ++fpp) { mask = 0xffffffff; /* Set to all 1's */ for (api = (*fpp)->api; *api != NULL; ++api) mask &= ~(*api)->used_mask; /* Clear API's bits */ if (mask == 0) { fprintf(stderr, "%s: ran out of bits at flag %s\n", progname, (*fpp)->name); return (1); } (*fpp)->value = mask = 1 << (ffs(mask) - 1); for (api = (*fpp)->api; *api != NULL; ++api) (*api)->used_mask |= mask; /* Set bit for API */ } return (0); } int flag_cmp_alpha(const void *a, const void *b) { FLAG *af, *bf; af = *(FLAG **)a; bf = *(FLAG **)b; if (af == NULL) { if (bf == NULL) return (0); return (1); } if (bf == NULL) { if (af == NULL) return (0); return (-1); } return (strcmp(af->name, bf->name)); } void print_api_mask() { API **app; char *p, buf[256]; /* Output a mask for the API. */ for (app = api_list; *app != NULL; ++app) { (void)snprintf( buf, sizeof(buf), "_%s_API_MASK", (*app)->name); for (p = buf; *p != '\0'; ++p) if (islower(*p)) *p = toupper(*p); else if (!isalpha(*p)) *p = '_'; define_print(buf, (*app)->used_mask); } } void print_api_remainder() { API **app; int unused, i; /* Output the bits remaining for the API. */ for (app = api_list; *app != NULL; ++app) { for (i = unused = 0; i < 32; ++i) if (!((*app)->used_mask & (1 << i))) ++unused; printf("%s: %d bits unused\n", (*app)->name, unused); } } void print_flag_value() { FLAG **fpp; /* Sort the FLAGS array in alphabetical order. */ qsort(flag_list, (u_int)(flag_end - flag_list), sizeof(FLAG *), flag_cmp_alpha); /* Output each flag's value. */ for (fpp = flag_list; *fpp != NULL; ++fpp) define_print((*fpp)->name, (*fpp)->value); } void define_print(char *name, u_int value) { char *sep; switch (strlen(name) / 8) { case 0: sep = "\t\t\t\t\t"; break; case 1: sep = "\t\t\t\t"; break; case 2: sep = "\t\t\t"; break; case 3: sep = "\t\t"; break; default: sep = "\t"; break; } printf("#define\t%s%s%#010x\n", name, sep, value); } int syserr(void) { fprintf(stderr, "%s: %s\n", progname, strerror(errno)); return (1); } int usage() { (void)fprintf(stderr, "usage: %s [-mrv]\n", progname); return (EXIT_FAILURE); }