/*
* Copyright (c) 2002, The Tendra Project <http://www.ten15.org/>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice unmodified, this list of conditions, and the following
* disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Crown Copyright (c) 1997
*
* This TenDRA(r) Computer Program is subject to Copyright
* owned by the United Kingdom Secretary of State for Defence
* acting through the Defence Evaluation and Research Agency
* (DERA). It is made available to Recipients with a
* royalty-free licence for its use, reproduction, transfer
* to other parties and amendment for any purpose not excluding
* product development provided that any such use et cetera
* shall be deemed to be acceptance of the following conditions:-
*
* (1) Its Recipients shall ensure that this Notice is
* reproduced upon any copies or amended versions of it;
*
* (2) Any amended version of it shall be clearly marked to
* show both the nature of and the organisation responsible
* for the relevant amendment or amendments;
*
* (3) Its onward transfer from a recipient to another
* party shall be deemed to be that party's acceptance of
* these conditions;
*
* (4) DERA gives no warranty or assurance as to its
* quality or suitability for any purpose and DERA accepts
* no liability whatsoever in relation to any use to which
* it may be put.
*
* $TenDRA: tendra/src/tools/tspec/index.c,v 1.8 2004/08/08 08:50:20 stefanf Exp $
*/
#include "config.h"
#include "fmm.h"
#include "msgcat.h"
#include "object.h"
#include "hash.h"
#include "name.h"
#include "type.h"
#include "print.h"
#include "utility.h"
/*
* FLAGS
*
* These flags are used to indicate various output states indicated by
* preprocessing directives. A value of 1 is the default (which
* actually means that the condition is false), 2 means that the given
* statement is true, and 0 means that its negation is true.
*/
static int building_libs = 1;
static int commented_out = 1;
/*
* FIELD SEPARATOR
*
* The field separator for the machine processable index. This
* separator can be changed, but no command line option is provided to
* do this as '$' seems ideal.
*/
static char field_sep = '$';
/*
* PRINT FIELD SEPARATOR
*
* Routine to print field separator of the machine processable index.
*/
#define print_field_sep() IGNORE putchar (field_sep)
/*
* PRINT FIELD
*
* Routine to print field and separator of the machine processable index.
*/
static void
print_field(char *s)
{
IGNORE printf ("%s%c", s, field_sep);
return;
}
/*
* PRINT ESCAPED FIELD
*
* Routine to print field without separator of the machine processable
* index, escaping characters that could confuse output processing tools.
*/
static void
print_escape(char *s)
{
int c;
while ((c = *s++) != '\0') {
if (c == field_sep) {
IGNORE printf ("\\F");
} else if (c == '\n') {
IGNORE printf ("\\n");
} else if (c == '\\') {
IGNORE printf ("\\\\");
} else {
IGNORE putchar (c);
}
}
return;
}
/*
* PRINT VALUE FIELD
*
* Routine to print the final value field of the machine processable index.
*/
static void
print_value(char *s)
{
print_field_sep ();
print_escape (s);
IGNORE putchar ('\n');
return;
}
/*
* PRINT EMPTY VALUE FIELD
*
* Routine to print the final empty value field of the machine processable
* index.
*/
static void
print_no_value(void)
{
IGNORE printf ("%c\n", field_sep);
return;
}
/*
* PRINT SORT, INFO AND TYPE FIELDS
*
* Routine to print sort, info and type fields of the machine processable
* index.
*/
static void
print_sit_v(char *s, char *i, type *t, char *nm)
{
IGNORE printf ("%s%c%s%c", s, field_sep, i, field_sep);
IGNORE print_type (stdout, t, nm, 0);
return;
}
/*
* PRINT SORT AND TYPE FIELDS
*
* Routine to print sort, empty info, and type fields of the machine
* processable index.
*/
static void
print_st_v(char *s, type *t, char *nm)
{
print_sit_v (s, "", t, nm);
return;
}
/*
* PRINT SORT AND INFO FIELDS
*
* Routine to print sort, info, and empty type fields of the machine
* processable index.
*/
static void
print_si_v(char *s, char *i)
{
IGNORE printf ("%s%c%s%c", s, field_sep, i, field_sep);
return;
}
/*
* PRINT SORT FIELD
*
* Routine to print sort, empty info, and empty type fields of the
* machine processable index.
*/
static void
print_s_v(char *s)
{
IGNORE printf ("%s%c%c", s, field_sep, field_sep);
return;
}
/*
* PRINT SORT, INFO, TYPE AND EMPTY VALUE FIELDS
*
* Routine to print sort, info, type and empty value fields of the
* machine processable index.
*/
static void
print_sit(char *s, char *i, type *t, char *nm)
{
print_sit_v (s, i, t, nm);
print_no_value ();
return;
}
/*
* PRINT SORT, TYPE AND EMPTY VALUE FIELDS
*
* Routine to print sort, empty info, type and empty value fields of the
* machine processable index.
*/
static void
print_st(char *s, type *t, char *nm)
{
print_st_v (s, t, nm);
print_no_value ();
return;
}
/*
* PRINT SORT, INFO AND EMPTY VALUE FIELDS
*
* Routine to print sort, info, empty type and empty value fields of the
* machine processable index.
*/
static void
print_si(char *s, char *i)
{
print_si_v (s, i);
print_no_value ();
return;
}
/*
* PRINT SORT AND EMPTY VALUE FIELDS
*
* Routine to print sort, empty info, empty type and empty value fields
* of the machine processable index.
*/
static void
print_s(char *s)
{
print_s_v (s);
print_no_value ();
return;
}
/*
* IF STACK STATE
*
* This stack is used to keep track of the current +IF conditions.
*/
static object **if_stack = 0;
static int if_stack_sz = 0;
static int if_stack_index = 0;
/*
* STACK AN IF COMMAND OBJECT
*
* Routine to stack an object representing +IFxxx or +ELSE.
*/
static void
stack_if (object *p)
{
if (if_stack_index == if_stack_sz) {
if_stack_sz += 16;
if_stack = xrealloc (if_stack, sizeof (*p) * if_stack_sz);
}
if_stack [if_stack_index] = p;
if_stack_index++;
return;
}
/*
* UNSTACK AN IF COMMAND OBJECT
*
* Routine to unstack an object representing +IFxxx or +ELSE.
*/
static object *
unstack_if(void)
{
return (if_stack [--if_stack_index]);
}
/*
* PRINT IF NESTING
*
* Routine to print the currently stacked +IFxxx and +ELSE nesting.
*/
static void
print_if_nest(void)
{
int i;
for (i = 0; i < if_stack_index; i++) {
char code;
object *p = if_stack [i];
char *c = p->name;
if (i > 0) print_escape (";");
if (i + 1 < if_stack_index &&
if_stack [i + 1]->u.u_num == CMD_ELSE) {
IGNORE printf ("e");
i++;
}
switch (p->u.u_num) EXHAUSTIVE {
case CMD_IF : code = 'i'; break;
case CMD_IFDEF : code = 'd'; break;
case CMD_IFNDEF : code = 'n'; break;
}
IGNORE printf ("%c", code);
print_escape (":");
print_escape (c);
}
return;
}
/*
* PRINT A MACHINE PROCESSABLE ITEM INDEX
*
* This routine prints the index item indicated by the token object p.
* u gives the token status, a the current file name, and e is used in
* enumeration items. The output is in fields suitable for machine
* processing by tools such as 'awk'.
*/
static void
print_item_m(object *p, char *u, char *a, type *e)
{
char *nm;
char *ap;
char *tnm = p->name;
object *q = p->u.u_obj;
if (q->objtype == OBJ_FIELD) {
nm = q->u.u_field->fname;
} else {
nm = q->name;
}
/* Field 1: C_SYMBOL */
print_field (nm);
/* Field 2: TOKEN */
print_field (tnm);
/* Field 3: STATUS */
IGNORE printf ("%c%c", u [0], field_sep);
/* Field 4: IF_NESTING */
print_if_nest ();
print_field_sep ();
/* Field 5: API_LOCATION */
for (ap = a; *ap && *ap != ':'; ap++) IGNORE putchar (*ap);
print_field_sep ();
/* Field 6: FILE_LOCATION */
if (*ap) ap++;
for (; *ap && *ap != ':'; ap++) IGNORE putchar (*ap);
print_field_sep ();
/* Field 7: LINE_LOCATION */
IGNORE printf ("%d%c", q->line_no, field_sep);
/* Field 8: SUBSET_NESTING */
if (*ap) IGNORE printf ("%s", ap + 1);
print_field_sep ();
/* Fields 9-12: SORT, INFO, TYPE, VALUE */
switch (q->objtype) {
case OBJ_CONST : {
print_st ("const", q->u.u_type, NULL);
break;
}
case OBJ_ENUMVAL : {
print_field ("enum_member");
print_type (stdout, e, NULL, 0);
print_field_sep ();
if (q->u.u_str) {
print_value (q->u.u_str);
} else {
print_no_value ();
}
break;
}
case OBJ_EXP : {
type *t = q->u.u_type;
char *s = (t->id == TYPE_LVALUE ? "lvalue" : "rvalue");
print_st (s, t, NULL);
break;
}
case OBJ_EXTERN : {
type *t = q->u.u_type;
if (t->id == TYPE_LVALUE) t = t->u.subtype;
if (t->id == TYPE_PROC) {
print_sit ("func", "extern", t, nm);
} else {
print_st ("extern", t, NULL);
}
break;
}
case OBJ_WEAK : {
type *t = q->u.u_type;
print_sit ("func", "weak", t, nm);
break;
}
case OBJ_DEFINE : {
char *s = q->u.u_str;
if (*s == '(') {
print_field ("define");
print_field ("param");
for (; *s && *s != ')'; s++) {
IGNORE putchar (*s);
}
if (*s == ')') s++;
IGNORE printf (")");
} else {
print_s_v ("define");
}
while (*s == ' ') s++;
print_value (s);
break;
}
case OBJ_FIELD : {
field *f = q->u.u_field;
print_field ("member");
print_type (stdout, f->stype, NULL, 0);
print_field_sep ();
print_type (stdout, f->ftype, NULL, 0);
print_no_value ();
break;
}
case OBJ_FUNC : {
print_st ("func", q->u.u_type, nm);
break;
}
case OBJ_MACRO : {
print_st ("macro", q->u.u_type, nm);
break;
}
case OBJ_NAT : {
print_s ("nat");
break;
}
case OBJ_STATEMENT : {
type *t = q->u.u_type;
if (t) {
print_sit ("statement", "param", t, NULL);
} else {
print_s ("statement");
}
break;
}
case OBJ_TOKEN : {
print_s_v ("token");
print_value (q->u.u_str);
break;
}
case OBJ_TYPE : {
type *t = q->u.u_type;
int i = t->id;
switch (i) {
case TYPE_DEFINED : {
print_st ("typedef", t->v.next, NULL);
break;
}
case TYPE_GENERIC : {
print_s ("opaque");
break;
}
case TYPE_INT : {
print_s ("integral");
break;
}
case TYPE_SIGNED : {
print_s ("signed_integral");
break;
}
case TYPE_UNSIGNED : {
print_s ("unsigned_integral");
break;
}
case TYPE_PROMOTE : {
print_field ("promotion");
print_type (stdout, t->v.next, NULL, 0);
print_field_sep ();
print_no_value ();
break;
}
case TYPE_FLOAT : {
print_s ("floating");
break;
}
case TYPE_ARITH : {
print_s ("arithmetic");
break;
}
case TYPE_SCALAR : {
print_s ("scalar");
break;
}
case TYPE_STRUCT :
case TYPE_STRUCT_TAG :
case TYPE_UNION :
case TYPE_UNION_TAG :
case TYPE_ENUM :
case TYPE_ENUM_TAG : {
char *s;
type *en = null;
object *r = t->v.obj2;
char *inf = (r ? "exact" : "");
switch (i) EXHAUSTIVE {
case TYPE_STRUCT : s = "struct"; break;
case TYPE_STRUCT_TAG : s = "struct_tag"; break;
case TYPE_UNION : s = "union"; break;
case TYPE_UNION_TAG : s = "union_tag"; break;
case TYPE_ENUM : s = "enum"; en = t; break;
case TYPE_ENUM_TAG : s = "enum_tag"; en = t; break;
}
print_si (s, inf);
while (r) {
print_item_m (r, u, a, en);
r = r->next;
}
break;
}
default : {
MSG_unknown_type_identifier (i);
break;
}
}
break;
}
default : {
MSG_unknown_object_type (q->objtype);
break;
}
}
return;
}
/*
* PRINT AN INDEX ITEM
*
* This routine prints the index item indicated by the token object p.
* u gives the token status, a the current file name, and e is used in
* enumeration items. The output is in a humun readable format.
*/
static void
print_item_h(object *p, char *u, char *a, type *e)
{
char *tnm = p->name;
object *q = p->u.u_obj;
char *nm = q->name;
IGNORE printf ("TOKEN: %s\n", tnm);
IGNORE printf ("STATUS: %s", u);
if (building_libs == 0) IGNORE printf (" (not library building)");
if (building_libs == 2) IGNORE printf (" (library building only)");
IGNORE printf ("\nDEFINED: %s, line %d\n", a, q->line_no);
IGNORE printf ("INFO: ");
if (commented_out == 2) IGNORE printf ("(commented out) ");
switch (q->objtype) {
case OBJ_CONST : {
IGNORE printf ("%s is a constant expression of type ", nm);
print_type (stdout, q->u.u_type, NULL, 0);
IGNORE printf ("\n\n");
break;
}
case OBJ_ENUMVAL : {
IGNORE printf ("%s is a member of the enumeration type ", nm);
print_type (stdout, e, NULL, 0);
IGNORE printf ("\n\n");
break;
}
case OBJ_EXP : {
IGNORE printf ("%s is an expression of type ", nm);
print_type (stdout, q->u.u_type, NULL, 0);
IGNORE printf ("\n\n");
break;
}
case OBJ_EXTERN : {
type *t = q->u.u_type;
if (t->id == TYPE_LVALUE) t = t->u.subtype;
IGNORE printf ("%s is an external ", nm);
if (t->id == TYPE_PROC) {
IGNORE printf ("function with prototype ");
print_type (stdout, t, nm, 0);
} else {
IGNORE printf ("expression with type ");
print_type (stdout, t, NULL, 0);
}
IGNORE printf ("\n\n");
break;
}
case OBJ_WEAK : {
type *t = q->u.u_type;
IGNORE printf ("%s is an external ", nm);
IGNORE printf ("function with weak prototype ");
print_type (stdout, t, nm, 0);
IGNORE printf ("\n\n");
break;
}
case OBJ_DEFINE : {
char *s = q->u.u_str;
IGNORE printf ("%s is a macro ", nm);
if (*s == '(') {
IGNORE printf ("with arguments ");
for (; *s && *s != ')'; s++) {
IGNORE putchar (*s);
}
if (*s == ')') s++;
IGNORE printf (") ");
}
while (*s == ' ') s++;
IGNORE printf ("defined to be %s\n\n", s);
break;
}
case OBJ_FIELD : {
field *f = q->u.u_field;
IGNORE printf ("%s is a field selector of ", f->fname);
print_type (stdout, f->stype, NULL, 0);
IGNORE printf (" of type ");
print_type (stdout, f->ftype, NULL, 0);
IGNORE printf ("\n\n");
break;
}
case OBJ_FUNC : {
IGNORE printf ("%s is a function with prototype ", nm);
print_type (stdout, q->u.u_type, nm, 0);
IGNORE printf ("\n\n");
break;
}
case OBJ_MACRO : {
IGNORE printf ("%s is a macro with prototype ", nm);
print_type (stdout, q->u.u_type, nm, 0);
IGNORE printf ("\n\n");
break;
}
case OBJ_NAT : {
IGNORE printf ("%s is a constant integer\n\n", nm);
break;
}
case OBJ_STATEMENT : {
type *t = q->u.u_type;
IGNORE printf ("%s is a statement", nm);
if (t) {
IGNORE printf (" with arguments");
print_type (stdout, t, NULL, 0);
}
IGNORE printf ("\n\n");
break;
}
case OBJ_TOKEN : {
IGNORE printf ("%s is a complex token\n\n", nm);
break;
}
case OBJ_TYPE : {
type *t = q->u.u_type;
int i = t->id;
print_type (stdout, t, NULL, 0);
switch (i) {
case TYPE_DEFINED : {
IGNORE printf (" is a type defined to be ");
print_type (stdout, t->v.next, NULL, 0);
IGNORE printf ("\n\n");
break;
}
case TYPE_GENERIC : {
IGNORE printf (" is a type\n\n");
break;
}
case TYPE_INT : {
IGNORE printf (" is an integral type\n\n");
break;
}
case TYPE_SIGNED : {
IGNORE printf (" is a signed integral type\n\n");
break;
}
case TYPE_UNSIGNED : {
IGNORE printf (" is an unsigned integral type\n\n");
break;
}
case TYPE_PROMOTE : {
IGNORE printf (" is the integral promotion of ");
print_type (stdout, t->v.next, NULL, 0);
IGNORE printf ("\n\n");
break;
}
case TYPE_FLOAT : {
IGNORE printf (" is a floating type\n\n");
break;
}
case TYPE_ARITH : {
IGNORE printf (" is an arithmetic type\n\n");
break;
}
case TYPE_SCALAR : {
IGNORE printf (" is a scalar type\n\n");
break;
}
case TYPE_STRUCT :
case TYPE_STRUCT_TAG :
case TYPE_UNION :
case TYPE_UNION_TAG : {
char *n;
object *r = t->v.obj2;
switch (i) EXHAUSTIVE {
case TYPE_STRUCT : n = "structure"; break;
case TYPE_STRUCT_TAG : n = "structure"; break;
case TYPE_UNION : n = "union"; break;
case TYPE_UNION_TAG : n = "union"; break;
}
if (r == null) {
IGNORE printf (" is an inexact %s type\n\n", n);
} else {
IGNORE printf (" is an exact %s type\n\n", n);
while (r) {
print_item_h (r, u, a, (type *) null);
r = r->next;
}
}
break;
}
case TYPE_ENUM :
case TYPE_ENUM_TAG : {
object *r = t->v.obj2;
IGNORE printf (" is an enumeration type\n\n");
while (r) {
print_item_h (r, u, a, t);
r = r->next;
}
break;
}
default : {
IGNORE printf (" is a type\n\n");
MSG_unknown_type_identifier (i);
break;
}
}
break;
}
default : {
MSG_unknown_object_type (q->objtype);
break;
}
}
return;
}
/*
* PRINT INDEX USING PRINT ITEM FUNCTION
*
* This routine prints an index of the set object input using fn.
*/
typedef void (*index_func)(object *, char *, char *, type *);
static void
print_index_with(object *input, index_func fn)
{
object *p = input->u.u_obj;
info *i = p->u.u_info;
char *a = p->name;
char *u = (i->implemented ? "implemented" : "used");
for (p = i->elements; p != null; p = p->next) {
switch (p->objtype) {
case OBJ_IF : {
/* Deal with preprocessing directives */
char *c = p->name;
if (fn == print_item_m) {
switch (p->u.u_num) {
case CMD_IF :
case CMD_IFDEF :
case CMD_IFNDEF :
case CMD_ELSE : {
stack_if (p);
break;
}
case CMD_ENDIF : {
if (unstack_if ()->u.u_num == CMD_ELSE) {
IGNORE unstack_if ();
}
break;
}
}
} else if (streq (c, BUILDING_MACRO)) {
/* Check for the building_libs macro */
switch (p->u.u_num) {
case CMD_IF :
case CMD_IFDEF : {
building_libs = 2;
break;
}
case CMD_IFNDEF : {
building_libs = 0;
break;
}
case CMD_ELSE : {
building_libs = 2 - building_libs;
break;
}
case CMD_ENDIF : {
building_libs = 1;
break;
}
}
} else {
/* Check for integers */
int n = 0;
while (*c == '-') c++;
while (*c >= '0' && *c <= '9') {
n = 10 * n + (*c - '0');
c++;
}
if (*c == 0) {
switch (p->u.u_num) {
case CMD_IF : {
commented_out = (n ? 0 : 2);
break;
}
case CMD_ELSE : {
commented_out = 2 - commented_out;
break;
}
case CMD_ENDIF : {
commented_out = 1;
break;
}
}
}
}
break;
}
case OBJ_SET : {
/* Deal with subsets */
print_index_with (p, fn);
break;
}
case OBJ_TOKEN : {
/* Deal with tokens */
if (i->implemented || !restrict_use) {
(*fn) (p, u, a, (type *) null);
}
break;
}
}
}
return;
}
/*
* PRINT MACHINE PROCESSABLE INDEX
*
* This routine prints an index intended for machine processing of the
* set object input.
*/
void
print_machine_index(object *input)
{
print_index_with (input, print_item_m);
return;
}
/*
* PRINT INDEX
*
* This routine prints an index intended for human readers of the set
* object input.
*/
void
print_index(object *input)
{
print_index_with (input, print_item_h);
return;
}
syntax highlighted by Code2HTML, v. 0.9.1