/*                                                               -*- C -*-
 *  GENCODE.C
 *
 *  (c)Copyright 1995 by Tobias Ferber,  All Rights Reserved.
 *
 *  This file is part of ADOC.
 *
 *  ADOC 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 of the License,
 *  or (at your option) any later version.
 *
 *  ADOC 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 program; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* $VER: $Id: gencode.c,v 1.10 1995/03/20 18:44:19 tf Exp $ */

#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>

#include "libfun.h"
#include "adoc.h"


/* convert tabs in `s' to spaces with a tab step of `ts' */

static void fexpand(FILE *fp, int ts, char *s)
{
  int column= 0;

  while(*s)
  {
    switch(*s)
    {
      case '\n':
        fputc(*s,fp);
        column= 0;
        break;

      case '\t':
        do { fputc(' ',fp); } while( (++column) % ts);
        break;

     default:
       fputc(*s,fp);
       ++column;
       break;
    }
    ++s;
  }
}


/*
**  AUTODOCS
*/

int gen_autodoc_toc(FILE *fp)
{
  char *fun;

  fprintf(fp,"TABLE OF CONTENTS\n\n");

  for(fun= stepfun(0); fun; fun= stepfun(1))
    fprintf(fp,"%s\n",fun);

  return 0;
}


int gen_autodoc(FILE *fp, int cols, int tabsize, int flags, char **body_macros)
{
  int err= 0;

  char *fun, *sec, *text;

  for(fun= stepfun(0); fun && (err==0); fun= stepfun(1))
  {
    if(flags & ADOC_FORM_FEEDS)
      fputc('\f',fp);

    fprintf(fp,"%s%*s\n\n",fun,(int)(cols-strlen(fun)),fun);

    for(sec= stepsec(0); sec && (err==0); sec= stepsec(1))
    {
      /* indent the section heading with 3 spaces */
      fprintf(fp,"   %s\n",sec);

      if( (text= getsec(sec)) )
      {
        if(text && *text)
        {
          char *x= strexpand(text, body_macros);

          if(x)
          {
            if(tabsize > 0)
              fexpand(fp,tabsize,x);

            else
              fputs(x,fp);

            free(x);
          }
          else err= __LINE__;
        }

        fputc('\n',fp);
      }
    }
  }

  return err;
}


/*
**  TEXINFO
*/


/* split `fun' into `chapter' and `section'.
   the string returned by this function has to be disposed via free() by the caller */

static char *chapsec(char *fun, char **chapter, char **section)
{
  char *c= strdup(fun);
  char *s= (char *)0;

  if(c)
  {
    if( (s= strchr(c,'/')) )
      *s++= '\0';
  }

  *chapter= c;
  *section= s;

  return c;
}

/*
 *  The implementation of references in Texinfo perfectly silly :'(
 *  Instead of offering one simple reference macro, Texinfo comes up
 *  with context-sensitive @ref{}, @xref{}, @pref{}, ... and prints
 *  a `See ...' and `See also ...' right in front of the it...
 */

