/*chelp.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.
*/

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <kernel.h>      /* TV 980115 */

#include "oslib/macros.h"
#include "oslib/os.h"

#include "lookup.h"

#include "def.h"
#include "chelp.h"

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

static int Print_Decl
(
   FILE  *file,
   def_t  t,
   char  *tag,
   char  *v,
   osbool var,
   int    nest
)
{
   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;
         }

         if ((rc = Print_Decl (file, t->data AS ref, NULL, v1, FALSE,
               nest + 1)) < 0)
            goto finish;
      }
      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
         (
            (
               rc = tag != NULL?
                     fprintf
                     (
                        file,
                        "%s %s\n%*s{  ",
                        t->tag == def_TYPE_STRUCT? "struct":
                        t->tag == def_TYPE_UNION?  "union": "list",
                        tag,
                        17 + 3*nest, " "
                     ):
                     fprintf
                     (
                        file,
                        "%s\n%*s{  ",
                        t->tag == def_TYPE_STRUCT? "struct":
                        t->tag == def_TYPE_UNION?  "union": "list",
                        17 + 3*nest, " "
                     )
            )
            < 0
         )
            goto finish;

         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)
            {
               char v1 [def_ID_LIMIT + 1];

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

               if ((rc = Print_Decl (file, t->data AS list.members [i],
                     NULL, v1, FALSE, nest + 1)) < 0)
                  goto finish;
            }
            else
               if ((rc = Print_Decl (file, t->data AS list.members [i],
                     NULL, t->data AS list.members [i]->name, FALSE,
                     nest + 1)) < 0)
                  goto finish;

            if ((rc = fprintf (file, ";\n%*s", 17 + 3*nest, " ")) < 0)
               goto finish;

            if (i != t->data AS list.count - 1)
               if ((rc = fprintf (file, "   ")) < 0)
                  goto finish;
         }

         if ((rc = fprintf (file, "}\n%*s%s", 17 + 3*nest, " ", v)) < 0)
            goto finish;
      }
      break;

      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, "(%s) [%d]", v,
                  t->data AS row.count)) < 0)
               goto finish;
         }

         if ((rc = Print_Decl (file, t->data AS row.base, NULL, v1, FALSE,
               nest + 1)) < 0)
            goto finish;
      }
      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];

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

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

finish:
   return rc;
}
/*-----------------------------------------------------------------------*/

os_error *chelp_output
(
   FILE     *file,
   char     *title,
   char     *author,
   lookup_t  needses,
   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_title [def_ID_LIMIT + 1];
   void     *context;
   int       i, rc = 0;

   NOT_USED (needses)
   NOT_USED (author)
   def_as_extern (c_title, title);

   /*Emit the TYPE declarations.*/
   context = 0;
   while (TRUE)
   {
      if ((error = lookup_enumerate (types, &type, (void **) &t, &context))
            != NULL)
         goto finish;
      if (context == 0) break;

      def_as_extern (c_name, type);

      if (t != NULL)
      {
         if ((rc = fprintf (file,
               "%%%s\n"
               "Defined in:   oslib/%s.h\n"
               "Declaration:  typedef%s", c_name,
               c_title, t->tag == def_TYPE_STRUCT ||
               t->tag == def_TYPE_UNION? "\n                 ": " ")) < 0)
            goto finish;

         if ((rc = Print_Decl (file, t, NULL, c_name, FALSE, 0)) < 0)
            goto finish;

         if ((rc = fprintf (file, ";\n\n")) < 0)
            goto finish;
      }
      else
      {
         if ((rc = fprintf (file,
               "%%%s\n"
               "Defined in:   oslib/%s.h\n"
               "Declaration:  typedef struct {...} *%s;\n\n",
               c_name, c_title, c_name)) < 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);

      if ((rc = fprintf
            (  file,
               "%%%s\n"
               "Defined in:   oslib/%s.h\n"
               "Declaration:  #define %s ",
               c_name, c_title, c_name)) < 0)
         goto finish;

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

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

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

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

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

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

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

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

            if ((rc = Print_Decl (file, c->type, NULL, NULL, FALSE,
                  0)) < 0)
               goto finish;

            if ((rc = fprintf (file, ") %d)", c->value)) < 0)
               goto finish;
         break;
      }

      if ((rc = fprintf (file, "\n\n")) < 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)
      {
         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';

            first = TRUE;

            if ((rc = fprintf (file,
                  "%%%s\n"
                  "Defined in:   oslib/%s.h\n"
                  "Declaration:  extern ",
                  c_name, c_title)) < 0)
               goto finish;

            if (!nonx)
            {
               if ((rc = fprintf (file,
                     "os_error *%s (",
                     c_name)) < 0)
                  goto finish;
            }
            else
            {
               if (s->value == NONE)
               {
                  /*No return value.*/
                  if ((rc = fprintf (file, "void %s (",
                        c_name)) < 0)
                     goto finish;
               }
               else if (s->value == 1 << def_FLAGS)
               {
                  /*Return value is |bits|.*/
                  if ((rc = fprintf (file, "bits %s (",
                        c_name)) < 0)
                     goto finish;
               }
               else
               {
                  /*Return value of type |s->outputs [r]|.*/
                  if ((s->ro & s->value) != NONE)
                     /*Prepend a star.*/
                     memmove (c_name + 1, c_name, strlen (c_name) + 1),
                     c_name [0] = '*';

                  if ((rc = Print_Decl (file, s->outputs [result], NULL,
                        c_name, FALSE, 0)) < 0)
                     goto finish;

                  if ((rc = fprintf (file, " (")) < 0)
                     goto finish;
               }
            }

            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 (!first)
                     {
                        if ((rc = fprintf (file, ",\n                 ")) < 0)
                           goto finish;
                     }
                     else
                        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;
                     }

                     if ((rc = Print_Decl (file, s->inputs [i], NULL,
                           arg_name, FALSE, 0)) < 0)
                        goto finish;
                  }

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

                     if (!first)
                     {
                        if ((rc = fprintf (file, ",\n                 ")) < 0)
                           goto finish;
                     }
                     else
                        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;
                     }

                     if ((rc = Print_Decl (file, s->outputs [i], NULL,
                           arg_name, FALSE, 0)) < 0)
                        goto finish;
                  }

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

                  if (!first)
                  {
                     if ((rc = fprintf (file, ",\n                 ")) < 0)
                        goto finish;
                  }
                  else
                     first = FALSE;

                  t.tag = def_TYPE_BITS;
                  if ((rc = Print_Decl (file, &t, NULL, "*psr", FALSE, 0)) < 0)
                     goto finish;
               }
            }
            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 (!first)
                        {
                           if ((rc = fprintf (file, ",\n"
                                 "                 ")) < 0)
                              goto finish;
                        }
                        else
                           first = FALSE;

                        if ((rc = Print_Decl (file,
                              s->inputs [i]->data AS list.members [cpt], NULL,
                              s->inputs [i]->data AS list.members [cpt]->name,
                              FALSE, 0)) < 0)
                           goto finish;
                     }

                     break;
                  }
            }

            if ((rc = fprintf (file, first? "void);\n": ");\n")) < 0)
                  goto finish;

            if ((rc = fprintf (file, "Summary:      %s\n\n",
                  s->description?s->description:"no documentation available")) < 0)
               goto finish;
         }
      }
   }

finish:
   if (rc < 0) error = (os_error*)_kernel_last_oserror ();          /* TV 980115 */
   return error;
}
