/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * Read in "high-level" adb script and emit C program. * The input may have specifications within {} which * we analyze and then emit C code to generate the * ultimate adb acript. * We are just a filter; no arguments are accepted. */ #include #include #include #define streq(s1, s2) (strcmp(s1, s2) == 0) #define LINELEN 1024 /* max line length expected in input */ #define STRLEN 128 /* for shorter strings */ #define NARGS 5 /* number of emitted subroutine arguments */ /* * Format specifier strings * which are recognized by adbgen when surrounded by {} */ #define FSTR_PTR "POINTER" #define FSTR_LONG_DEC "LONGDEC" #define FSTR_LONG_OCT "LONGOCT" #define FSTR_ULONG_DEC "ULONGDEC" #define FSTR_ULONG_HEX "ULONGHEX" #define FSTR_ULONG_OCT "ULONGOCT" /* * Types of specifications in {}. */ #define PTR_HEX 0 /* emit hex pointer format char */ #define LONG_DEC 1 /* emit decimal long format char */ #define LONG_OCT 2 /* emit octal unsigned long format char */ #define ULONG_DEC 3 /* emit decimal unsigned long format char */ #define ULONG_HEX 4 /* emit hexadecimal long format char */ #define ULONG_OCT 5 /* emit octal unsigned long format char */ #define FMT_ENTRIES 6 /* number of adbgen format specifier strings */ #define PRINT 6 /* print member name with format */ #define INDIRECT 7 /* fetch member value */ #define OFFSETOK 8 /* insist that the offset is ok */ #define SIZEOF 9 /* print sizeof struct */ #define END 10 /* get offset to end of struct */ #define OFFSET 11 /* just emit offset */ #define EXPR 12 /* arbitrary C expression */ /* * Special return code from nextchar. */ #define CPP -2 /* cpp line, restart parsing */ typedef struct adbgen_fmt { char *f_str; char f_char; } adbgen_fmt_t; char struct_name[STRLEN]; /* struct name */ char member[STRLEN]; /* member name */ char format[STRLEN]; /* adb format spec */ char arg[NARGS][STRLEN]; /* arg list for called subroutine */ char *ptr_hex_fmt; /* adb format character for pointer in hex */ char *long_dec_fmt; /* adb format character for long in decimal */ char *ulong_dec_fmt; /* adb format character for ulong in decimal */ char *ulong_hex_fmt; /* adb format character for ulong in hex */ char *long_oct_fmt; /* adb format character for long in octal */ char *ulong_oct_fmt; /* adb format character for ulong in octal */ int line_no = 1; /* input line number - for error messages */ int specsize; /* size of {} specification - 1 or 2 parts */ int state; /* XXX 1 = gathering a printf */ /* This is a kludge so we emit pending */ /* printf's when we see a CPP line */ adbgen_fmt_t adbgen_fmt_tbl [FMT_ENTRIES] = { {FSTR_PTR}, {FSTR_LONG_DEC}, {FSTR_LONG_OCT}, {FSTR_ULONG_DEC}, {FSTR_ULONG_HEX}, {FSTR_ULONG_OCT} }; void emit_call(char *name, int nargs); void emit_end(void); void emit_expr(void); void emit_indirect(void); void emit_offset(void); void emit_offsetok(void); void emit_print(void); void emit_printf(char *cp); void emit_sizeof(void); void generate(void); int get_type(void); int nextchar(char *cp); void read_spec(void); char *start_printf(void); int main(int argc, char **argv) { char *cp; int c; int warn_flag = 0; int is_lp64 = 0; char *usage = "adbgen1 [-w] [-m ilp32|lp64] < \n"; while ((c = getopt(argc, argv, "m:w")) != EOF) { switch (c) { case 'm': if (streq(optarg, "ilp32")) is_lp64 = 0; else if (streq(optarg, "lp64")) is_lp64 = 1; else fprintf(stderr, usage); break; case 'w': warn_flag++; break; case '?': fprintf(stderr, usage); break; } } if (is_lp64) { adbgen_fmt_tbl[PTR_HEX].f_char = 'J'; adbgen_fmt_tbl[LONG_DEC].f_char = 'e'; adbgen_fmt_tbl[LONG_OCT].f_char = 'g'; adbgen_fmt_tbl[ULONG_DEC].f_char = 'E'; adbgen_fmt_tbl[ULONG_HEX].f_char = 'J'; adbgen_fmt_tbl[ULONG_OCT].f_char = 'G'; } else { adbgen_fmt_tbl[PTR_HEX].f_char = 'X'; adbgen_fmt_tbl[LONG_DEC].f_char = 'D'; adbgen_fmt_tbl[LONG_OCT].f_char = 'Q'; adbgen_fmt_tbl[ULONG_DEC].f_char = 'U'; adbgen_fmt_tbl[ULONG_HEX].f_char = 'X'; adbgen_fmt_tbl[ULONG_OCT].f_char = 'O'; } /* * Get structure name. */ cp = struct_name; while ((c = nextchar(NULL)) != '\n') { if (c == EOF) { fprintf(stderr, "Premature EOF\n"); exit(1); } if (c == CPP) continue; *cp++ = (char)c; } *cp = '\0'; /* * Basically, the generated program is just an ongoing printf * with breaks for {} format specifications. */ printf("\n"); printf("#include \n"); printf("#include \n"); printf("\n\n"); printf("int do_fmt(char *acp);\n"); printf("void format(char *name, size_t size, char *fmt);\n"); printf("void indirect(off_t offset, size_t size, " "char *base, char *member);\n"); printf("void offset(off_t off);\n"); printf("void offsetok(void);\n"); printf("\n\n"); printf("main(int argc, char *argv[])\n"); printf("{\n"); if (warn_flag) { printf("\textern int warnings;\n\n\twarnings = 0;\n"); } cp = start_printf(); while ((c = nextchar(cp)) != EOF) { switch (c) { case '"': *cp++ = '\\'; /* escape ' in string */ *cp++ = '"'; break; case '\n': *cp++ = '\\'; /* escape newline in string */ *cp++ = 'n'; break; case '{': emit_printf(cp); read_spec(); generate(); cp = start_printf(); break; case CPP: /* * Restart printf after cpp line. */ cp = start_printf(); break; default: *cp++ = c; break; } if (cp - arg[1] >= STRLEN - 10) { emit_printf(cp); cp = start_printf(); } } emit_printf(cp); /* terminate program, checking for "error" mode */ printf("\n\tif (argc > 1 && strcmp(argv[1], \"-e\") == 0) {\n"); printf("\t\textern int warns;\n\n"); printf("\t\tif (warns)\n"); printf("\t\t\treturn (1);\n"); printf("\t}\n"); printf("\treturn (0);\n"); printf("}\n"); return (0); } int nextchar(char *cp) { int c; static int newline = 1; c = getchar(); /* * Lines beginning with '#' and blank lines are passed right through. */ while (newline) { switch (c) { case '#': if (state) emit_printf(cp); do { putchar(c); c = getchar(); if (c == EOF) return (c); } while (c != '\n'); putchar(c); line_no++; return (CPP); case '\n': if (state) emit_printf(cp); putchar(c); c = getchar(); line_no++; break; default: newline = 0; break; } } if (c == '\n') { newline++; line_no++; } return (c); } /* * Get started on printf of ongoing adb script. */ char * start_printf(void) { char *cp; strcpy(arg[0], "\"%s\""); cp = arg[1]; *cp++ = '"'; state = 1; /* XXX */ return (cp); } /* * Emit call to printf to print part of ongoing adb script. */ void emit_printf(cp) char *cp; { *cp++ = '"'; *cp = '\0'; emit_call("printf", 2); state = 0; /* XXX */ } /* * Read {} specification. * The first part (up to a comma) is put into "member". * The second part, if present, is put into "format". */ void read_spec(void) { char *cp; int c; int nesting; cp = member; specsize = 1; nesting = 0; while ((c = nextchar(NULL)) != '}' || (c == '}' && nesting)) { switch (c) { case EOF: fprintf(stderr, "Unexpected EOF inside {}\n"); exit(1); case '\n': fprintf(stderr, "Newline not allowed in {}, line %d\n", line_no); exit(1); case '#': fprintf(stderr, "# not allowed in {}, line %d\n", line_no); exit(1); case ',': if (specsize == 2) { fprintf(stderr, "Excessive commas in {}, "); fprintf(stderr, "line %d\n", line_no); exit(1); } specsize = 2; *cp = '\0'; cp = format; break; case '{': /* * Allow up to one set of nested {}'s for adbgen * requests of the form {member, {format string}} */ if (!nesting) { nesting = 1; *cp++ = c; } else { fprintf(stderr, "Too many {'s, line %d\n", line_no); exit(1); } break; case '}': *cp++ = c; nesting = 0; break; default: *cp++ = c; break; } } *cp = '\0'; if (cp == member) { specsize = 0; } } /* * Decide what type of input specification we have. */ int get_type(void) { int i; if (specsize == 1) { if (streq(member, "SIZEOF")) { return (SIZEOF); } if (streq(member, "OFFSETOK")) { return (OFFSETOK); } if (streq(member, "END")) { return (END); } for (i = 0; i < FMT_ENTRIES; i++) if (streq(member, adbgen_fmt_tbl[i].f_str)) return (i); return (OFFSET); } if (specsize == 2) { if (member[0] == '*') { return (INDIRECT); } if (streq(member, "EXPR")) { return (EXPR); } return (PRINT); } fprintf(stderr, "Invalid specification, line %d\n", line_no); exit(1); } /* * Generate the appropriate output for an input specification. */ void generate(void) { char *cp; int type; type = get_type(); switch (type) { case PTR_HEX: case LONG_DEC: case LONG_OCT: case ULONG_DEC: case ULONG_HEX: case ULONG_OCT: cp = start_printf(); *cp++ = adbgen_fmt_tbl[type].f_char; emit_printf(cp); break; case PRINT: emit_print(); break; case OFFSET: emit_offset(); break; case INDIRECT: emit_indirect(); break; case OFFSETOK: emit_offsetok(); break; case SIZEOF: emit_sizeof(); break; case EXPR: emit_expr(); break; case END: emit_end(); break; default: fprintf(stderr, "Internal error in generate\n"); exit(1); } } /* * Emit calls to set the offset and print a member. */ void emit_print(void) { char *cp; char fmt_request[STRLEN]; int i; char number[STRLEN]; emit_offset(); /* * Emit call to "format" subroutine */ sprintf(arg[0], "\"%s\"", member); sprintf(arg[1], "sizeof ((struct %s *)0)->%s", struct_name, member); /* * Split the format string into * This is for format strings that contain format specifier requests * like {POINTER_HEX}, {LONG_DEC}, etc. which need to be substituted * with a format character instead. */ for (cp = format, i = 0; *cp >= '0' && *cp <= '9' && *cp != '\0'; cp++, i++) number[i] = *cp; number[i] = '\0'; for (i = 0; i < FMT_ENTRIES; i++) { (void) sprintf(fmt_request, "{%s}", adbgen_fmt_tbl[i].f_str); if (streq(cp, fmt_request)) { sprintf(arg[2], "\"%s%c\"", number, adbgen_fmt_tbl[i].f_char); break; } } if (i == FMT_ENTRIES) sprintf(arg[2], "\"%s\"", format); emit_call("format", 3); } /* * Emit calls to set the offset and print a member. */ void emit_offset(void) { /* * Emit call to "offset" subroutine */ sprintf(arg[0], "(off_t) &(((struct %s *)0)->%s)", struct_name, member); emit_call("offset", 1); } /* * Emit call to indirect routine. */ void emit_indirect(void) { sprintf(arg[0], "(off_t) &(((struct %s *)0)->%s)", struct_name, member+1); sprintf(arg[1], "sizeof ((struct %s *)0)->%s", struct_name, member+1); sprintf(arg[2], "\"%s\"", format); /* adb register name */ sprintf(arg[3], "\"%s\"", member); emit_call("indirect", 4); } /* * Emit call to "offsetok" routine. */ void emit_offsetok(void) { emit_call("offsetok", 0); } /* * Emit call to printf the sizeof the structure. */ void emit_sizeof(void) { sprintf(arg[0], "\"0t%%d\""); sprintf(arg[1], "sizeof (struct %s)", struct_name); emit_call("printf", 2); } /* * Emit call to printf an arbitrary C expression. */ void emit_expr(void) { sprintf(arg[0], "\"0t%%d\""); sprintf(arg[1], "(%s)", format); emit_call("printf", 2); } /* * Emit call to set offset to end of struct. */ void emit_end(void) { sprintf(arg[0], "sizeof (struct %s)", struct_name); emit_call("offset", 1); } /* * Emit call to subroutine name with nargs arguments from arg array. */ void emit_call(char *name, int nargs) { int i; printf("\t%s(", name); /* name of subroutine */ for (i = 0; i < nargs; i++) { if (i > 0) { printf(", "); /* argument separator */ } printf("%s", arg[i]); /* argument */ } printf(");\n"); /* end of call */ }