1 /****************************************************************************
2  * Copyright (C) 2008 by Matteo Franchin                                    *
3  *                                                                          *
4  * This file is part of Box.                                                *
5  *                                                                          *
6  *   Box is free software: you can redistribute it and/or modify it         *
7  *   under the terms of the GNU Lesser General Public License as published  *
8  *   by the Free Software Foundation, either version 3 of the License, or   *
9  *   (at your option) any later version.                                    *
10  *                                                                          *
11  *   Box is distributed in the hope that it will be useful,                 *
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of         *
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
14  *   GNU Lesser General Public License for more details.                    *
15  *                                                                          *
16  *   You should have received a copy of the GNU Lesser General Public       *
17  *   License along with Box.  If not, see <http://www.gnu.org/licenses/>.   *
18  ****************************************************************************/
19 
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <assert.h>
23 
24 #include "types.h"
25 #include "formatter.h"
26 
27 #define MAX_STACK_LEVEL 10
28 #define BUFFER_SIZE 128
29 
30 typedef enum {
31   STATUS_NORMAL=0,
32   STATUS_STACK_FULL,
33   STATUS_LITERAL,
34   STATUS_WAIT_SUB,
35   STATUS_WAIT_SUP
36 } Status;
37 
_Add_Char(BoxGFmtStack * stack,char c)38 static void _Add_Char(BoxGFmtStack *stack, char c) {
39   BoxGFmt *fmt = stack->fmt;
40   int i = fmt->buffer_pos++;
41   if (fmt->buffer_pos > fmt->buffer_size) {
42     if (fmt->buffer == (char *) NULL) {
43       fmt->buffer = (char *) malloc(BUFFER_SIZE);
44       fmt->buffer_size = BUFFER_SIZE;
45     }
46 
47     if (fmt->buffer_pos > fmt->buffer_size) {
48       while (fmt->buffer_pos > fmt->buffer_size) fmt->buffer_size *= 2;
49       fmt->buffer = (char *) realloc(fmt->buffer, fmt->buffer_size);
50     }
51 
52     assert(fmt->buffer != (char *) NULL);
53   }
54 
55   fmt->buffer[i] = c;
56 }
57 
BoxGFmt_Get_Buffer(BoxGFmtStack * stack)58 char *BoxGFmt_Get_Buffer(BoxGFmtStack *stack) {
59   BoxGFmt *fmt = stack->fmt;
60   _Add_Char(stack, '\0');
61   --fmt->buffer_pos;
62   return fmt->buffer;
63 }
64 
BoxGFmt_Clear_Buffer(BoxGFmtStack * stack)65 void BoxGFmt_Clear_Buffer(BoxGFmtStack *stack) {
66   BoxGFmt *fmt = stack->fmt;
67   fmt->buffer_pos = 0;
68 }
69 
_Draw(BoxGFmtStack * stack)70 static int _Draw(BoxGFmtStack *stack) {
71   BoxGFmt *fmt = stack->fmt;
72   if (fmt->buffer_pos < 1) return stack->eye;
73   if (fmt->draw != NULL) fmt->draw(stack);
74   return stack->eye;
75 }
76 
_Save(BoxGFmtStack * stack)77 static void _Save(BoxGFmtStack *stack) {
78   BoxGFmt *fmt = stack->fmt;
79   if (fmt->save != NULL) fmt->save(stack);
80 }
81 
_Restore(BoxGFmtStack * stack)82 static void _Restore(BoxGFmtStack *stack) {
83   BoxGFmt *fmt = stack->fmt;
84   if (fmt->restore != NULL) fmt->restore(stack);
85 }
86 
_Subscript(BoxGFmt * fmt,BoxGFmtStack * stack)87 static void _Subscript(BoxGFmt *fmt, BoxGFmtStack *stack) {
88   if (fmt->subscript != NULL) fmt->subscript(stack);
89 }
90 
_Superscript(BoxGFmt * fmt,BoxGFmtStack * stack)91 static void _Superscript(BoxGFmt *fmt, BoxGFmtStack *stack) {
92   if (fmt->superscript != NULL) fmt->superscript(stack);
93 }
94 
_Text_Formatter(BoxGFmtStack * stack)95 static int _Text_Formatter(BoxGFmtStack *stack) {
96   BoxGFmt *fmt = stack->fmt;
97   Status status;
98   BoxGFmtStack new_stack;
99 
100   status = (stack->level >= MAX_STACK_LEVEL) ?
101            STATUS_STACK_FULL : STATUS_NORMAL;
102 
103   for(;;) {
104     char c = stack->text[stack->eye];
105     if (c == '\0') return _Draw(stack);
106 
107     switch(status) {
108     case STATUS_STACK_FULL:
109       ++stack->eye;
110       switch(c) {
111       case '}':
112         if (stack->level == MAX_STACK_LEVEL) return stack->eye;
113         --stack->level;
114         break;
115       case '{':
116         ++stack->level;
117         break;
118       }
119       break;
120 
121     case STATUS_NORMAL:
122       ++stack->eye;
123       switch(c) {
124       case '_':
125         status = STATUS_WAIT_SUB;
126         break;
127       case '^':
128         status = STATUS_WAIT_SUP;
129         break;
130       case '}':
131         if (stack->level > 0) {
132           fmt->draw(stack);
133           return stack->eye;
134         }
135         _Add_Char(stack, c);
136         break;
137       case '\n':
138         (void) _Draw(stack);
139         fmt->newline(stack);
140         break;
141       default:
142         _Add_Char(stack, c);
143         break;
144       }
145       break;
146 
147     case STATUS_LITERAL:
148       status = STATUS_NORMAL;
149       ++stack->eye;
150       _Add_Char(stack, c);
151       break;
152 
153     case STATUS_WAIT_SUB:
154     case STATUS_WAIT_SUP:
155       ++stack->eye;
156       switch(c) {
157       case '^':
158       case '_':
159         _Add_Char(stack, c);
160         status = STATUS_NORMAL;
161         break;
162 
163       default:
164         (void) _Draw(stack);
165         _Save(stack);
166         new_stack = *stack;
167         ++new_stack.level;
168         if (status == STATUS_WAIT_SUB)
169           _Subscript(fmt, & new_stack);
170         else
171           _Superscript(fmt, & new_stack);
172 
173         if (c == '{') {
174           stack->eye = _Text_Formatter(& new_stack);
175 
176         } else {
177           /* We emulate the syntax 'char_{s}' for the syntex 'char_s' */
178           new_stack.short_text[0] = c;
179           new_stack.short_text[1] = '}';
180           new_stack.short_text[2] = '\0';
181           new_stack.text = new_stack.short_text;
182           new_stack.eye = 0;
183           (void) _Text_Formatter(& new_stack);
184         }
185 
186         _Restore(stack);
187         if (stack->eye == -1) return stack->eye;
188         status = STATUS_NORMAL;
189         break;
190       }
191       break;
192     }
193   }
194 }
195 
BoxGFmt_Init(BoxGFmt * fmt)196 void BoxGFmt_Init(BoxGFmt *fmt) {
197   fmt->restore = fmt->save = fmt->draw = fmt->newline =
198     fmt->subscript = fmt->superscript = NULL;
199   fmt->private_data = (void *) NULL;
200 }
201 
BoxGFmt_Get(BoxGFmtStack * stack)202 BoxGFmt *BoxGFmt_Get(BoxGFmtStack *stack) {
203   return stack->fmt;
204 }
205 
BoxGFmt_Get_Private(BoxGFmt * fmt)206 void *BoxGFmt_Get_Private(BoxGFmt *fmt) {
207   return fmt->private_data;
208 }
209 
BoxGFmt_Set_Private(BoxGFmt * fmt,void * private_data)210 void BoxGFmt_Set_Private(BoxGFmt *fmt, void *private_data) {
211   fmt->private_data = private_data;
212 }
213 
BoxGFmt_Draw_Text(BoxGFmt * fmt,const char * text)214 void BoxGFmt_Draw_Text(BoxGFmt *fmt, const char *text) {
215   BoxGFmtStack stack;
216   stack.level = 0;
217   stack.eye = 0;
218   stack.text = text;
219 
220   fmt->buffer_pos = 0;
221   fmt->buffer_size = 0;
222   fmt->buffer = (char *) NULL;
223   stack.fmt = fmt;
224 
225   (void) _Text_Formatter(& stack);
226   if (fmt->buffer != (char *) NULL) free(fmt->buffer);
227 }
228