/*cstrong.c - output an help file given a module defn*/

/*OSLib---efficient, type-safe, transparent, extensible,\n"
   register-safe A P I coverage of RISC O S*/
/*Copyright  1994 Jonathan Coxhead*/

/*
      OSLib is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 1, or (at your option)
   any later version.

      OSLib is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

      You should have received a copy of the GNU General Public License
   along with this programme; if not, write to the Free Software
   Foundation, Inc, 675 Mass Ave, Cambridge, MA 02139, U S A.
*/

/*
   20001120 TV Major re-work to emit StrongHelp2 format files,
               sub-divided into consts, types, and SWIs, and
               alphabetically sorted
   20010130 TV fixes calls to *printf with null arguments (Timothy Baldwin)
*/

//#define TRACE 1

/*From CLib*/
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <kernel.h>     /* TV 990127 */
#include <stddef.h>

/*From OSLib*/
#include "oslib/macros.h"
#include "oslib/os.h"
#include "oslib/osfile.h"
#include "oslib/territory.h"

/*From Support*/
#include "x.h"
#include "lookup.h"
#include "trace.h"

/*local*/
#include "def.h"
#include "cstrong.h"

#define INDEX_MAX 1000        /* max number of items in each index */
#define INDEX_COLUMNS 1       /* number of columns on index pages */
#define DESCRIPTION_MAX 256   /* max no of chars in description line */

// sub-manual names
#define CONSTS _consts
#define TYPES _types
#define SWIS _swis
#define SEP "_"

#ifdef EXECUTE_ON_UNIX
#  define DIRSEP "/"
#else
#  define DIRSEP "."
#endif

/* type definitions */

/* global variables */
static int            Entry_Count;             /* number of items in index */
static char          *Strings[ INDEX_MAX ];    /* object names for the index */


/**************************************************************************/
/* return a pointer to a static string containing |level| tab chars
*/
static char* indent( int level )
{
   static char string[32];

   if( level < 0 ) level = 0;
   level %= 16;

   *string = '\0';
   while( level -- > 0 )
      strcat( string, " \t" );

   return string;
}


/****************************************************************/
/* case insensitive strcmp
** ignoring underscores
*/
static int ustricmp( const char *String1, const char *String2 )
{
  int iStatus;

  do
  {
      while( *String1 && *String1 == '_' )
         String1++;
      while( *String2 && *String2 == '_' )
         String2++;

      iStatus = toupper( *String1 ) - toupper( *String2 );
  }
  while( iStatus == 0 && *String1++ != '\0' && *String2++ != '\0' );

  return iStatus;
}


/****************************************************************/
/* compare two strings */
static int Cmp( void const *v0, void const *v1 )
{
   int cmp = 0;

   #define s0 (*(const char**)v0)
   #define s1 (*(const char**)v1)

   cmp = ustricmp( s0, s1 );

//   tracef( "cmp (\"%s\" \"%s\") = %i\n" _ s0 _ s1 _ SGN(cmp) );
   return cmp;
}


/****************************************************************/
/* create and open a data file <path_name>.<prefix>_.<suffix>
** returns file handle
** throws exception on error
*/
static FILE *open_file( const char *path_name,
                        const char *name,
                        osbool searchable
                      )
{
   char prefix[ FILENAME_MAX + 1 ];
   char data_name[ FILENAME_MAX + 1 ];
   FILE *help_data = NULL;
   char *suffix;

   /* extract the module name prefix */
   suffix = strchr( name, '_' );
   prefix[ 0 ] = '\0';
   if( suffix )
   {
      strncat( prefix, name, suffix-name );
   }

   /* ensure the directory exists */
   sprintf( data_name, "%s" DIRSEP "%s" SEP, path_name, prefix );
   tracef( "open_file - creating %s\n" _ data_name );
   osfile_create_dir( data_name, 0 );

   /* create the file */
   sprintf( data_name,
            "%s" DIRSEP "%s" SEP DIRSEP "%s",
            path_name,
            prefix,
            suffix? suffix + 1 : name
          );
   tracef( "open_file - opening %s\n" _ data_name );
   if( ( help_data = fopen( data_name, searchable?"w":"wb" ) ) == NULL )
      x_THROW_LAST_ERROR();

   return help_data;
}

/****************************************************************/
/* write non-index page header
** returns number of characters written
** throws exception on error
*/
static int header( FILE* file, const char* name, const char* title )
{
   int i;
   if ( ( i =  fprintf( file,
                        "%s\n"
                        "#Parent %s_\n"
                        "#Wrap Off\n"
                        "#FH3:OSLib: %s\n"
                        "#Line\n"
                        "#Tab ,fcode ,fcode ,fcode ,fcode ,fcode\n"
                        "Defined in:%soslib/<%s=>%s_>.h\n",
                        name,
                        title,
                        name,
                        indent(1),
                        title,
                        title
                      )
        ) < 0
      )
      x_THROW_LAST_ERROR();
   return i;
}

/****************************************************************/
/* create and open a new non-index page, and write its header
** returns file handle
** throws exception on error
*/
static FILE *open_page( const char *path_name,
                        const char *page_name,
                        const char *title
                      )
{
   FILE* file;

   /* Open the file */
   file = open_file( path_name, page_name, TRUE );

   /* write page header */
   header( file, page_name, title );

   return file;
}