static int see_also(FILE *fp, char *refs, int flags)
{
  int err= 0;
  char *buf= strdup(refs);

#define isref(c) ( ('A'<=(c) && (c)<='Z') || \
                   ('a'<=(c) && (c)<='z') || \
                   ('0'<=(c) && (c)<='9') || ((c)=='_') || ((c)=='-') || ((c)=='/') || ((c)=='.') )

  if(buf)
  {
    /* make `s' the working pointer in `buf' */
    char *s= buf;

    /*
     *  former revisions of ADOC used to indent the parsed references
     *  by the same amount of white space as the first.  Actually we
     *  do not indent them amymore, so out it goes....
     */

    /* indentation string of the first reference */
#if 0
    char *indent= (char *)0;
#endif

    int num_refs= 0;

    while( *s && (err==0) )
    {
      char *l, *r;

      /* move `l' and `r' to the left and right end of a reference in `s' */

      for(l=s; *l && !isref(*l); l++) ;
      for(r=l; *r &&  isref(*r); r++) ;

      /* terminate the reference string with a '\0' */
      if(*r) *r++= '\0';

      /* save the indentation of the first reference */
#if 0
      if(num_refs == 0)
      {
        if( (indent= strdup(s)) )
        {
          char *t= indent;

          while(*t==' ' || *t=='\t')
            ++t;

          *t= '\0';
        }
        else err= __LINE__;
      }
#endif

      /* move `s' behind the reference */
      s= r;

      if( *l && (err==0) )
      {
        /* look for a function `l' and initialize `fun' to it's name */
        char *fun= (char *)0;

/*fprintf(stderr,"--> @ref{%s} ?\n",l);*/

        if( getfun(l) )
          fun= strdup(l);

        else if( !strchr(l,'/') )
        {
          /*
           *  Okay, we tried it the easy way but perhaps this is a reference
           *  into the library without the library name in front of it.
           *  Let's try appending `l' to the the current library name...
           */

          char *f= getfun( (char *)0 );

          if(f)
          {
            char *x, *xl, *xr;

            if( (x= chapsec(f, &xl, &xr)) )
            {
              size_t len= strlen(xl) + 1 + strlen(l) + 1;
              char *y= (char *)malloc( len * sizeof(char) );

              if(y)
              {
                sprintf(y,"%s/%s",xl,l);

/*fprintf(stderr,"--> @ref{%s} ?\n",y);*/

                if( getfun(y) )
                  fun= strdup(y);

                free(y);
              }
              else err= __LINE__;

              free(x);
            }
            else err= __LINE__;
          }
          else /* no current function? */
            err= __LINE__;
        }

        if(err == 0)
        {
          /* print the reference */

          if( fun )
          {
            char *cs, *chapter, *section;

            if( (cs= chapsec(fun, &chapter, &section)) )
            {
              if(flags & TEXI_ITEMIZE_REFERENCES)
              {
                if(num_refs==0)
                  fprintf(fp,"@itemize\n");

                fprintf(fp,"@item\n@xref{%s %s}.\n",chapter,section);
              }
              else /* not itemized */
              {
                if( (num_refs==0) && (flags & TEXI_GROUP_SECTIONS) )
                  fprintf(fp,"@group\n");

                fprintf(fp,"%s{%s %s},\n", (num_refs==0) ? "@*@xref"
                                                         : "@ref"
                                         , chapter
                                         , section
                );
              }

              free(cs);
            }
            else err= __LINE__;

            free(fun);
          }
          else /* !fun */
          {
            if(flags & TEXI_ITEMIZE_REFERENCES)
            {
              if(num_refs==0)
                fprintf(fp,"@itemize\n");

              fprintf(fp,"@item\nSee @file{%s}\n",l);
            }
            else /* not itemized */
            {
              if( (num_refs==0) && (flags & TEXI_GROUP_SECTIONS) )
                fprintf(fp,"@group\n");

              fprintf(fp,"%s{%s},\n",((num_refs==0) ? "@*See @file":"@file"),l);
            }
          }

          /* now at least one reference is printed */
          ++num_refs;
        }
      }
    }

    if(num_refs > 0)
    {
      if(flags & TEXI_ITEMIZE_REFERENCES)
        fprintf(fp,"@end itemize\n");

      else /* not itemized */
      {
        if(flags & TEXI_GROUP_SECTIONS)
          fprintf(fp,"@end group\n");

        fprintf(fp,"for more information.\n");
      }
    }

#if 0
    if(indent)
      free(indent);
#endif

    free(buf);
  }
  else err= __LINE__;

#undef isref

  return err;
}


