root/usr.bin/dig/lib/dns/gen.c
/*
 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

/*! \file */

#include <sys/types.h>

#include <ctype.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#define INSIST(cond) \
        if (!(cond)) { \
                fprintf(stderr, "%s:%d: INSIST(%s)\n", \
                         __FILE__, __LINE__, #cond); \
                abort(); \
        }

#define TOTEXTARGS "rdata, tctx, target"
#define TOTEXTCLASS "rdata->rdclass"
#define TOTEXTTYPE "rdata->type"
#define TOTEXTDEF "use_default = 1"

#define FROMWIREARGS "rdclass, type, source, dctx, options, target"
#define FROMWIRECLASS "rdclass"
#define FROMWIRETYPE "type"
#define FROMWIREDEF "use_default = 1"

#define TOWIREARGS "rdata, cctx, target"
#define TOWIRECLASS "rdata->rdclass"
#define TOWIRETYPE "rdata->type"
#define TOWIREDEF "use_default = 1"

static const char copyright[] =
"/*\n"
" * Copyright (C) 2004%s Internet Systems Consortium, Inc. (\"ISC\")\n"
" * Copyright (C) 1998-2003 Internet Software Consortium.\n"
" *\n"
" * Permission to use, copy, modify, and distribute this software for any\n"
" * purpose with or without fee is hereby granted, provided that the above\n"
" * copyright notice and this permission notice appear in all copies.\n"
" *\n"
" * THE SOFTWARE IS PROVIDED \"AS IS\" AND ISC DISCLAIMS ALL WARRANTIES WITH\n"
" * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY\n"
" * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,\n"
" * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM\n"
" * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE\n"
" * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR\n"
" * PERFORMANCE OF THIS SOFTWARE.\n"
" */\n"
"\n"
"/***************\n"
" ***************\n"
" ***************   THIS FILE IS AUTOMATICALLY GENERATED BY gen.c.\n"
" ***************   DO NOT EDIT!\n"
" ***************\n"
" ***************/\n"
"\n"
"/*! \\file */\n"
"\n";

#define STR_EXPAND(tok) #tok
#define STR(tok) STR_EXPAND(tok)

#define TYPECLASSLEN 20         /* DNS mnemonic size. Must be less than 100. */
#define TYPECLASSBUF (TYPECLASSLEN + 1)
#define TYPECLASSFMT "%" STR(TYPECLASSLEN) "[-0-9a-z]_%d"
#define DIRNAMESIZE 256

static struct cc {
        struct cc *next;
        int rdclass;
        char classname[TYPECLASSBUF];
} *classes;

static struct tt {
        struct tt *next;
        int rdclass;
        int type;
        char classname[TYPECLASSBUF];
        char typename[TYPECLASSBUF];
        char dirname[DIRNAMESIZE];      /* XXX Should be max path length */
} *types;

static char *
funname(const char *, char *);
static void
doswitch(const char *, const char *, const char *, const char *,
         const char *, const char *);
static void
add(int, const char *, int, const char *, const char *);
static void
sd(int, const char *, const char *, char);


static char *
funname(const char *s, char *buf) {
        char *b = buf;
        char c;

        INSIST(strlen(s) < TYPECLASSBUF);
        while ((c = *s++)) {
                *b++ = (c == '-') ? '_' : c;
        }
        *b = '\0';
        return (buf);
}

static void
doswitch(const char *name, const char *function, const char *args,
         const char *tsw, const char *csw, const char *res)
{
        struct tt *tt;
        int first = 1;
        int lasttype = 0;
        int subswitch = 0;
        char buf1[TYPECLASSBUF], buf2[TYPECLASSBUF];
        const char *result = " result =";

        if (res == NULL)
                result = "";

        for (tt = types; tt != NULL; tt = tt->next) {
                if (first) {
                        fprintf(stdout, "\n#define %s \\\n", name);
                        fprintf(stdout, "\tswitch (%s) { \\\n" /*}*/, tsw);
                        first = 0;
                }
                if (tt->type != lasttype && subswitch) {
                        if (res == NULL)
                                fprintf(stdout, "\t\tdefault: break; \\\n");
                        else
                                fprintf(stdout,
                                        "\t\tdefault: %s; break; \\\n", res);
                        fputs(/*{*/ "\t\t} \\\n", stdout);
                        fputs("\t\tbreak; \\\n", stdout);
                        subswitch = 0;
                }
                if (tt->rdclass && tt->type != lasttype) {
                        fprintf(stdout, "\tcase %d: switch (%s) { \\\n" /*}*/,
                                tt->type, csw);
                        subswitch = 1;
                }
                if (tt->rdclass == 0)
                        fprintf(stdout,
                                "\tcase %d:%s %s_%s(%s); break;",
                                tt->type, result, function,
                                funname(tt->typename, buf1), args);
                else
                        fprintf(stdout,
                                "\t\tcase %d:%s %s_%s_%s(%s); break;",
                                tt->rdclass, result, function,
                                funname(tt->classname, buf1),
                                funname(tt->typename, buf2), args);
                fputs(" \\\n", stdout);
                lasttype = tt->type;
        }
        if (subswitch) {
                if (res == NULL)
                        fprintf(stdout, "\t\tdefault: break; \\\n");
                else
                        fprintf(stdout, "\t\tdefault: %s; break; \\\n", res);
                fputs(/*{*/ "\t\t} \\\n", stdout);
                fputs("\t\tbreak; \\\n", stdout);
        }
        if (first) {
                if (res == NULL)
                        fprintf(stdout, "\n#define %s\n", name);
                else
                        fprintf(stdout, "\n#define %s %s;\n", name, res);
        } else {
                if (res == NULL)
                        fprintf(stdout, "\tdefault: break; \\\n");
                else
                        fprintf(stdout, "\tdefault: %s; break; \\\n", res);
                fputs(/*{*/ "\t}\n", stdout);
        }
}

static void
add(int rdclass, const char *classname, int type, const char *typename,
    const char *dirname)
{
        struct tt *newtt = (struct tt *)malloc(sizeof(*newtt));
        struct tt *tt, *oldtt;
        struct cc *newcc;
        struct cc *cc, *oldcc;

        INSIST(strlen(typename) < TYPECLASSBUF);
        INSIST(strlen(classname) < TYPECLASSBUF);
        INSIST(strlen(dirname) < DIRNAMESIZE);

        if (newtt == NULL) {
                fprintf(stderr, "malloc() failed\n");
                exit(1);
        }

        newtt->next = NULL;
        newtt->rdclass = rdclass;
        newtt->type = type;

        strncpy(newtt->classname, classname, sizeof(newtt->classname));
        newtt->classname[sizeof(newtt->classname) - 1] = '\0';

        strncpy(newtt->typename, typename, sizeof(newtt->typename));
        newtt->typename[sizeof(newtt->typename) - 1] = '\0';

        if (strncmp(dirname, "./", 2) == 0)
                dirname += 2;
        strncpy(newtt->dirname, dirname, sizeof(newtt->dirname));
        newtt->dirname[sizeof(newtt->dirname) - 1] = '\0';

        tt = types;
        oldtt = NULL;

        while ((tt != NULL) && (tt->type < type)) {
                oldtt = tt;
                tt = tt->next;
        }

        while ((tt != NULL) && (tt->type == type) && (tt->rdclass < rdclass)) {
                if (strcmp(tt->typename, typename) != 0)
                        exit(1);
                oldtt = tt;
                tt = tt->next;
        }

        if ((tt != NULL) && (tt->type == type) && (tt->rdclass == rdclass))
                exit(1);

        newtt->next = tt;
        if (oldtt != NULL)
                oldtt->next = newtt;
        else
                types = newtt;

        /*
         * Do a class switch for this type.
         */
        if (rdclass == 0)
                return;

        newcc = (struct cc *)malloc(sizeof(*newcc));
        if (newcc == NULL) {
                fprintf(stderr, "malloc() failed\n");
                exit(1);
        }
        newcc->rdclass = rdclass;
        strncpy(newcc->classname, classname, sizeof(newcc->classname));
        newcc->classname[sizeof(newcc->classname) - 1] = '\0';
        cc = classes;
        oldcc = NULL;

        while ((cc != NULL) && (cc->rdclass < rdclass)) {
                oldcc = cc;
                cc = cc->next;
        }

        if ((cc != NULL) && cc->rdclass == rdclass) {
                free((char *)newcc);
                return;
        }

        newcc->next = cc;
        if (oldcc != NULL)
                oldcc->next = newcc;
        else
                classes = newcc;
}

static void
sd(int rdclass, const char *classname, const char *dirname, char filetype) {
        DIR *dirp;
        struct dirent *dp;
        char buf[TYPECLASSLEN + sizeof("_65535.h")];
        char typename[TYPECLASSBUF];
        int type, n;

        if ((dirp = opendir(dirname)) == NULL)
                return;

        while ((dp = readdir(dirp)) != NULL) {
                if (sscanf(dp->d_name, TYPECLASSFMT, typename, &type) != 2)
                        continue;
                if ((type > 65535) || (type < 0))
                        continue;

                n = snprintf(buf, sizeof(buf), "%s_%d.%c", typename,
                             type, filetype);
                INSIST(n > 0 && (unsigned)n < sizeof(buf));
                if (strcmp(buf, dp->d_name) != 0)
                        continue;
                add(rdclass, classname, type, typename, dirname);
        }

        closedir(dirp);
}

int
main(int argc, char **argv) {
        DIR *dirp;
        struct dirent *dp;
        char buf[DIRNAMESIZE];          /* XXX Should be max path length */
        char srcdir[DIRNAMESIZE];       /* XXX Should be max path length */
        int rdclass;
        char classname[TYPECLASSBUF];
        struct tt *tt;
        struct cc *cc;
        struct tm *tm;
        time_t now;
        char year[11];
        int lasttype;
        int code = 1;
        int class_enum = 0;
        int type_enum = 0;
        int structs = 0;
        int depend = 0;
        int c, n;
        char buf1[TYPECLASSBUF];
        char filetype = 'c';
        FILE *fd;
        char *prefix = NULL;
        char *suffix = NULL;
        char *file = NULL;

        srcdir[0] = '\0';
        while ((c = getopt(argc, argv, "cdits:F:P:S:")) != -1)
                switch (c) {
                case 'c':
                        code = 0;
                        depend = 0;
                        type_enum = 0;
                        class_enum = 1;
                        filetype = 'c';
                        structs = 0;
                        break;
                case 'd':
                        code = 0;
                        depend = 1;
                        class_enum = 0;
                        type_enum = 0;
                        structs = 0;
                        filetype = 'h';
                        break;
                case 't':
                        code = 0;
                        depend = 0;
                        class_enum = 0;
                        type_enum = 1;
                        filetype = 'c';
                        structs = 0;
                        break;
                case 'i':
                        code = 0;
                        depend = 0;
                        class_enum = 0;
                        type_enum = 0;
                        structs = 1;
                        filetype = 'h';
                        break;
                case 's':
                        if (strlen(optarg) >
                            DIRNAMESIZE - 2 * TYPECLASSLEN  -
                            sizeof("/rdata/_65535_65535")) {
                                fprintf(stderr, "\"%s\" too long\n",
                                        optarg);
                                exit(1);
                        }
                        n = snprintf(srcdir, sizeof(srcdir), "%s/",
                                     optarg);
                        INSIST(n > 0 && (unsigned)n < sizeof(srcdir));
                        break;
                case 'F':
                        file = optarg;
                        break;
                case 'P':
                        prefix = optarg;
                        break;
                case 'S':
                        suffix = optarg;
                        break;
                case '?':
                        exit(1);
                }

        n = snprintf(buf, sizeof(buf), "%srdata", srcdir);
        INSIST(n > 0 && (unsigned)n < sizeof(srcdir));

        if ((dirp = opendir(buf)) == NULL)
                exit(1);

        while ((dp = readdir(dirp)) != NULL) {
                if (sscanf(dp->d_name, TYPECLASSFMT, classname,
                           &rdclass) != 2)
                        continue;
                if ((rdclass > 65535) || (rdclass < 0))
                        continue;

                n = snprintf(buf, sizeof(buf), "%srdata/%s_%d",
                             srcdir, classname, rdclass);
                INSIST(n > 0 && (unsigned)n < sizeof(buf));
                if (strcmp(buf + 6 + strlen(srcdir), dp->d_name) != 0)
                        continue;
                sd(rdclass, classname, buf, filetype);
        }
        closedir(dirp);

        n = snprintf(buf, sizeof(buf), "%srdata/generic", srcdir);
        INSIST(n > 0 && (unsigned)n < sizeof(srcdir));
        sd(0, "", buf, filetype);

        if (time(&now) != -1) {
                if ((tm = localtime(&now)) != NULL && tm->tm_year > 104) {
                        n = snprintf(year, sizeof(year), "-%d",
                                     tm->tm_year + 1900);
                        INSIST(n > 0 && (unsigned)n < sizeof(year));
                } else
                        year[0] = 0;
        } else
                year[0] = 0;

        if (!depend)
                fprintf(stdout, copyright, year);

        if (code) {
                fputs("#ifndef DNS_CODE_H\n", stdout);
                fputs("#define DNS_CODE_H 1\n\n", stdout);

                fputs("#include <isc/result.h>\n\n", stdout);
                fputs("#include <dns/name.h>\n\n", stdout);

                for (tt = types; tt != NULL; tt = tt->next)
                        fprintf(stdout, "#include \"%s/%s_%d.c\"\n",
                                tt->dirname, tt->typename, tt->type);

                fputs("\n\n", stdout);

                doswitch("TOTEXTSWITCH", "totext", TOTEXTARGS,
                         TOTEXTTYPE, TOTEXTCLASS, TOTEXTDEF);
                doswitch("FROMWIRESWITCH", "fromwire", FROMWIREARGS,
                         FROMWIRETYPE, FROMWIRECLASS, FROMWIREDEF);
                doswitch("TOWIRESWITCH", "towire", TOWIREARGS,
                         TOWIRETYPE, TOWIRECLASS, TOWIREDEF);

                fputs("#endif /* DNS_CODE_H */\n", stdout);
        } else if (type_enum) {
                char *s;

                fprintf(stdout, "#ifndef DNS_ENUMTYPE_H\n");
                fprintf(stdout, "#define DNS_ENUMTYPE_H 1\n\n");

                fprintf(stdout, "enum {\n");
                fprintf(stdout, "\tdns_rdatatype_none = 0,\n");

                lasttype = 0;
                for (tt = types; tt != NULL; tt = tt->next)
                        if (tt->type != lasttype)
                                fprintf(stdout,
                                        "\tdns_rdatatype_%s = %d,\n",
                                        funname(tt->typename, buf1),
                                        lasttype = tt->type);

                fprintf(stdout, "\tdns_rdatatype_ixfr = 251,\n");
                fprintf(stdout, "\tdns_rdatatype_axfr = 252,\n");
                fprintf(stdout, "\tdns_rdatatype_mailb = 253,\n");
                fprintf(stdout, "\tdns_rdatatype_maila = 254,\n");
                fprintf(stdout, "\tdns_rdatatype_any = 255\n");

                fprintf(stdout, "};\n\n");

                fprintf(stdout, "#define dns_rdatatype_none\t"
                        "((dns_rdatatype_t)dns_rdatatype_none)\n");

                for (tt = types; tt != NULL; tt = tt->next)
                        if (tt->type != lasttype) {
                                s = funname(tt->typename, buf1);
                                fprintf(stdout,
                                        "#define dns_rdatatype_%s\t%s"
                                        "((dns_rdatatype_t)dns_rdatatype_%s)"
                                        "\n",
                                        s, strlen(s) < 2U ? "\t" : "", s);
                                lasttype = tt->type;
                        }

                fprintf(stdout, "#define dns_rdatatype_ixfr\t"
                        "((dns_rdatatype_t)dns_rdatatype_ixfr)\n");
                fprintf(stdout, "#define dns_rdatatype_axfr\t"
                        "((dns_rdatatype_t)dns_rdatatype_axfr)\n");
                fprintf(stdout, "#define dns_rdatatype_mailb\t"
                        "((dns_rdatatype_t)dns_rdatatype_mailb)\n");
                fprintf(stdout, "#define dns_rdatatype_maila\t"
                        "((dns_rdatatype_t)dns_rdatatype_maila)\n");
                fprintf(stdout, "#define dns_rdatatype_any\t"
                        "((dns_rdatatype_t)dns_rdatatype_any)\n");

                fprintf(stdout, "\n#endif /* DNS_ENUMTYPE_H */\n");

        } else if (class_enum) {
                char *s;
                int classnum;

                fprintf(stdout, "#ifndef DNS_ENUMCLASS_H\n");
                fprintf(stdout, "#define DNS_ENUMCLASS_H 1\n\n");

                fprintf(stdout, "enum {\n");

                fprintf(stdout, "\tdns_rdataclass_reserved0 = 0,\n");
                fprintf(stdout, "#define dns_rdataclass_reserved0 \\\n\t\t\t\t"
                        "((dns_rdataclass_t)dns_rdataclass_reserved0)\n");

#define PRINTCLASS(name, num) \
        do { \
                s = funname(name, buf1); \
                classnum = num; \
                fprintf(stdout, "\tdns_rdataclass_%s = %d%s\n", s, classnum, \
                       classnum != 255 ? "," : ""); \
                fprintf(stdout, "#define dns_rdataclass_%s\t" \
                       "((dns_rdataclass_t)dns_rdataclass_%s)\n", s, s); \
        } while (0)

                for (cc = classes; cc != NULL; cc = cc->next) {
                        if (cc->rdclass == 3)
                                PRINTCLASS("chaos", 3);
                        else if (cc->rdclass == 255)
                                PRINTCLASS("none", 254);
                        PRINTCLASS(cc->classname, cc->rdclass);
                }

#undef PRINTCLASS

                fprintf(stdout, "};\n\n");
                fprintf(stdout, "#endif /* DNS_ENUMCLASS_H */\n");
        } else if (structs) {
                if (prefix != NULL) {
                        if ((fd = fopen(prefix,"r")) != NULL) {
                                while (fgets(buf, sizeof(buf), fd) != NULL)
                                        fputs(buf, stdout);
                                fclose(fd);
                        }
                }
                for (tt = types; tt != NULL; tt = tt->next) {
                        snprintf(buf, sizeof(buf), "%s/%s_%d.h",
                                tt->dirname, tt->typename, tt->type);
                        if ((fd = fopen(buf,"r")) != NULL) {
                                while (fgets(buf, sizeof(buf), fd) != NULL)
                                        fputs(buf, stdout);
                                fclose(fd);
                        }
                }
                if (suffix != NULL) {
                        if ((fd = fopen(suffix,"r")) != NULL) {
                                while (fgets(buf, sizeof(buf), fd) != NULL)
                                        fputs(buf, stdout);
                                fclose(fd);
                        }
                }
        } else if (depend) {
                for (tt = types; tt != NULL; tt = tt->next)
                        fprintf(stdout, "%s:\t%s/%s_%d.h\n", file,
                                tt->dirname, tt->typename, tt->type);
        }

        if (ferror(stdout) != 0)
                exit(1);

        return (0);
}