home *** CD-ROM | disk | FTP | other *** search
- /* -*- 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.13 1995/04/12 13:33:43 tf Exp $ */
-
- #include <ctype.h>
- #include <stdlib.h>
- #include <string.h>
- #include <stdio.h>
-
- #include "libfun.h"
-
- /* strexpand.c */
- extern char *strexpand(char *, char **);
-
- /* repair buggy realloc() implementation */
- #define my_realloc(ptr,size) ( (ptr) ? realloc(ptr,size) : malloc(size) )
-
- /* 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; }
-
- /****** libfun/newfun ********************************************************
- *
- * NAME
- * newfun -- Begin a new function
- *
- * SYNOPSIS
- * error= newfun(fun);
- *
- * int funindent(char *fun);
- *
- * FUNCTION
- * This function adds the function `fun'. All sections added via
- * newsec() will be made sections of this function.
- *
- * INPUTS
- * fun - The name of the function to add.
- * (should be of the form "library/function")
- *
- * RESULT
- * error - either 0 on success or !=0 in case of an error
- *
- * SEE ALSO
- * newsec(), funfree()
- *
- ******************************************************************************
- *
- */
-
- 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;
- }
-
- /****** libfun/funsort *******************************************************
- *
- * NAME
- * funsort -- Sort all functions defined via newfun()
- *
- * SYNOPSIS
- * funsort();
- *
- * void funsort(void);
- *
- * FUNCTION
- * Here we sort all functions defined via newfun() alphabetically.
- *
- * INPUTS
- * none
- *
- * RESULT
- * none
- *
- * SEE ALSO
- * newfun(), funfree()
- *
- ******************************************************************************
- *
- */
-
- 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;
- }
-
-
- /****** libfun/funexpand *****************************************************
- *
- * NAME
- * funexpand -- Expand macros in all function bodies (unused)
- *
- * SYNOPSIS
- * error= funexpand(macros);
- *
- * int funexpand(char **macros);
- *
- * FUNCTION
- * This function performs a strexpand() on all body text sections of all
- * functions.
- *
- * INPUTS
- * macros - the expansion table for strexpand()
- *
- * RESULT
- * error - either 0 on success or !=0 in case of an error
- *
- * SEE ALSO
- * strexpand()
- *
- ******************************************************************************
- *
- */
-
- 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;
- }
-
-
- /****** libfun/funindent *****************************************************
- *
- * NAME
- * funindent -- Perform an indentation correction on all functions
- *
- * SYNOPSIS
- * error= funindent(indent, tabsize);
- *
- * int funindent(int indent, int tabsize);
- *
- * FUNCTION
- * This function performs a global rework on all functions defined via
- * newfun(). The first thing this function does is converting tabs in
- * all body text lines into spaces. Then the indentation of body text
- * lines in all functions is then resized to `indent' spaces.
- * The indentation is minimized if `indent' is 0. The `tabsize' is
- * needed to compute the minimum indentation of a body text, i.e. the
- * maximum width of a columnar block beginning at column 1 which cuts
- * leading white space without hurting any text.
- *
- * INPUTS
- * indent - The number of spaces used for the new indentation
- * of all body text lines
- * tabsize - The number of columns between two tab stops
- *
- * RESULT
- * error - either 0 on success or !=0 in case of an error
- *
- * SEE ALSO
- *
- ******************************************************************************
- *
- */
-
- int funindent(int indent, int tabsize)
- {
- 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)
- {
- char *s= fs->data.text;
-
- /*printf("%s: %s\n",lf->name,fs->name);*/
-
- if(s && *s)
- {
- int num_lines = 1; /* the number of lines in the body text */
- int num_tabs = 0; /* number of tabs used in the body text */
- int i, k; /* indentation counter */
- int i_min= -1; /* minimum indentation of this body text */
-
- /* compute the minimum indentation `i_min' in `s' */
-
- while(*s)
- {
- /* get the indentation `i' of the current line */
- for(i=0; *s==' ' || *s=='\t'; s++) {
- if(*s=='\t') i+= tabsize, i-= (i%tabsize), num_tabs++;
- else i++;
- }
-
- if( i>0 && (i<i_min || i_min<0) )
- i_min= i;
-
- for(;*s && *s!='\n';s++)
- if(*s=='\t') ++num_tabs;
-
- while(*s=='\n')
- ++s, ++num_lines;
- }
-
- if(i_min > 1)
- {
- /* allocate a new string buffer for the indented fs->data.text */
- char *x= (char *)malloc( (size_t)(strlen(s= fs->data.text) + num_lines*indent + num_tabs*tabsize + 1) );
-
- if(x)
- {
- char *t= x;
-
- while(*s)
- {
- /* get the indentation of the current line */
- for(i=0; *s==' ' || *s=='\t'; s++) {
- if(*s=='\t') i+= tabsize, i-= (i%tabsize);
- else i++;
- }
-
- if( *s && (*s!='\n') )
- {
- /* cut the columnar block & add the new indentation */
- for(k= i - i_min + indent; k>0; k--)
- *t++= ' ';
-
- /* `i' still holds the indentation of the old body text line.
- We need this value to expand tabs as if we would still
- have the old indentation */
-
- for(; *s && *s!='\n'; s++)
- {
- if(*s=='\t')
- {
- /* DICE (Amiga) compiles the following line wrong! */
- /*do { *t++= ' '; } while( (++i) % tabsize );*/
- do { *t++= ' '; i++; } while( (i % tabsize) );
- }
- else i++, *t++= *s;
- }
- }
-
- while(*s=='\n')
- *t++= *s++;
- }
- *t= '\0';
-
- free(fs->data.text);
- fs->data.text= x;
- }
- else err= __LINE__;
- }
- }
- }
- }
-
- return err;
- }
-
- /****** libfun/newsec ********************************************************
- *
- * NAME
- * newsec -- Add a new heading to the current function description
- *
- * SYNOPSIS
- * error= newsec(title);
- *
- * int newsec(char *title);
- *
- * FUNCTION
- * This function adds a new section heading `title' to the current
- * function description which began with the last newfun() call.
- * The following calls of addtext() will add the text to this new
- * section.
- *
- * INPUTS
- * title - The section heading keyword
- *
- * RESULT
- * error - either 0 on success or !=0 in case of an error
- *
- * SEE ALSO
- * newfun(), addtext()
- *
- ******************************************************************************
- *
- */
-
- 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;
- }
-
- /****** libfun/addtext *******************************************************
- *
- * NAME
- * addtext -- Add a new line of text to the current function description
- *
- * SYNOPSIS
- * error= addtext(text);
- *
- * int funindent(char *text);
- *
- * FUNCTION
- * This function dynamically adds a new line of text to the current
- * section in the current function description. There does not need
- * to be a trailing newline `\n' at the end of `text'.
- *
- * INPUTS
- * text - The string to add to the description
- *
- * RESULT
- * error - either 0 on success or !=0 in case of an error
- *
- * NOTES
- * Body text can only be added to a section. There must be at least
- * one newsec() following newfun() before this function can add body
- * text lines.
- *
- * SEE ALSO
- * newsec(), funfree()
- *
- ******************************************************************************
- *
- */
-
- 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 *)my_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;
- }
-
-
- /****** libfun/getfun ********************************************************
- *
- * NAME
- * getfun -- Get a function by name
- *
- * SYNOPSIS
- * fun= getfun(name)
- *
- * char *getfun(char *name);
- *
- * FUNCTION
- * This function searches the list of function descriptions for
- * a function `name' and returns a pointer `fun' to the name of the
- * function or a (char *)0 if there is no such function `name'.
- *
- * INPUTS
- * name - The name of the function to look for
- *
- * RESULT
- * fun - The real name of the function `name' or (char *)0
- * if no function `name' was found.
- *
- * SEE ALSO
- * newfun(), getsec()
- *
- ******************************************************************************
- *
- */
-
- 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;
- }
-
-
- /****** libfun/getsec ********************************************************
- *
- * NAME
- * getsec -- Get a section text via section heading
- *
- * SYNOPSIS
- * text= getsec(heading)
- *
- * char *getsec(char *heading);
- *
- * FUNCTION
- * This function searches for the section `heading' in the function
- * description of the current function. If this section exists then
- * the corresponding section text is returned.
- *
- * INPUTS
- * heading - The section title keyword to look for
- *
- * RESULT
- * text - The section text of that section or (char *)0 if
- * there is no such section in the description of the
- * current function.
- *
- * SEE ALSO
- * newsec(), getfun()
- *
- ******************************************************************************
- *
- */
-
- 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;
- }
-
-
- /****** libfun/islib **********************************************************
- *
- * NAME
- * islib -- Find a library in the list of functions
- *
- * SYNOPSIS
- * gotit= islib(name)
- *
- * int islib(char *name);
- *
- * FUNCTION
- * Scan the list of functions for a function which begins with `name'
- * and has a `/' immediately following `name'.
- *
- * INPUTS
- * name - The name of the library (without a trailing `/')
- *
- * RESULT
- * gotit - 1 if there is at least one function `name/...'
- * 0 otherwise.
- *
- ******************************************************************************
- *
- */
-
- int islib(char *name)
- {
- libfun_t *lf= (libfun_t *)0;
-
- if(name)
- {
- size_t l= strlen(name);
-
- for(lf= libfun_head(funlist); (lf && strncmp(lf->name, name, l)); lf= lf->succ)
- ;
- if(lf && lf->name && (lf->name)[l]!='/')
- lf= (libfun_t *)0;
- }
-
- return lf ? 1:0;
- }
-
-
- /****** libfun/stepfun *******************************************************
- *
- * NAME
- * stepfun -- Move the current function pointer
- *
- * SYNOPSIS
- * fun= stepfun(trigger);
- *
- * char *stepfun(int trigger);
- *
- * FUNCTION
- * This function moves the current function pointer according to the
- * `trigger' value.
- *
- * INPUTS
- * trigger - < 0 moves back `-trigger' functions
- * = 0 moves to the first function
- * > 0 moves forward `trigger' functions
- *
- * RESULT
- * fun - The name of the function or (char *)0
- *
- * SEE ALSO
- * stepsec()
- *
- ******************************************************************************
- *
- */
-
- 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;
- }
-
-
- /****** libfun/stepsec *******************************************************
- *
- * NAME
- * stepsec -- Move the current section pointer
- *
- * SYNOPSIS
- * sec= stepsec(trigger);
- *
- * char *stepsec(int trigger);
- *
- * FUNCTION
- * This function moves the current section pointer according to the
- * `trigger' value.
- *
- * INPUTS
- * trigger - < 0 moves back `-trigger' sections
- * = 0 moves to the first section
- * > 0 moves forward `trigger' sections
- *
- * RESULT
- * sec - The name of the section or (char *)0
- *
- * SEE ALSO
- * stepfun()
- *
- ******************************************************************************
- *
- */
-
- 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;
- }
-
-
- /****** libfun/funfree *******************************************************
- *
- * NAME
- * funfree -- Dispose all function descriptions
- *
- * SYNOPSIS
- * funfree();
- *
- * void funfree(void);
- *
- * FUNCTION
- * This function disposes all function descriptions including all their
- * sections.
- *
- * INPUTS
- * none
- *
- * RESULT
- * none
- *
- * SEE ALSO
- * newfun(), newsec(), addtext()
- *
- ******************************************************************************
- *
- */
-
- 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;
- }
-