int gen_texinfo_header(FILE *fp, char *fname, char **header_macros)
{
  int err= 0;

  char *default_header=
    "\\input texinfo  @c -*-texinfo-*-\n"
    "@comment %%**start of header\n"
    "@setfilename PROJECT.guide\n"
    "@settitle Autodocs for @code{PROJECT}\n"
    "@paragraphindent 0\n"
    "@iftex\n"
    "@afourpaper\n"
    "@headings double\n"
    "@finalout\n"
    "@setchapternewpage on\n"  /* odd */
    "@end iftex\n"
    "@comment %%**end of header\n\n"
    "@ifinfo\n"
    "@node Top\n"
    "@top\n"
    "This document describes @code{PROJECT} version EDITION.\n\n"  /* was @value{EDITION} */
    "@noindent Copyright @copyright{} COPYRIGHT\n"
    "@end ifinfo\n\n"
    "@titlepage\n"
    "@title PROJECT\n"
    "@subtitle Documentation taken from source code\n"
    "@subtitle Edition for Version EDITION\n"
    "@subtitle @today\n"
    "@author AUTHOR\n\n"
    "@page\n"
    "@vskip 0pt plus 1filll\n"
    "Copyright @copyright{} COPYRIGHT\n"
    "@end titlepage\n\n";

  if(fname && *fname)
  {
    FILE *fh= fopen(fname,"r");

    if(fh)
    {
      char *header= (char *)0;
      size_t header_size= 0;

      /* compute the size of the header file */

#ifdef BUGGY_FTELL

      do {

        (void)fgetc(fh);

        if(!feof(fh))
          ++header_size;

      } while(!feof(fh) || ferror(fh))

#else /* ftell() works fine */

      if( fseek(fh,0L,2L) >= 0) /* 2 == OFFSET_END */
        header_size= ftell(fh);

      else
        err= __LINE__;

#endif /* BUGGY_FTELL */

      if(!ferror(fh) && header_size > 0)
      {
        if(fseek(fh,-header_size,1L) < 0) /* 1 == OFFSET_CURRENT */
          err= __LINE__;
      }
      else err= __LINE__;


      /* load the header */

      if(err == 0)
      {
        if( (header= (char *)malloc( (header_size + 1) * sizeof(char) )) )
        {
          fread(header, sizeof(char), header_size, fh);
          header[header_size]= '\0';

          if( ferror(fh) )
          {
            free(header);
            header= (char *)0;
            err= __LINE__;
          }
        }
        else err= __LINE__;
      }

      if(err == 0)
      {
        char *x= strexpand(header, header_macros);

        if(x)
        {
          fputs(x,fp);
          free(x);
        }
        else /* out of memory */
          err= __LINE__;
      }

      fclose(fh);
    }
  }

  else /* !fname */
  {
    char *x= strexpand(default_header, header_macros);

    if(x)
    {
      fputs(x,fp);
      free(x);
    }
    else /* out of memory */
      err= __LINE__;
  }

  return err;
}