/****************************************************************/
/* clear the index */
static void index_clear()
{
   for( ; Entry_Count > 0; Entry_Count-- )
   {
      tracef( "free %i %x\n" _ Entry_Count - 1 _ Strings[Entry_Count - 1] );
      free( Strings [Entry_Count - 1] );
   }
}


/****************************************************************/
/* Create a new record in the array of index entries,
**   preparatory to writing the index file.
**    handles the special case of an object name not containing an underscore
** throws exception on error
*/
static void index_entry( const char *entry )
{
   x_LOCAL_ERROR( Error_overflow, 0x101, "Index Overflow" );

   tracef ("entry %i -> \"%s\"\n" _ Entry_Count _ entry);
   if( Entry_Count < INDEX_MAX )
   {
      if( strchr( entry, '_' ) )
         Strings[ Entry_Count ] = qstrdup (entry);
      else
      {
         /* need special redirection if the name contains no underscore */
         char temp[def_ID_LIMIT];
         sprintf( temp, "%s=>" SEP "%s", entry, entry );
         Strings[ Entry_Count ] = qstrdup (temp);
      }
      Entry_Count++;
   }
   else
     x_THROW( Error_overflow );
}


/****************************************************************/
/* Create an index file
** if type == NULL, then write !root
** returns 0
** throws exception on error
*/
static int index_file(  const char* path_name,
                        const char* title,
                        const char* type
                     )
{
   int rc = 0;
   int i;
   FILE *index;
   char temp_name[ FILENAME_MAX + 1 ];
   x_exception x0;

   tracef ("index_file type \"%s\"\n" _ type ? type : "!Root");

   // create the directory/file
   sprintf( temp_name,
            "%s" SEP "%s",
            title,
            type==NULL ?
               "!Root":
               type
          );
   if ( ( index = open_file( path_name, temp_name, FALSE ) ) == NULL )
   {
      x_THROW_LAST_ERROR();
   }
   else
   {
      x_TRY( &x0 )
      {
         // sort the index
         qsort ( Strings, Entry_Count, sizeof Strings[0], &Cmp);

         // write the header
         if ( fprintf( index,
                        "%s%s\n"
                        "#Parent OSLib:!Root\n"
                        "#Wrap Off\n"
                        "#fH2:OSLib: %s.h%s%s\n"
                        "#Line\n"
                        "#Table Columns %d\n",
                        title,
                        type ? type : "",
                        title,
                        (type && *type) ? " - " : "",
                        (type && *type) ?
                           (*type == '_') ? type+1 : type
                           :  "",
                        (type && *type) ? INDEX_COLUMNS : 3
                     ) < 0
            ) x_THROW_LAST_ERROR();

         // write the index
         for( i = 0; i < Entry_Count; i++ )
         {
            if ( fprintf( index,
                          "<%s>\n", Strings[i]
                        ) < 0
               ) x_THROW_LAST_ERROR();
         }

         // write the tailer
         if ( rc >= 0 &&
               fprintf( index,
                         "#EndTable\n"
                      ) < 0
            ) x_THROW_LAST_ERROR();

         fclose( index );
         index_clear();

      }
      x_CATCH( &x0 )
      {
         fclose( index );
         x_RETHROW( &x0 );
      }
   }

   return rc;
}


/**************************************************************************/
/*Prints a declaration of |v| as an object of type |t|, using |tag| as the
**      structure tag.
**  throws exception on error
*/

