You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tellico/src/translators/btparse/tex_tree.c

415 lines
11 KiB

/* ------------------------------------------------------------------------
@NAME : tex_tree.c
@DESCRIPTION: Functions for dealing with strings of TeX code: converting
them to tree representation, traversing the trees to glean
useful information, and converting back to string form.
@GLOBALS :
@CALLS :
@CALLERS :
@CREATED : 1997/05/29, Greg Ward
@MODIFIED :
@VERSION : $Id: tex_tree.c,v 1.4 1999/11/29 01:13:10 greg Rel $
@COPYRIGHT : Copyright (c) 1996-99 by Gregory P. Ward. All rights reserved.
This file is part of the btparse library. This library 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 2
of the License, or (at your option) any later version.
-------------------------------------------------------------------------- */
/*#include "bt_config.h"*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "error.h"
#include "btparse.h"
/*#include "my_dmalloc.h"*/
/* blech! temp hack until I make error.c perfect and magical */
#define string_warning(w) fprintf (stderr, w);
typedef struct treestack_s
{
bt_tex_tree * node;
struct treestack_s
* prev,
* next;
} treestack;
/* ----------------------------------------------------------------------
* Stack manipulation functions
*/
/* ------------------------------------------------------------------------
@NAME : push_treestack()
@INPUT : *stack
node
@OUTPUT : *stack
@RETURNS :
@DESCRIPTION: Creates and initializes new node in a stack, and pushes it
onto the stack.
@GLOBALS :
@CALLS :
@CALLERS :
@CREATED : 1997/05/29, GPW
@MODIFIED :
-------------------------------------------------------------------------- */
static void
push_treestack (treestack **stack, bt_tex_tree *node)
{
treestack *newtop;
newtop = (treestack *) malloc (sizeof (treestack));
newtop->node = node;
newtop->next = NULL;
newtop->prev = *stack;
if (*stack != NULL) /* stack already has some entries */
{
(*stack)->next = newtop;
*stack = newtop;
}
*stack = newtop;
} /* push_treestack() */
/* ------------------------------------------------------------------------
@NAME : pop_treestack
@INPUT : *stack
@OUTPUT : *stack
@RETURNS :
@DESCRIPTION: Pops an entry off of a stack of tex_tree nodes, frees up
the wrapper treestack node, and returns the popped tree node.
@GLOBALS :
@CALLS :
@CALLERS :
@CREATED : 1997/05/29, GPW
@MODIFIED :
-------------------------------------------------------------------------- */
static bt_tex_tree *
pop_treestack (treestack **stack)
{
treestack * oldtop;
bt_tex_tree * node;
if (*stack == NULL)
internal_error ("attempt to pop off empty stack");
oldtop = (*stack)->prev;
node = (*stack)->node;
free (*stack);
if (oldtop != NULL)
oldtop->next = NULL;
*stack = oldtop;
return node;
} /* pop_treestack() */
/* ----------------------------------------------------------------------
* Tree creation/destruction functions
*/
/* ------------------------------------------------------------------------
@NAME : new_tex_tree
@INPUT : start
@OUTPUT :
@RETURNS : pointer to newly-allocated node
@DESCRIPTION: Allocates and initializes a bt_tex_tree node.
@GLOBALS :
@CALLS :
@CALLERS :
@CREATED : 1997/05/29, GPW
@MODIFIED :
-------------------------------------------------------------------------- */
static bt_tex_tree *
new_tex_tree (char *start)
{
bt_tex_tree * node;
node = (bt_tex_tree *) malloc (sizeof (bt_tex_tree));
node->start = start;
node->len = 0;
node->child = node->next = NULL;
return node;
}
/* ------------------------------------------------------------------------
@NAME : bt_build_tex_tree
@INPUT : string
@OUTPUT :
@RETURNS : pointer to a complete tree; call bt_free_tex_tree() to free
the entire tree
@DESCRIPTION: Traverses a string looking for TeX groups ({...}), and builds
a tree containing pointers into the string and describing
its brace-structure.
@GLOBALS :
@CALLS :
@CALLERS :
@CREATED : 1997/05/29, GPW
@MODIFIED :
-------------------------------------------------------------------------- */
bt_tex_tree *
bt_build_tex_tree (char * string)
{
int i;
int depth;
int len;
bt_tex_tree
* top,
* cur,
* new;
treestack
* stack;
i = 0;
depth = 0;
len = strlen (string);
top = new_tex_tree (string);
stack = NULL;
cur = top;
while (i < len)
{
switch (string[i])
{
case '{': /* go one level deeper */
{
if (i == len-1) /* open brace in last character? */
{
string_warning ("unbalanced braces: { at end of string");
goto error;
}
new = new_tex_tree (string+i+1);
cur->child = new;
push_treestack (&stack, cur);
cur = new;
depth++;
break;
}
case '}': /* pop level(s) off */
{
while (i < len && string[i] == '}')
{
if (stack == NULL)
{
string_warning ("unbalanced braces: extra }");
goto error;
}
cur = pop_treestack (&stack);
depth--;
i++;
}
i--;
if (i == len-1) /* reached end of string? */
{
if (depth > 0) /* but not at depth 0 */
{
string_warning ("unbalanced braces: not enough }'s");
goto error;
}
/*
* if we get here, do nothing -- we've reached the end of
* the string and are at depth 0, so will just fall out
* of the while loop at the end of this iteration
*/
}
else /* still have characters left */
{ /* to worry about */
new = new_tex_tree (string+i+1);
cur->next = new;
cur = new;
}
break;
}
default:
{
cur->len++;
}
} /* switch */
i++;
} /* while i */
if (depth > 0)
{
string_warning ("unbalanced braces (not enough }'s)");
goto error;
}
return top;
error:
bt_free_tex_tree (&top);
return NULL;
} /* bt_build_tex_tree() */
/* ------------------------------------------------------------------------
@NAME : bt_free_tex_tree
@INPUT : *top
@OUTPUT : *top (set to NULL after it's free()'d)
@RETURNS :
@DESCRIPTION: Frees up an entire tree created by bt_build_tex_tree().
@GLOBALS :
@CALLS : itself, free()
@CALLERS :
@CREATED : 1997/05/29, GPW
@MODIFIED :
-------------------------------------------------------------------------- */
void
bt_free_tex_tree (bt_tex_tree **top)
{
if ((*top)->child) bt_free_tex_tree (&(*top)->child);
if ((*top)->next) bt_free_tex_tree (&(*top)->next);
free (*top);
*top = NULL;
}
/* ----------------------------------------------------------------------
* Tree traversal functions
*/
/* ------------------------------------------------------------------------
@NAME : bt_dump_tex_tree
@INPUT : node
depth
stream
@OUTPUT :
@RETURNS :
@DESCRIPTION: Dumps a TeX tree: one node per line, depth indented according
to depth.
@GLOBALS :
@CALLS : itself
@CALLERS :
@CREATED : 1997/05/29, GPW
@MODIFIED :
-------------------------------------------------------------------------- */
void
bt_dump_tex_tree (bt_tex_tree *node, int depth, FILE *stream)
{
char buf[256];
if (node == NULL)
return;
if (node->len > 255)
internal_error ("augughgh! buf too small");
strncpy (buf, node->start, node->len);
buf[node->len] = (char) 0;
fprintf (stream, "%*s[%s]\n", depth*2, "", buf);
bt_dump_tex_tree (node->child, depth+1, stream);
bt_dump_tex_tree (node->next, depth, stream);
}
/* ------------------------------------------------------------------------
@NAME : count_length
@INPUT : node
@OUTPUT :
@RETURNS :
@DESCRIPTION: Counts the total number of characters that will be needed
to print a string reconstructed from a TeX tree. (Length
of string in each node, plus two [{ and }] for each down
edge.)
@GLOBALS :
@CALLS : itself
@CALLERS : bt_flatten_tex_tree
@CREATED : 1997/05/29, GPW
@MODIFIED :
-------------------------------------------------------------------------- */
static int
count_length (bt_tex_tree *node)
{
if (node == NULL) return 0;
return
node->len +
(node->child ? 2 : 0) +
count_length (node->child) +
count_length (node->next);
}
/* ------------------------------------------------------------------------
@NAME : flatten_tree
@INPUT : node
*offset
@OUTPUT : *buf
*offset
@RETURNS :
@DESCRIPTION: Dumps a reconstructed string ("flat" representation of the
tree) into a pre-allocated buffer, starting at a specified
offset.
@GLOBALS :
@CALLS : itself
@CALLERS : bt_flatten_tex_tree
@CREATED : 1997/05/29, GPW
@MODIFIED :
-------------------------------------------------------------------------- */
static void
flatten_tree (bt_tex_tree *node, char *buf, int *offset)
{
strncpy (buf + *offset, node->start, node->len);
*offset += node->len;
if (node->child)
{
buf[(*offset)++] = '{';
flatten_tree (node->child, buf, offset);
buf[(*offset)++] = '}';
}
if (node->next)
{
flatten_tree (node->next, buf, offset);
}
}
/* ------------------------------------------------------------------------
@NAME : bt_flatten_tex_tree
@INPUT : top
@OUTPUT :
@RETURNS : flattened string representation of the tree (as a string
allocated with malloc(), so you should free() it when
you're done with it)
@DESCRIPTION: Counts the number of characters needed for a "flat"
string representation of a tree, allocates a string of
that size, and generates the string.
@GLOBALS :
@CALLS : count_length, flatten_tree
@CALLERS :
@CREATED : 1997/05/29, GPW
@MODIFIED :
-------------------------------------------------------------------------- */
char *
bt_flatten_tex_tree (bt_tex_tree *top)
{
int len;
int offset;
char * buf;
len = count_length (top);
buf = (char *) malloc (sizeof (char) * (len+1));
offset = 0;
flatten_tree (top, buf, &offset);
return buf;
}