1 /* ------------------------------------------------------------------------
2 @NAME       : tex_tree.c
3 @DESCRIPTION: Functions for dealing with strings of TeX code: converting
4               them to tree representation, traversing the trees to glean
5               useful information, and converting back to string form.
6 @GLOBALS    :
7 @CALLS      :
8 @CALLERS    :
9 @CREATED    : 1997/05/29, Greg Ward
10 @MODIFIED   :
11 @VERSION    : $Id$
12 @COPYRIGHT  : Copyright (c) 1996-99 by Gregory P. Ward.  All rights reserved.
13 
14               This file is part of the btparse library.  This library is
15               free software; you can redistribute it and/or modify it under
16               the terms of the GNU Library General Public License as
17               published by the Free Software Foundation; either version 2
18               of the License, or (at your option) any later version.
19 -------------------------------------------------------------------------- */
20 
21 #include "bt_config.h"
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include "error.h"
26 #include "btparse.h"
27 #include "my_dmalloc.h"
28 
29 /* blech! temp hack until I make error.c perfect and magical */
30 #define string_warning(w) fprintf (stderr, w);
31 
32 typedef struct treestack_s
33 {
34    bt_tex_tree * node;
35    struct treestack_s
36                * prev,
37                * next;
38 } treestack;
39 
40 
41 /* ----------------------------------------------------------------------
42  * Stack manipulation functions
43  */
44 
45 /* ------------------------------------------------------------------------
46 @NAME       : push_treestack()
47 @INPUT      : *stack
48               node
49 @OUTPUT     : *stack
50 @RETURNS    :
51 @DESCRIPTION: Creates and initializes new node in a stack, and pushes it
52               onto the stack.
53 @GLOBALS    :
54 @CALLS      :
55 @CALLERS    :
56 @CREATED    : 1997/05/29, GPW
57 @MODIFIED   :
58 -------------------------------------------------------------------------- */
59 static void
push_treestack(treestack ** stack,bt_tex_tree * node)60 push_treestack (treestack **stack, bt_tex_tree *node)
61 {
62    treestack  *newtop;
63 
64    newtop = (treestack *) malloc (sizeof (treestack));
65    newtop->node = node;
66    newtop->next = NULL;
67    newtop->prev = *stack;
68 
69    if (*stack != NULL)                  /* stack already has some entries */
70    {
71       (*stack)->next = newtop;
72       *stack = newtop;
73    }
74 
75    *stack = newtop;
76 
77 } /* push_treestack() */
78 
79 
80 /* ------------------------------------------------------------------------
81 @NAME       : pop_treestack
82 @INPUT      : *stack
83 @OUTPUT     : *stack
84 @RETURNS    :
85 @DESCRIPTION: Pops an entry off of a stack of tex_tree nodes, frees up
86               the wrapper treestack node, and returns the popped tree node.
87 @GLOBALS    :
88 @CALLS      :
89 @CALLERS    :
90 @CREATED    : 1997/05/29, GPW
91 @MODIFIED   :
92 -------------------------------------------------------------------------- */
93 static bt_tex_tree *
pop_treestack(treestack ** stack)94 pop_treestack (treestack **stack)
95 {
96    treestack *   oldtop;
97    bt_tex_tree * node;
98 
99    if (*stack == NULL)
100       internal_error ("attempt to pop off empty stack");
101    oldtop = (*stack)->prev;
102    node = (*stack)->node;
103    free (*stack);
104    if (oldtop != NULL)
105       oldtop->next = NULL;
106    *stack = oldtop;
107    return node;
108 
109 } /* pop_treestack() */
110 
111 
112 /* ----------------------------------------------------------------------
113  * Tree creation/destruction functions
114  */
115 
116 /* ------------------------------------------------------------------------
117 @NAME       : new_tex_tree
118 @INPUT      : start
119 @OUTPUT     :
120 @RETURNS    : pointer to newly-allocated node
121 @DESCRIPTION: Allocates and initializes a bt_tex_tree node.
122 @GLOBALS    :
123 @CALLS      :
124 @CALLERS    :
125 @CREATED    : 1997/05/29, GPW
126 @MODIFIED   :
127 -------------------------------------------------------------------------- */
128 static bt_tex_tree *
new_tex_tree(char * start)129 new_tex_tree (char *start)
130 {
131    bt_tex_tree * node;
132 
133    node = (bt_tex_tree *) malloc (sizeof (bt_tex_tree));
134    node->start = start;
135    node->len = 0;
136    node->child = node->next = NULL;
137    return node;
138 }
139 
140 
141 /* ------------------------------------------------------------------------
142 @NAME       : bt_build_tex_tree
143 @INPUT      : string
144 @OUTPUT     :
145 @RETURNS    : pointer to a complete tree; call bt_free_tex_tree() to free
146               the entire tree
147 @DESCRIPTION: Traverses a string looking for TeX groups ({...}), and builds
148               a tree containing pointers into the string and describing
149               its brace-structure.
150 @GLOBALS    :
151 @CALLS      :
152 @CALLERS    :
153 @CREATED    : 1997/05/29, GPW
154 @MODIFIED   :
155 -------------------------------------------------------------------------- */
156 bt_tex_tree *
bt_build_tex_tree(char * string)157 bt_build_tex_tree (char * string)
158 {
159    int     i;
160    int     depth;
161    int     len;
162    bt_tex_tree
163          * top,
164          * cur,
165          * new;
166    treestack
167          * stack;
168 
169    i = 0;
170    depth = 0;
171    len = strlen (string);
172    top = new_tex_tree (string);
173    stack = NULL;
174 
175    cur = top;
176 
177    while (i < len)
178    {
179       switch (string[i])
180       {
181          case '{':                      /* go one level deeper */
182          {
183             if (i == len-1)             /* open brace in last character? */
184             {
185                string_warning ("unbalanced braces: { at end of string");
186                goto error;
187             }
188 
189             new = new_tex_tree (string+i+1);
190             cur->child = new;
191             push_treestack (&stack, cur);
192             cur = new;
193             depth++;
194             break;
195          }
196          case '}':                      /* pop level(s) off */
197          {
198             while (i < len && string[i] == '}')
199             {
200                if (stack == NULL)
201                {
202                   string_warning ("unbalanced braces: extra }");
203                   goto error;
204                }
205                cur = pop_treestack (&stack);
206                depth--;
207                i++;
208             }
209             i--;
210 
211             if (i == len-1)             /* reached end of string? */
212             {
213                if (depth > 0)           /* but not at depth 0 */
214                {
215                   string_warning ("unbalanced braces: not enough }'s");
216                   goto error;
217                }
218 
219                /*
220                 * if we get here, do nothing -- we've reached the end of
221                 * the string and are at depth 0, so will just fall out
222                 * of the while loop at the end of this iteration
223                 */
224             }
225             else                        /* still have characters left */
226             {                           /* to worry about */
227                new = new_tex_tree (string+i+1);
228                cur->next = new;
229                cur = new;
230             }
231 
232             break;
233          }
234          default:
235          {
236             cur->len++;
237          }
238 
239       } /* switch */
240 
241       i++;
242 
243    } /* while i */
244 
245    if (depth > 0)
246    {
247       string_warning ("unbalanced braces (not enough }'s)");
248       goto error;
249    }
250 
251    return top;
252 
253 error:
254    bt_free_tex_tree (&top);
255    return NULL;
256 
257 } /* bt_build_tex_tree() */
258 
259 
260 /* ------------------------------------------------------------------------
261 @NAME       : bt_free_tex_tree
262 @INPUT      : *top
263 @OUTPUT     : *top (set to NULL after it's free()'d)
264 @RETURNS    :
265 @DESCRIPTION: Frees up an entire tree created by bt_build_tex_tree().
266 @GLOBALS    :
267 @CALLS      : itself, free()
268 @CALLERS    :
269 @CREATED    : 1997/05/29, GPW
270 @MODIFIED   :
271 -------------------------------------------------------------------------- */
272 void
bt_free_tex_tree(bt_tex_tree ** top)273 bt_free_tex_tree (bt_tex_tree **top)
274 {
275    if ((*top)->child) bt_free_tex_tree (&(*top)->child);
276    if ((*top)->next) bt_free_tex_tree (&(*top)->next);
277    free (*top);
278    *top = NULL;
279 }
280 
281 
282 
283 /* ----------------------------------------------------------------------
284  * Tree traversal functions
285  */
286 
287 /* ------------------------------------------------------------------------
288 @NAME       : bt_dump_tex_tree
289 @INPUT      : node
290               depth
291               stream
292 @OUTPUT     :
293 @RETURNS    :
294 @DESCRIPTION: Dumps a TeX tree: one node per line, depth indented according
295               to depth.
296 @GLOBALS    :
297 @CALLS      : itself
298 @CALLERS    :
299 @CREATED    : 1997/05/29, GPW
300 @MODIFIED   :
301 -------------------------------------------------------------------------- */
302 void
bt_dump_tex_tree(bt_tex_tree * node,int depth,FILE * stream)303 bt_dump_tex_tree (bt_tex_tree *node, int depth, FILE *stream)
304 {
305    char  buf[256];
306 
307    if (node == NULL)
308       return;
309 
310    if (node->len > 255)
311       internal_error ("augughgh! buf too small");
312    strncpy (buf, node->start, node->len);
313    buf[node->len] = (char) 0;
314 
315    fprintf (stream, "%*s[%s]\n", depth*2, "", buf);
316 
317    bt_dump_tex_tree (node->child, depth+1, stream);
318    bt_dump_tex_tree (node->next, depth, stream);
319 
320 }
321 
322 
323 /* ------------------------------------------------------------------------
324 @NAME       : count_length
325 @INPUT      : node
326 @OUTPUT     :
327 @RETURNS    :
328 @DESCRIPTION: Counts the total number of characters that will be needed
329               to print a string reconstructed from a TeX tree.  (Length
330               of string in each node, plus two [{ and }] for each down
331               edge.)
332 @GLOBALS    :
333 @CALLS      : itself
334 @CALLERS    : bt_flatten_tex_tree
335 @CREATED    : 1997/05/29, GPW
336 @MODIFIED   :
337 -------------------------------------------------------------------------- */
338 static int
count_length(bt_tex_tree * node)339 count_length (bt_tex_tree *node)
340 {
341    if (node == NULL) return 0;
342    return
343       node->len +
344       (node->child ? 2 : 0) +
345       count_length (node->child) +
346       count_length (node->next);
347 }
348 
349 
350 /* ------------------------------------------------------------------------
351 @NAME       : flatten_tree
352 @INPUT      : node
353               *offset
354 @OUTPUT     : *buf
355               *offset
356 @RETURNS    :
357 @DESCRIPTION: Dumps a reconstructed string ("flat" representation of the
358               tree) into a pre-allocated buffer, starting at a specified
359               offset.
360 @GLOBALS    :
361 @CALLS      : itself
362 @CALLERS    : bt_flatten_tex_tree
363 @CREATED    : 1997/05/29, GPW
364 @MODIFIED   :
365 -------------------------------------------------------------------------- */
366 static void
flatten_tree(bt_tex_tree * node,char * buf,int * offset)367 flatten_tree (bt_tex_tree *node, char *buf, int *offset)
368 {
369    strncpy (buf + *offset, node->start, node->len);
370    *offset += node->len;
371 
372    if (node->child)
373    {
374       buf[(*offset)++] = '{';
375       flatten_tree (node->child, buf, offset);
376       buf[(*offset)++] = '}';
377    }
378 
379    if (node->next)
380    {
381       flatten_tree (node->next, buf, offset);
382    }
383 }
384 
385 
386 /* ------------------------------------------------------------------------
387 @NAME       : bt_flatten_tex_tree
388 @INPUT      : top
389 @OUTPUT     :
390 @RETURNS    : flattened string representation of the tree (as a string
391               allocated with malloc(), so you should free() it when
392               you're done with it)
393 @DESCRIPTION: Counts the number of characters needed for a "flat"
394               string representation of a tree, allocates a string of
395               that size, and generates the string.
396 @GLOBALS    :
397 @CALLS      : count_length, flatten_tree
398 @CALLERS    :
399 @CREATED    : 1997/05/29, GPW
400 @MODIFIED   :
401 -------------------------------------------------------------------------- */
402 char *
bt_flatten_tex_tree(bt_tex_tree * top)403 bt_flatten_tex_tree (bt_tex_tree *top)
404 {
405    int    len;
406    int    offset;
407    char * buf;
408 
409    len = count_length (top);
410    buf = (char *) malloc (sizeof (char) * (len+1));
411    offset = 0;
412    flatten_tree (top, buf, &offset);
413    return buf;
414 }
415