static void Print_Decl
(
   const char  *title,  /* module title */
   FILE  *file,   /* output file */
   const def_t  t,      /* structure definition */
   const char  *tag,    /* structure tag */
   const char  *v,      /* structure name */
   osbool var,    /* */
   int    nest    /* format nesting level */
)
{
   int rc = 0;

   switch (t->tag)
   {
      case def_TYPE_INT:
         if ((rc = fprintf (file, v == NULL? "int": "int %s", v)) < 0)
            goto finish;
      break;

      case def_TYPE_SHORT:
         if ((rc = fprintf (file, v == NULL? "short": "short %s", v)) < 0)
            goto finish;
      break;

      case def_TYPE_BYTE:
         if ((rc = fprintf (file, v == NULL? "byte": "byte %s", v)) < 0)
            goto finish;
      break;

      case def_TYPE_CHAR:
         if ((rc = fprintf (file, v == NULL? "char": "char %s", v)) < 0)
            goto finish;
      break;

      case def_TYPE_BITS:
         if ((rc = fprintf (file, v == NULL? "bits": "bits %s", v)) < 0)
            goto finish;
      break;

      case def_TYPE_BYTES:
         if ((rc = fprintf (file, v == NULL? "bytes": "bytes %s", v)) < 0)
            goto finish;
      break;

      case def_TYPE_BOOL:
         if ((rc = fprintf (file, v == NULL? "osbool": "osbool %s", v)) < 0)
            goto finish;
      break;

      case def_TYPE_REF:
      {
         char v1 [def_ID_LIMIT + 1];

         if (v == NULL || v [0] == '/')
         {
            if (v != NULL)
            {
               if ((rc = sprintf (v1, "*%s", v)) < 0)
                  goto finish;
            }
            else
            {
               if ((rc = sprintf (v1, "*")) < 0)
                  goto finish;
            }
         }
         else
         {
            if ((rc = sprintf (v1, "*%s", v)) < 0)
               goto finish;
         }

         Print_Decl( title,
                     file,
                     t->data AS ref,
                     NULL,
                     v1,
                     FALSE,
                     nest + 1
                   );
      }
      break;

      case def_TYPE_STRING:
         if ((rc = fprintf (file, "char %s", v)) < 0)
            goto finish; /*v != NULL*/
      break;

      case def_TYPE_ASM:
         if ((rc = fprintf (file, "void %s", v)) < 0)
            goto finish; /*v != NULL*/
      break;

      case def_TYPE_DATA:
         if ((rc = fprintf (file, "byte %s", v)) < 0)
            goto finish; /*v != NULL*/
      break;

      case def_TYPE_STRUCT:
      case def_TYPE_UNION:
      {
         int i;

         if( var ) nest += 1;

         if
         (
            (
               rc = (tag != NULL)?
                     fprintf
                     (
                        file,
                        "%s %s%s\n%s\\{\t",
                        t->tag == def_TYPE_STRUCT? "struct":
                           t->tag == def_TYPE_UNION?  "union": "list",
                        tag,
                        var? " \\\\" : "",
                        indent( nest+1 )
                     ):
                     fprintf
                     (
                        file,
                        "%s%s\n%s\\{\t",
                        t->tag == def_TYPE_STRUCT? "struct":
                           t->tag == def_TYPE_UNION?  "union": "list",
                        var? " \\\\" : "",
                        indent( nest+1 )
                     )
            ) < 0
         )  goto finish;

         /* Print any base name if available */
         if (t->tag == def_TYPE_STRUCT && t->data AS list.base)
         {
            /* structure with base definition; emit its name */
            char base [def_ID_LIMIT + 1];
            char prefix [def_ID_LIMIT + 1];

            def_as_macro (base, t->data AS list.base->data AS id);
            def_as_prefix( prefix, t->data AS list.base->data AS id);

            if (  (  rc = fprintf(  file,
                                    "<%s_MEMBERS>\n%s",
                                    base,
                                    indent( nest+2 )
                                 )
                  ) < 0
               )
               goto finish;
         }

         /* emit the structure member definitions */
         for (i = 0; i < t->data AS list.count; i++)
         {
            if (i == t->data AS list.count - 1 &&
                  t->tag == def_TYPE_STRUCT &&
                  t->data AS list.ellipsis)
            {
               /* repeated member of a structure containing ellipsis */
               char v1 [def_ID_LIMIT + 1];

               if ( ( rc = sprintf( v1,
                                    ( t->data AS list.members [i]->name [0] == '*' )?
                                          "(%s) [%s]" : "%s [%s]",
                                    t->data AS list.members [i]->name,
                                    !var? "...": "N"
                                  )
                     ) < 0
                  )
                  goto finish;

               Print_Decl(  title,
                            file,
                            t->data AS list.members [i],
                            NULL,
                            v1,
                            var,
                            nest+1
                         );
            }
            else
               /* normal member */
               Print_Decl( title,
                           file,
                           t->data AS list.members [i],
                           NULL,
                           t->data AS list.members [i]->name,
                           var,
                           nest+1
                         );

            if ( ( rc = fprintf( file,
                                 ";%s\n%s",
                                 var? " \\\\" : "",
                                 indent( (i == t->data AS list.count - 1)? nest + 1 : nest + 2 )
                               )
                 ) < 0
               ) goto finish;
         }

         /* closing brace & struct name */
         if ( ( rc = fprintf( file,
#if 1 /* puts struct name on next line after brace */
                              "}%s\n%s%s",
                              var? " \\\\" : "",
                              indent(nest+1),
                              v
#else
                              "}\t%s",
                              v
#endif
                            )
              ) < 0
            ) goto finish;
      }
      break;  /* end case data structure/union */

      case def_TYPE_ROW:
      {
         char v1 [def_ID_LIMIT + 1];

         if (v == NULL || v [0] == '/')
            if (v != NULL)
            {
               if ((rc = sprintf (v1, "%s [%d]", v,
                     t->data AS row.count)) < 0)
                  goto finish;
            }
            else
            {
               if ((rc = sprintf (v1, "[%d]", t->data AS row.count)) < 0)
                  goto finish;
            }
         else
            if ((rc = sprintf (v1, v [0] == '*' ? "(%s) [%d]": "%s [%d]", v,
                  t->data AS row.count)) < 0)
               goto finish;

         Print_Decl( title,
                     file,
                     t->data AS row.base,
                     NULL,
                     v1,
                     FALSE,
                     nest + 1
                   );
      }
      break;

      case def_TYPE_VOID:
         if ((rc = fprintf (file, v == NULL? "void": "void %s", v)) < 0)
            goto finish;
      break;

      case def_TYPE_ID:
      {
         char c_name [def_ID_LIMIT + 1], *ul;

         def_as_extern (c_name, t->data AS id);

         if (strncmp (c_name, title, strlen (title)) == 0)
         {
            /*Type name is of the form <title><other stuff>, so this will
               definitely work. Note that we don't require an underscore -
               this means that type names of the form <title><prefix>_<name>
               are assumed to be defined locally.*/
            if ((rc = fprintf (file, v == NULL? "<%s>": "<%s> %s", c_name, v)) < 0)
               goto finish;
         }
         else if ((ul = strchr (c_name, '_')) != NULL)
         {
            /*Type name is not one of ours, but it has an underscore in it:
               assume it comes from a module called whatever its prefix is.*/
            if ( ( rc = fprintf( file,
                                 ( v == NULL )?
                                    "<%s=>%s>" :
                                    "<%s=>%s> %s",
                                 c_name,
                                 c_name,
                                 v
                               )
                  ) < 0
               ) goto finish;
         }
         else
         {
            /*We have no idea. It's probably 'void'.*/
            if ((rc = fprintf (file, v == NULL? "%s": "%s %s", c_name, v)) <
                  0)
               goto finish;
         }
      }
      break;
   }

finish:
   if( rc < 0 )
      x_THROW_LAST_ERROR();
}


