/*                                                               -*- C -*-
 *  LIBFUN.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: libfun.c,v 1.7 1995/03/20 18:44:15 tf Exp $ */

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

#include "libfun.h"

/* strexpand.c */
extern char *strexpand(char *, char **);

/* minimum #of chars added to funsec.data with each realloc() */
#define SECMINSIZE 1024

/* list of sections in a function description */

typedef struct funsec
{
  struct funsec *succ, *pred;
  char *name;          /* section title keyword */
  struct {
    char *text;        /* section text */
    size_t len;        /* strlen(text) */
    size_t size;       /* #of chars allocated for text */
  } data;

} funsec_t;


/* library function nodes */

typedef struct libfun
{
  struct libfun *succ, *pred;
  char *name;          /* name of the function: `library/function' */
  struct funsec *sec;  /* list of sections in the function description */

} libfun_t;


/* variables */

static libfun_t *funlist  = (libfun_t *)0;  /* list of all functions */
static libfun_t *funstack = (libfun_t *)0;  /* function on the stack */
static libfun_t *thisfun  = (libfun_t *)0;  /* the current function (the one constructed last) */
static funsec_t *thissec  = (funsec_t *)0;  /* the current section in the current function */

char *pushfun(void) { char *result= funstack ? funstack->name : (char *)0; funstack = thisfun;  return result; }
char *popfun(void)  { char *result= funstack ? funstack->name : (char *)0; thisfun  = funstack; return result; }

/**/

static libfun_t *libfun_head(libfun_t *lf) { return (lf && lf->pred) ? libfun_head(lf->pred) : lf; }
static libfun_t *libfun_tail(libfun_t *lf) { return (lf && lf->succ) ? libfun_tail(lf->succ) : lf; }

static funsec_t *funsec_head(funsec_t *fs) { return (fs && fs->pred) ? funsec_head(fs->pred) : fs; }
static funsec_t *funsec_tail(funsec_t *fs) { return (fs && fs->succ) ? funsec_tail(fs->succ) : fs; }

/*
**  NEWFUN
*/

static libfun_t *libfun_new(char *fun)
{
  libfun_t *lf= (libfun_t *)0;

  if(fun)
  {
    lf= (libfun_t *)malloc( sizeof(libfun_t) );

    if(lf)
    {
      lf->name= strdup(fun);

      if(lf->name)
      {
        lf->succ =
        lf->pred = (libfun_t *)0;
        lf->sec  = (funsec_t *)0;
      }
      else
      {
        free(lf);
        lf= (libfun_t *)0;
      }
    }
  }

  return lf;
}

/**/

int newfun(char *fun)
{
  libfun_t *new= libfun_new(fun);

  if(new)
  {
    libfun_t *lf= libfun_tail(funlist);

    if(lf)
    {
      lf->succ= new;
      new->pred= lf;
    }
    else funlist= new;
  }

  thisfun= new;
  thissec= new->sec;

  return new ? 0:1;
}

/*
**  FUNSORT
*/

static int funbubble(libfun_t *a)
{
  int result= 0;

  /* perform a recursive bubble-sort on `a' and it's successors
     return the #of bubbles */

  if(a && a->succ)
  {
    libfun_t *b= a->succ;

    if( strcmp(a->name, b->name) > 0 )
    {
      if( (b->pred= a->pred) )
        b->pred->succ= b;

      if( (a->succ= b->succ) )
        a->succ->pred= a;

      b->succ= a;
      a->pred= b;

      ++result;
    }

    result += funbubble(b);
  }

  return result;
}

/**/

void funsort(void)
{
  /* as long as there are bubbles... */
  while( funbubble(libfun_head(funlist)) );

  /* sorting might have made another libfun_t the head of funlist */
  funlist= libfun_head(funlist);

  for(thisfun= funlist; thisfun; thisfun= thisfun->succ)
  {
    /* make sure that thisfun->sec is the head of the sections */
    thisfun->sec= funsec_head(thisfun->sec);
  }

  thisfun= (libfun_t *)0;
  thissec= (funsec_t *)0;
}


/*
**  FUNEXPAND
*/

int funexpand(char **macros)
{
  int err= 0;

  libfun_t *lf;
  funsec_t *fs;

  for(lf= libfun_head(funlist); lf && (err==0); lf= lf->succ)
  {
    for(fs= funsec_head(lf->sec); fs && (err==0); fs= fs->succ)
    {
      if( fs->data.text )
      {
        char *x= strexpand(fs->data.text, macros);

        if(x)
        {
          free(fs->data.text);
          fs->data.text= x;
        }
        else err= 1;
      }
    }
  }

  return err;
}


/*
**  NEWSEC
*/

