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