/****************************************************************/
/* emit the description of a structure to file help_data
**    returns 0
**    throws exception on error
*/
static int print_struct_desc( FILE* help_data, def_t t )
{
   int rc = 0;

   if (t != NULL)
   {
      if (t->description != NULL)
      {
         char temp[ DESCRIPTION_MAX ];
         char *p1, *p2;

         for ( p1 = t->description, p2 = temp;
               *p1 != '\0' && p2 - temp < DESCRIPTION_MAX - 2;
               p1++
             )
         {
            switch( *p1 )
            {
            case '{':
               /* escape this char */
               *p2++ = '\\';
               *p2++ = *p1;
               break;

            default:
               *p2++ = *p1;
               break;
            }
         }
         *p2 = '\0';

         if ( fprintf( help_data,
                       "\n#Wrap On\n"
                       "Description:\t%s\n"
                       "#Wrap Off",
                       temp
                     ) < 0
            ) x_THROW_LAST_ERROR();
      }

      if (t->tag == def_TYPE_STRUCT || t->tag == def_TYPE_UNION)
      {
         osbool first = TRUE;
         int i;

         /* print the field descriptions */
         for ( i = 0; i < t->data AS list.count; i++ )
            if ( t->data AS list.members [i]->description != NULL )
            {
               if ( fprintf(  help_data,
                              "\n%s%s - %s",
                              first? "Fields:\t": " \t",
                              t->data AS list.members [i]->name,
                              t->data AS list.members [i]->description
                           ) < 0
                  ) x_THROW_LAST_ERROR();

               first = FALSE;
            }
      }
   }

    return rc;
}


/****************************************************************/
/* Emit the list of constants associated with a type
**
** Throws exception on error
*/
static void print_const_list( FILE* help_data,
                              const lookup_t consts,
                              const def_t t,
                              const char* type
                            )
{
   void *context;
   osbool first = TRUE;
   char* cnst;
   def_c c;
   os_error* error;

   context = NULL;
   while (TRUE)
   {
      if ( ( error = lookup_enumerate( consts, &cnst, (void **) &c, &context ))
            != NULL)
         x_THROW( error );

      if (context == 0) break;

      /*loop invariant: we are at the end of the previous line*/

      if (  c->type->tag == def_TYPE_ID &&
            strcmp( c->type->data AS id, type ) == 0
         )
      {
         char c_name [def_ID_LIMIT + 1];

         if ( fprintf(  help_data,
                        first ?  "\nValues:\t":
                                 "\n\t"
                     ) <0
            ) x_THROW_LAST_ERROR();
            first = FALSE;

         def_as_macro( c_name, cnst );
         if ( fprintf( help_data, "<%s>", c_name ) < 0 )
            x_THROW_LAST_ERROR();
      }
   }

}