static funsec_t *funsec_new(char *title)
{
  funsec_t *fs= (funsec_t *)malloc( sizeof(funsec_t) );

  if(fs)
  {
    fs->name= (title) ? strdup(title) : (char *)0;

    if(title && !fs->name)
    {
      free(fs);
      fs= (funsec_t *)0;
    }

    else
    {
      fs->succ =
      fs->pred = (funsec_t *)0;

      fs->data.text = (char *)0;
      fs->data.len  =
      fs->data.size = 0;
    }
  }

  return fs;
}


int newsec(char *title)
{
  int err= 0;

  if(thisfun)
  {
    funsec_t *new= funsec_new(title);

    if(new)
    {
      funsec_t *fs= funsec_tail(thisfun->sec);

      if(fs)
      {
        fs->succ= new;
        new->pred= fs;
      }
      else thisfun->sec= new;
    }
    else err= 2;

    thissec= new;
  }
  else err= 1;

  return err;
}

/*
**  ADDTEXT
*/

int addtext(char *text)
{
  int err= 0;

  if(thissec)
  {
    if(text)
    {
      /* get the minimum needed size of the text block */
      size_t len= thissec->data.len + 1 + strlen(text) + 1;  /* '\n' + '\0' */

      /* see if we have enough room for '\n' and text */
      if(len >= thissec->data.size)
      {
        /* resize the text buffer to the needed size + SECMINSIZE chars */
        char *old= thissec->data.text;
        char *new= (char *)realloc(old, (SECMINSIZE + len) * sizeof(char) );

        if(new)
        {
          thissec->data.text = new;
          thissec->data.size = SECMINSIZE + len;

          if(!old)
            *new= '\0';
        }
        else /* !new */
        {
          free(thissec->data.text);
          thissec->data.text = (char *)0;
          thissec->data.len  =
          thissec->data.size = 0;
          err= 1;
        }
      }

      /* append the new `text' portion to the section text */
      if(thissec->data.text)
      {
        if(*thissec->data.text)
          strcat(thissec->data.text,"\n");

        if(*text)
          strcat(thissec->data.text,text);

        thissec->data.len= strlen(thissec->data.text);
      }

    }
    /* else: no error */
  }
  else err= -1;

  return err;
}


/*
**  GETxxx
*/

char *getfun(char *name)
{
  libfun_t *lf= (libfun_t *)0;

  if(name)
  {
    for(lf= libfun_head(funlist); (lf && strcmp(lf->name, name)); lf= lf->succ) 
      ;
  }
  else lf= thisfun;

  return lf ? lf->name : (char *)0;
}




char *getsec(char *name)
{
  funsec_t *fs= (funsec_t *)0;

  if(name)
  {
    if(thisfun)
    {
      for(fs= funsec_head(thisfun->sec); (fs && strcmp(fs->name, name)); fs= fs->succ)
        ;
    }
  }
  else fs= thissec;

  return fs ? fs->data.text : (char *)0;
}


/*
**  STEPxxx
*/


char *stepfun(int trigger)
{
  char *result= (char *)0;

  if(trigger > 0)
  {
    for(; thisfun && (trigger > 0); trigger--)
      thisfun= thisfun->succ;
  }
  else if(trigger < 0)
  {
    for(; thisfun && (trigger < 0); trigger++)
      thisfun= thisfun->pred;
  }
  else /* trigger == 0 */
    thisfun= libfun_head(funlist);

  if(thisfun)
  {
    thissec= funsec_head(thisfun->sec);
    result= thisfun->name;
  }
  else thissec= (funsec_t *)0;

  return result;
}



char *stepsec(int trigger)
{
  char *result= (char *)0;

  if(trigger > 0)
  {
    for(; thissec && (trigger > 0); trigger--)
      thissec= thissec->succ;
  }
  else if(trigger < 0)
  {
    for(; thissec && (trigger < 0); trigger++)
      thissec= thissec->pred;
  }
  else /* trigger == 0 */
  {
    if(thisfun)
      thissec= funsec_head(thisfun->sec);

    else
      thissec= (funsec_t *)0;
  }

  if(thissec)
    result= thissec->name;

  return result;
}


/*
**  FREEFUN
*/

static funsec_t *funsec_dispose(funsec_t *fs)
{
  if(fs)
  {
    fs->succ= funsec_dispose(fs->succ);

    if(fs->name)
      free(fs->name);

    if(fs->data.text)
      free(fs->data.text);
  }

  return (funsec_t *)0;
}

static libfun_t *libfun_dispose(libfun_t *lf)
{
  if(lf)
  {
    lf->succ= libfun_dispose(lf->succ);

    if(lf->name)
      free(lf->name);

    lf->sec= funsec_dispose( funsec_head(lf->sec) );

    free(lf);
  }

  return (libfun_t *)0;
}

void funfree(void)
{
  funlist= thisfun= libfun_dispose( libfun_head(funlist) );
  thissec= (funsec_t *)0;
}