int gen_texinfo(FILE *fp, int tabsize, int flags, char *body_environment, char **body_macros)
{
  int err= 0;
  char *fun, *sec;

  /* main menu:
     create a @menu with an entry for each library.
     an additional entry will be created for the function index */

  if(err == 0)
  {
    char *last_chapter = (char *)0;  /* last printed chapter */
    char *last_cs      = (char *)0;  /* string allocated for `last_chapter' */

    fprintf(fp,"@menu\n");

    for(fun= stepfun(0); fun && (err==0); fun= stepfun(1))
    {
      char *cs, *chapter, *section;

      if( (cs= chapsec(fun, &chapter, &section)) )
      {
        if( !last_chapter || strcmp(last_chapter, chapter) )
        {
          fprintf(fp,"* %s::\n", chapter);

          if(last_cs)
            free(last_cs);

          last_cs      = cs;
          last_chapter = chapter;
        }
        else
          free(cs);
      }
      else /* out of memory */
        err= __LINE__;
    }

    if(last_cs)
      free(last_cs);

    fprintf(fp, "\n"
                "* Function Index::\n"
                "@end menu\n\n");
  }

#if 0

  /* create one single (flat) menu for functions mapped to
     @chapters.  This code is obsolete since we introduced
     the hierarchical layout with libraries mapped to
     @chapters and functions mapped to @sections. */

  if(err == 0)
  {
    fprintf(fp,"@menu\n");

    for(fun= stepfun(0); fun && (err==0); fun= stepfun(1))
    {
      char *cs, *chapter, *section;

      if( (cs= chapsec(fun, &chapter, &section)) )
      {
        fprintf(fp,"* %s %s::\n", chapter, section);
        free(cs);
      }
      else /* out of memory */
        err= __LINE__;
    }

    fprintf(fp, "\n"
                "* Function Index::\n"
                "@end menu\n\n");
  }

#endif

  /* chapters & sections */

  if(err == 0)
  {
    char *last_chapter = (char *)0;
    char *last_cs      = (char *)0;

    for(fun= stepfun(0); fun && (err==0); fun= stepfun(1))
    {
      char *cs, *chapter, *section;

      if( (cs= chapsec(fun, &chapter, &section)) )
      {
        if( !last_chapter || strcmp(last_chapter, chapter) )
        {
          /*
           *  Start a new library
           */

          fprintf(fp, "\n"
                      "@node %s\n"
                      "@chapter %s\n\n"
                      ,chapter
                      ,chapter
          );

          /* print a sub menu for the current chapter */

          pushfun();
          {
            int done;
            char *sub_fun;

            fprintf(fp,"@menu\n");
            for(sub_fun= fun, done= 0; sub_fun && (err==0) && !done; sub_fun= stepfun(1))
            {
              char *sub_cs, *sub_chapter, *sub_section;

              if( (sub_cs= chapsec(sub_fun, &sub_chapter, &sub_section)) )
              {
                if( (done= strcmp(sub_chapter, chapter)) == 0 )
                  fprintf(fp,"* %s %s::\n", sub_chapter, sub_section);

                free(sub_cs);
              }
              else err= __LINE__;
            }
            fprintf(fp,"@end menu\n\n");
          }
          popfun();

          if(last_cs)
            free(last_cs);

          last_cs      = cs;
          last_chapter = chapter;
        }

        else /* we didn't start a new page for the chapter */
        {
          if(flags & TEXI_FUNCTION_NEWPAGE)
            fprintf(fp,"@page\n");
        }

        /*
         *  Begin a new function 
         */

        fprintf(fp, "\n"
                    "@node %s %s\n"
                    "@section %s\n"
                    "@findex %s\n"
                    "\n"
                    ,chapter, section
                    ,section
                    ,section
        );

        if(flags & TEXI_TABLE_FUNCTIONS)
          fprintf(fp,"@table @b\n");

        for(sec= stepsec(0); sec && (err == 0); sec= stepsec(1))
        {
          char *text= getsec(sec);

          if(text && *text)
          {

            if(flags & TEXI_TABLE_FUNCTIONS)
              fprintf(fp,"@item %s\n",sec);
            else
             fprintf(fp,"@b{%s}\n",sec);  /* was @strong{} or @code{} */
             /*fprintf(fp,"@noindent\n@b{%s}\n",sec);*/


            if( (flags & TEXI_PARSE_REFERENCES) && strcmp(sec,"SEE ALSO") == 0 )
              err= see_also(fp, text, flags);

            else /* ! "SEE ALSO" */
            {
              char *x= strexpand(text,body_macros);

              if(x)
              {
                fprintf(fp,"@noindent\n"
                           "@%s\n",body_environment);

                if(flags & TEXI_GROUP_SECTIONS)
                  fprintf(fp,"@group\n");

                if(tabsize > 0)
                  fexpand(fp,tabsize,x);
                else
                  fputs(x,fp);

                if(flags & TEXI_GROUP_SECTIONS)
                  fprintf(fp,"@end group\n");

                fprintf(fp,"@end %s\n"
                           "@refill\n",body_environment);

                free(x);
              }
              else /* out of memory */
                err= __LINE__;
            }
          }
          /* else (!text) -> no error */

          fprintf(fp,"\n");
        }

        if(flags & TEXI_TABLE_FUNCTIONS)
          fprintf(fp,"@end table\n");
      }
      else /* out of memory */
        err= __LINE__;
    }

    if(last_cs)
      free(last_cs);
  }

  /* index */

  if(err == 0)
  {
    fprintf(fp, "@node Function Index\n"
                "@unnumbered Function Index\n"
                "@printindex fn\n"
                "\n"
                "@page\n"
                "@contents\n"
                "@bye\n");
  }

  return err;
}