/****************************************************************/
/* emit a a type declaration
** throws exception on error
*/
static void emit_type(  const char* path,
                        const char* c_title,
                        const char* type,
                        const lookup_t consts,
                        const def_t t
                     )
{
   FILE* help_data;
   x_exception x0, x1;
   int i;
   char c_name[def_ID_LIMIT + 1];   /* C-style name of object */

   /* format the type name as ModuleName_foo_bar */
   def_as_extern (c_name, type);
   tracef( "found %s\n" _ c_name );

   /* test for abstract structure */
   if (t == NULL || t->tag == def_TYPE_ABSTRACT)
   {
      /* create/open the page */
      help_data = open_page( path, c_name, c_title );

      x_TRY( &x0 )
      {
         if ( fprintf(  help_data,
                        "Declaration:%stypedef struct \\{...} *%s;",
                        indent(1),
                        c_name
                     ) < 0
            ) x_THROW_LAST_ERROR();

         /* print structure description, if available */
         print_struct_desc( help_data, t );

         /*Additionally supply a list of constants with values of that type.*/
         print_const_list( help_data, consts, t, type );

         fclose( help_data );
         help_data = NULL;

         /* add this entry to the index */
         index_entry( c_name );
      }
         x_CATCH( &x0 )
      {
         fclose( help_data );
         help_data = NULL;
         x_RETHROW( &x0 );
      }
   } /* end abstract structure */

   else
   {
      /* structure definition */
      char base [def_ID_LIMIT + 1];

      if (  t->tag == def_TYPE_STRUCT &&
            t->data AS list.ellipsis &&
            t->data AS list.count > 1
         )
      {
         /* we have a variable-size structure, which is defined using a macro */
         def_t def;

         /* emit the macro */
         def_as_macro( base,  type );
         strcat( base, "_MEMBERS" );

         /* create/open the page */
         help_data = open_page( path, base, c_title );

         x_TRY( &x0 )
         {
            if ( fprintf(  help_data,
                           "Declaration:%s#define\t%s",
                           indent(1), base
                        ) < 0
               )  x_THROW_LAST_ERROR();

            if ( t->data AS list.base )
            {
               /* the structure is based upon another; print the base name */
               char base [def_ID_LIMIT + 1];

               def_as_macro( base,  t->data AS list.base->data AS id );
               if ( fprintf( help_data,
                             " \\\\\n%s%s_MEMBERS",
                             indent(2),
                             base
                           ) < 0
                  ) x_THROW_LAST_ERROR();
            }

            /* emit each structure member */
            for ( i = 0; i < t->data AS list.count - 1; i++ )
            {
               if ( fprintf( help_data,
                             " \\\\\n%s",
                             indent(2)
                           ) < 0
                  ) x_THROW_LAST_ERROR();

               Print_Decl( c_title,
                           help_data,
                           t->data AS list.members [i],
                           NULL,
                           t->data AS list.members [i]->name,
                           TRUE,
                           0
                         );

               if ( fprintf( help_data, ";" ) < 0 )
                  x_THROW_LAST_ERROR();
            }

            fclose( help_data );
            help_data = NULL;

            /* add this entry to the index */
            index_entry( base );
         }
         x_CATCH( &x0 )
         {
            fclose( help_data );
            help_data = NULL;
            x_RETHROW( &x0 );
         }

         /* Make up a new structure consisting of just the base */
         def = x_ALLOC( def_sizeof_TYPE( def_TYPE_STRUCT ) );
         memcpy( def, t, def_sizeof_TYPE( def_TYPE_STRUCT ) );

         def -> data AS list.count -= 1;
         def -> data AS list.ellipsis = FALSE;
         strcpy( base, type );
         strcat( base, "Base" );
         def_as_extern( c_name, base );

         x_TRY( &x0 )
         {
            help_data = open_page( path, c_name, c_title );

            x_TRY( &x1 )
            {
               if ( fprintf(  help_data,
                              "Declaration:%stypedef ",
                              indent(1)
                           ) < 0
                  ) x_THROW_LAST_ERROR();

               /* print the declaration */
               Print_Decl( c_title, help_data, def, NULL, c_name, FALSE, 0 );

               /* final semi-colon */
               if ( fprintf (help_data, ";") < 0 )
                  x_THROW_LAST_ERROR();

               /* print structure description, if available */
               print_struct_desc(  help_data, def );

               /*Additionally supply a list of constants with values of that type.*/
               print_const_list( help_data, consts, def, type );

               fclose( help_data );
               help_data = NULL;

               /* add this entry to the index */
               index_entry ( c_name);

               free( def );
            }
            x_CATCH( &x1 )
            {
               fclose( help_data );
               help_data = NULL;
               x_RETHROW( &x1 );
            }
         }
         x_CATCH( &x0 )
         {
            free( def );
            x_RETHROW( &x0 );
         }
      }

      /* finally emit the declared type definition */
      def_as_extern( c_name, type );

      /* open the page */
      help_data = open_page( path, c_name, c_title );

      x_TRY( &x0 )
      {
         if ( fprintf(  help_data,
                        "Declaration:%stypedef ",
                        indent(1)
                     ) < 0
            ) x_THROW_LAST_ERROR();

         /* print the declaration */
         Print_Decl( c_title, help_data, t, NULL, c_name, FALSE, 0);

         /* final semi-colon */
         if ( fprintf (help_data, ";") < 0)
             x_THROW_LAST_ERROR();

         /* print structure description, if available */
         print_struct_desc(  help_data, t );

         /*Additionally supply a list of constants with values of that type.*/
         print_const_list( help_data, consts, t, type );

         fclose( help_data );
         help_data = NULL;

         /* add this entry to the index */
         index_entry ( c_name);
      }
      x_CATCH( &x0 )
      {
         fclose( help_data );
         help_data = NULL;
         x_RETHROW( &x0 );
      }
   }
}

/****************************************************************/
/* main entry */
os_error *cstrong_output
(
   char     *output,    /* path name to contain output */
   char     *title,     /* module title */
   char     *author,    /* unused */
   lookup_t  needses,   /* unused */
   lookup_t  consts,
   lookup_t  types,
   lookup_t  swis
)
{
   os_error *error = NULL;
   char     *cnst,
            *type,
            *swi;
   def_c     c;
   def_t     t;
   def_s     s;
   char      c_name [def_ID_LIMIT + 1],   /* C-style name of current object */
             c_title [def_ID_LIMIT + 1],  /* C-style name of module title */
/*             data_name [FILENAME_MAX + 1], */
/*             index_name [FILENAME_MAX + 1], */
             path[ FILENAME_MAX + 1 ],    /* path name for destination */
             temp_name [FILENAME_MAX + 1 ];  /* temp workspace */
   void     *context;
   int       rc = 0, i;
   FILE     /* *index = NULL, */
            *help_data = NULL;
   x_exception x0;

   NOT_USED (author)
   NOT_USED (needses)

   x_TRY( &x0 )
   {
      /* get the destination path name */
      path[0] = '\0';
   //   strncat( path, output, strrchr( output, '.' ) - output );
      strcpy( path, output );

      def_as_extern (c_title, title);

      /*Emit the TYPE declarations.*/
      context = 0;

      while (TRUE)
      {
         /* step through the TYPES table */
         if ((error = lookup_enumerate (types, &type, (void **) &t, &context))
               != NULL)
            goto finish;
         if (context == 0) break;  /* end of table */

         emit_type( path, c_title, type, consts, t );
      }  /* end type declarations */

      /* Emit the types index page.*/
      if ( ( rc = index_file( path, title, STR( TYPES ) ) ) < 0 )
         goto finish;


      /*Emit the CONST declarations.*/
      context = 0;

      while (TRUE)
      {
         if ((error = lookup_enumerate (consts, &cnst, (void **) &c, &context))
               != NULL)
            goto finish;
         if (context == 0) break;

         def_as_macro (c_name, cnst);

         /* open/create the page */
         help_data = open_page( path, c_name, c_title );

         /* write the page */
         if ( ( rc = fprintf( help_data,
                              "Declaration:%s#define\t%s\t",
                              indent(1),
                              c_name
                            )
               ) < 0
            ) goto finish;

         switch (c->type->tag)
         {
            case def_TYPE_INT:
               if ( ( rc = fprintf( help_data,
                                    "%s%d%s",
                                    c->value < 0? "(": "",
                                    c->value,
                                    c->value < 0? ")": ""
                                  )
                    ) < 0
                  ) goto finish;
            break;

            case def_TYPE_SHORT:
               if ((rc = fprintf (help_data, "((short) %d)", c->value)) < 0)
                  goto finish;
            break;

            case def_TYPE_BYTE:
               if ((rc = fprintf (help_data, "((byte) %d)", c->value)) < 0)
                  goto finish;
            break;

            case def_TYPE_CHAR:
               if (isprint (c->value))
               {
                  if ((rc = fprintf (help_data, "((char) '%c')", c->value)) < 0)
                     goto finish;
               }
               else
               {
                  if ((rc = fprintf (help_data, "((char) '\\\\x%.2X')", c->value))
                        < 0)
                     goto finish;
               }
            break;

            case def_TYPE_BITS:
               if ((rc = fprintf (help_data, "0x%Xu", c->value)) < 0)
                  goto finish;
            break;

            case def_TYPE_BYTES:
               if ((rc = fprintf (help_data, "((bytes) 0x%Xu)", c->value)) < 0)
                  goto finish;
            break;

            case def_TYPE_BOOL:
               if ((rc = fprintf (help_data, "%s", WHETHER (c->value))) < 0)
                  goto finish;
            break;

            default:
               if ((rc = fprintf (help_data, "((")) < 0)
                  goto finish;

               Print_Decl(c_title, help_data, c->type, NULL, NULL, FALSE, 0);

               if ((rc = fprintf (help_data, ") 0x%Xu)", c->value)) < 0)
                     /*was %d 19th Apr 1995*/
                  goto finish;
            break;
         }

         if (c->description != NULL)
            if ( ( rc = fprintf( help_data,
                                 "\n#Tab ,fstd"
                                 "\n#Wrap On"
                                 "\nDescription:%s%s"
                                 "\n#Wrap Off"
                                 "\n#Tab ,fcode ,fcode ,fcode ,fcode",
                                 indent(1),
                                 c->description
                               )
                 ) < 0
               ) goto finish;

         index_entry ( c_name);

         fclose( help_data );
         help_data = NULL;
      }  // end of const list

      /* Emit the const index page.*/
      if ( ( rc = index_file( path, title, STR( CONSTS ) ) ) < 0 )
         goto finish;



      /*Emit the SWI declarations.*/
      context = 0;

      while (TRUE)
      {
         osbool nonx, first;

         if ((error = lookup_enumerate (swis, &swi, (void **) &s, &context)) !=
               NULL)
            goto finish;
         if (context == 0) break;

         if ( s->absent )
         {
            /* SWI declared absent */
            for (nonx = FALSE; nonx <= TRUE; nonx++)
            {
               def_as_extern( c_name + ( ( !nonx )? 1: 0 ), swi );
               if (!nonx) c_name [0] = 'x';
               tracef ("SWI Absent starting %s ...\n" _ c_name);

               /* create/open the output file */
               help_data = open_page( path, c_name, c_title );

               if ( s->description != NULL )
               {
                  /* print description of absent SWI */
                  if ( ( rc = fprintf( help_data,
                                       "#Tab ,fstd"
                                       "\n#Wrap On"
                                       "\nDescription:\t%s"
                                       "\n#Wrap Off"
                                       "\n#Tab ,fcode ,fcode ,fcode ,fcode",
                                       s->description
                                     )
                        ) < 0
                     ) goto finish;
               }
               else
               {
                  /* no description for absent SWI */
                  if ( ( rc = fprintf( help_data,
                                       "\n#Tab ,fcode ,fstd"
                                       "\nDescription:\tSWI 0x%X\t- not directly callable"
                                       "\n#Tab ,fcode ,fcode ,fcode ,fcode",
                                       s->swi
                                     )
                       ) < 0
                     ) goto finish;
               }

               fclose( help_data );
               help_data = NULL;

               index_entry( c_name);
            }
         }
         else
         {
            int result = s->value != NONE? def_bit_index (s->value, 0): -1;
               /*number of register returned as result*/

            for (nonx = FALSE; nonx <= TRUE; nonx++)
            {
               def_as_extern (c_name + (!nonx? 1: 0), swi);
               if (!nonx) c_name [0] = 'x';
               tracef ("SWI starting %s ...\n" _ c_name);

               /* create/open the output file */
               help_data = open_page( path, c_name, c_title );

               first = TRUE;

               if ( ( rc = fprintf( help_data,
                                    "Declaration:%sextern ",
                                    indent(1)
                                  )
                    ) < 0
                  ) goto finish;

               /* emit return value */
               if (!nonx)
               {
                  /* x-SWI - print return value */
                  if ( ( rc = fprintf( help_data,
                                       "<os_error=>os_error>\t*%s\n",
                                       c_name
                                     )
                       ) < 0
                     ) goto finish;
               }
               else
               {
                  /* non-x - print return value */
                  if (s->value == NONE)
                  {
                     /*No return value.*/
                     if ( ( rc = fprintf( help_data,
                                          "void%s%s\n",
                                          indent(1),
                                          c_name
                                        )
                          ) < 0
                        ) goto finish;
                  }
                  else if (s->value == 1 << def_FLAGS)
                  {
                     /*Return value is |bits|.*/
                     if ( ( rc = fprintf( help_data,
                                          "bits\t%s\n",
                                          c_name
                                        )
                          ) < 0
                        ) goto finish;
                  }
                  else
                  {
                     /*Return value of type |s->outputs [r]|.*/
                     char decl_name [def_ID_LIMIT + 1];

                     /*Prepend a star if the return value is a REFERENCES.*/
                     sprintf( decl_name,
                              (s->ro & s->value) != NONE? "\t*%s": "\t%s",
                              c_name
                            );

                     Print_Decl( c_title,
                                 help_data,
                                 s->outputs [result],
                                 NULL,
                                 decl_name,
                                 FALSE,
                                 0
                               );

                     if ( ( rc = fprintf( help_data,
                                          "\n"
                                        )
                          ) < 0
                        ) goto finish;
                  }
               }

               /* emit the parameter list */
               if (!def_using_block (s))
               {
                  for (i = 0; i < 10; i++)
                     if ((s->i & 1 << i) != 0)
                     {
                        char arg_name [def_ID_LIMIT + 1];

                        if ( ( rc = fprintf( help_data,
                                             "%s%s",
                                             first ? "" : ",\n",
                                             first ? " \t \t(\t" : indent(3)
                                           )
                             ) < 0
                           ) goto finish;
                        first = FALSE;

                        if ((s->ri & 1 << i) == 0)
                        {
                           if ( ( rc = sprintf( arg_name,
                                                "%s",
                                                s->inputs [i]->name
                                              )
                                ) < 0
                              ) goto finish;
                        }
                        else
                        {
                           if ((rc = sprintf (arg_name, "const *%s",
                                 s->inputs [i]->name)) < 0)
                              goto finish;
                        }

                        Print_Decl( c_title,
                                    help_data,
                                    s->inputs [i],
                                    NULL,
                                    arg_name,
                                    FALSE,
                                    0
                                  );
                     }

                  for (i = 0; i < 10; i++)
                     if ((s->o & 1 << i) != 0 && (!nonx || i != result))
                     {
                        char arg_name [def_ID_LIMIT + 1];

                        if ( ( rc = fprintf( help_data,
                                             "%s%s",
                                             first ? "" : ",\n",
                                             first ? " \t \t(\t" : indent(3)
                                           )
                             ) < 0
                           ) goto finish;
                        first = FALSE;

                        if ((s->ro & 1 << i) == 0)
                        {
                           if ((rc = sprintf (arg_name, "*%s",
                                 s->outputs [i]->name)) < 0)
                              goto finish;
                        }
                        else
                        {
                           if ((rc = sprintf (arg_name, "**%s",
                                 s->outputs [i]->name)) < 0)
                              goto finish;
                        }

                        Print_Decl( c_title,
                                    help_data,
                                    s->outputs [i],
                                    NULL,
                                    arg_name,
                                    FALSE,
                                    0
                                  );
                     }

                  if (s->f_out && (!nonx || def_FLAGS != result))
                  {
                     struct def_t t;

                     if ( ( rc = fprintf( help_data,
                                          "%s%s",
                                          first ? "" : ",\n",
                                          first ? " \t \t(\t " : indent(3)
                                        )
                          ) < 0
                        ) goto finish;
                     first = FALSE;

                     t.tag = def_TYPE_BITS;
                     Print_Decl( c_title,
                                 help_data,
                                 &t,
                                 NULL,
                                 "*psr",
                                 FALSE,
                                 0
                               );
                  }
               }
               else
               {
                  /*First locate the register pointing to the block.*/
                  for (i = 0; i < 10; i++)
                     if ((s->i & 1 << i) != 0)
                     {
                        int cpt;

                        for (cpt = 0; cpt < s->inputs [i]->data AS list.count; cpt++)
                        {
                           if ( ( rc = fprintf( help_data,
                                                "%s%s%s%s",
                                                first ? "" : ",\n",
                                                indent(2),
                                                first ? "(" : "",
                                                indent(1)
                                              )
                                ) < 0
                              ) goto finish;
                           first = FALSE;

                           Print_Decl( c_title,
                                       help_data,
                                       s->inputs [i]->data AS list.members [cpt],
                                       NULL,
                                       s->inputs [i]->data AS list.members [cpt]->name,
                                       FALSE,
                                       0
                                     );
                        }

                        break;
                     }
               }

               /* closing brace and semi-colon */
               if ( ( rc = fprintf( help_data,
                                    "%s%s%s%s",
                                    indent(2),
                                    first ? "(\tvoid\n" : "\n",
                                    indent(2),
                                    ");"
                                  )
                    ) < 0
                  ) goto finish;

               tracef ("describing %s ...\n" _  c_name);
               if (s->description != NULL)
                  if ( ( rc = fprintf( help_data,
                                       "\n#Tab ,fstd"
                                       "\n#Wrap On"
                                       "\nDescription:\t%s"
                                       "\n#Wrap Off"
                                       "\n#Tab ,fcode ,fcode ,fcode ,fcode",
                                       s->description
                                     )
                       ) < 0
                     ) goto finish;

               if (!def_using_block (s))
               {
                  osbool first;
                  int  i;

                  tracef ("inputs ...\n");
                  first = TRUE;
                  /* print the entry conditions */
                  for (i = 0; i < 10; i++)
                     if ((s->i & 1 << i) != NONE)
                     {
                        if ( ( rc = fprintf( help_data,
                                             "\n%s%s",
                                             first ? /*"#Tab ,fcode ,fcode ,fcode ,fcode"*/
                                                     "\nEntry:"
                                                   : "",
                                             indent(1)
                                           )
                             ) < 0
                           ) goto finish;

                        if ( ( rc = fprintf( help_data,
                                             "%s",
                                             s->inputs [i]->name
                                           )
                             ) < 0
                           ) goto finish;

                        if (s->inputs [i]->description != NULL)
                           if ((rc = fprintf (help_data, " - %s",
                                 s->inputs [i]->description)) < 0)
                              goto finish;

                        if ((rc = fprintf (help_data, "\t(R%d)", i)) < 0)
                           goto finish;

                        first = FALSE;
                     }

                  tracef ("outputs ...\n");
                  first = TRUE;
                  for (i = 0; i < 10; i++)
                     if ((s->o & 1 << i) != NONE && (i != result || !nonx))
                     {
                        if ( ( rc = fprintf( help_data,
                                             "\n%s%s",
                                             first ?  /*"#Tab ,fcode ,fcode ,fcode ,fcode\n"*/
                                                      "Exit:"
                                                   :  "",
                                             indent(1)
                                           )
                             ) < 0
                           )
                           goto finish;

                        if ((rc = fprintf (help_data, "%s",
                              s->outputs [i]->name)) < 0)
                           goto finish;

                        if (s->outputs [i]->description != NULL)
                           if ((rc = fprintf (help_data, " - %s",
                                 s->outputs [i]->description)) < 0)
                              goto finish;

                        if ((rc = fprintf (help_data, "\t(R%d)", i)) < 0)
                           goto finish;

                        first = FALSE;
                     }

                  tracef ("returns ...\n");
                  if (nonx && result != -1)
                  {
                     if (result == def_FLAGS)
                     {
                        if ( ( rc = fprintf( help_data,
                                             /*"\n#Tab ,fcode ,fcode ,fcode ,fcode"*/
                                             "\n%s\t%s",
                                             "Returns:",
                                             "processor status register"
                                           )
                             ) < 0
                           ) goto finish;
                     }
                     else
                     {
                        if ( ( rc = fprintf( help_data,
                                             /*"\n#Tab ,fcode ,fcode ,fcode ,fcode"*/
                                             "\n%s\t%s\t(R%d)",
                                             "Returns:",
                                             ( s->outputs [result]->description != NULL )?
                                                   s->outputs [result]->description:
                                                   s->outputs [result]->name,
                                             result
                                           )
                             ) < 0
                           ) goto finish;
                     }
                  }
               }
               fclose( help_data );
               help_data = NULL;

               index_entry (c_name);
            }
         }
      }

      /* write the SWI index to file */
      if ( ( rc = index_file( path, title, STR( SWIS ) ) ) < 0 )
         goto finish;


      /* create the root index.*/
      sprintf( temp_name, "Constants=>%s" SEP STR(CONSTS), title );
      index_entry( temp_name );
      sprintf( temp_name, "Types=>%s" SEP STR(TYPES), title );
      index_entry( temp_name );
      sprintf( temp_name, "SWIs=>%s" SEP STR(SWIS), title );
      index_entry( temp_name );

      /* write the index to file */
      if ( ( rc = index_file( path, title, NULL ) ) < 0 )
         goto finish;
   }
   x_CATCH( &x0 )
   {
      rc = 0;
      error = x0.error;
   }


finish:
   if (help_data != NULL)
      fclose (help_data);

//   if (index != NULL)
//      fclose (index);

   if (rc < 0)
      error = (os_error*)_kernel_last_oserror ();

   if( error )
      fprintf( stderr, "DefMod CStrong: %d %s", error -> errnum, error -> errmess );

   return error;
}



