1 /*
2 * text-node.c
3 * Copyright (C) 2001-2009 Jim Evins <evins@snaught.com>.
4 *
5 * This file is part of gLabels.
6 *
7 * gLabels is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * gLabels is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with gLabels. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include <config.h>
22
23 #include "text-node.h"
24
25 #include <string.h>
26
27 #include "merge.h"
28
29 #include "debug.h"
30
31
32 /*===========================================*/
33 /* Local function prototypes */
34 /*===========================================*/
35
36 static glTextNode *extract_text_node (const gchar *text,
37 gint *n);
38
39 static gboolean is_empty_field (const glTextNode *text_node,
40 const glMergeRecord *record);
41
42
43 /****************************************************************************/
44 /* Expand single node into representative string. */
45 /****************************************************************************/
46 gchar *
gl_text_node_expand(const glTextNode * text_node,const glMergeRecord * record)47 gl_text_node_expand (const glTextNode *text_node,
48 const glMergeRecord *record)
49 {
50 gchar *text;
51
52 if (text_node->field_flag) {
53 if (record == NULL) {
54 return g_strdup_printf ("${%s}", text_node->data);
55 } else {
56 text = gl_merge_eval_key (record, text_node->data);
57 if (text != NULL) {
58 return text;
59 } else {
60 return g_strdup_printf ("%s", "");
61 }
62 }
63 } else {
64 return g_strdup (text_node->data);
65 }
66 }
67
68
69 /*--------------------------------------------------------------------------*/
70 /* PRIVATE. Is node a field that evaluates empty? */
71 /*--------------------------------------------------------------------------*/
72 static gboolean
is_empty_field(const glTextNode * text_node,const glMergeRecord * record)73 is_empty_field (const glTextNode *text_node,
74 const glMergeRecord *record)
75 {
76 gchar *text;
77 gboolean ret = FALSE;
78
79 if ( (record != NULL) && text_node->field_flag) {
80 text = gl_merge_eval_key (record, text_node->data);
81 if ( (text == NULL) || (text[0] == 0) ) {
82 ret = TRUE;
83 }
84 g_free (text);
85 }
86
87 return ret;
88 }
89
90
91 /****************************************************************************/
92 /* Create a single text node from given text. */
93 /****************************************************************************/
94 glTextNode *
gl_text_node_new_from_text(const gchar * text)95 gl_text_node_new_from_text (const gchar *text)
96 {
97 gint n;
98
99 return extract_text_node (text, &n);
100 }
101
102
103 /*--------------------------------------------------------------------------*/
104 /* PRIVATE. Create a single text node from given text. n = characters used */
105 /*--------------------------------------------------------------------------*/
106 static glTextNode *
extract_text_node(const gchar * text,gint * n)107 extract_text_node (const gchar *text,
108 gint *n)
109 {
110 glTextNode *text_node;
111 gchar *p;
112 gint m;
113
114 text_node = g_new0 (glTextNode, 1);
115
116 if (strncmp (text, "${", strlen ("${")) == 0) {
117 /* We are at the beginning of a "FIELD" node */
118 text_node->field_flag = TRUE;
119 *n = strlen ("${");
120 text += *n;
121 for (p = (gchar *)text, m = 0; *p != 0; p++, m++, (*n)++) {
122 if (*p == '}') {
123 (*n)++;
124 break;
125 }
126 }
127 text_node->data = g_strndup (text, m);
128 } else {
129 /* We are at the beginning of a literal node */
130 text_node->field_flag = FALSE;
131 for (p = (gchar *)text, *n = 0; *p != 0; p++, (*n)++) {
132 if (strncmp (p, "${", strlen ("${")) == 0)
133 break;
134 if (*p == '\n')
135 break;
136 }
137 text_node->data = g_strndup (text, *n);
138 }
139
140 return text_node;
141 }
142
143
144 /****************************************************************************/
145 /* Copy a single text node. */
146 /****************************************************************************/
147 glTextNode *
gl_text_node_dup(const glTextNode * src)148 gl_text_node_dup (const glTextNode *src)
149 {
150 glTextNode *dst;
151
152 if ( src == NULL ) return NULL;
153
154 dst = g_new0 (glTextNode, 1);
155
156 dst->field_flag = src->field_flag;
157 dst->data = g_strdup (src->data);
158
159 return dst;
160 }
161
162
163 /****************************************************************************/
164 /* Free a single text node. */
165 /****************************************************************************/
166 void
gl_text_node_free(glTextNode ** text_node)167 gl_text_node_free (glTextNode **text_node)
168 {
169 if ( *text_node == NULL ) return;
170
171 g_free ((*text_node)->data);
172 (*text_node)->data = NULL;
173 g_free (*text_node);
174 *text_node = NULL;
175 }
176
177
178 /****************************************************************************/
179 /* Compare 2 text nodes for equality. */
180 /****************************************************************************/
181 gboolean
gl_text_node_equal(const glTextNode * text_node1,const glTextNode * text_node2)182 gl_text_node_equal (const glTextNode *text_node1,
183 const glTextNode *text_node2)
184 {
185 /* First take care of the case of either or both being NULL. */
186 if ( text_node1 == NULL ) {
187 return ( text_node2 == NULL );
188 } else {
189 if ( text_node2 == NULL ) {
190 return FALSE;
191 }
192 }
193
194 /* Bail if field flags differ. */
195 if ( text_node1->field_flag != text_node2->field_flag ) {
196 return FALSE;
197 }
198
199 /* Now take care of the case of either or both data fields being NULL. */
200 if ( text_node1->data == NULL ) {
201 return ( text_node2->data == NULL );
202 } else {
203 if ( text_node2->data == NULL ) {
204 return FALSE;
205 }
206 }
207
208 /* Field flags are identical, so now compare the data. */
209 return (strcmp (text_node1->data, text_node2->data) == 0);
210 }
211
212
213 /****************************************************************************/
214 /* Expand text lines into single string. */
215 /****************************************************************************/
216 gchar *
gl_text_node_lines_expand(GList * lines,const glMergeRecord * record)217 gl_text_node_lines_expand (GList *lines,
218 const glMergeRecord *record)
219 {
220 GList *p_line, *p_node;
221 glTextNode *text_node;
222 gchar *text, *old_text, *expanded_node;
223 gboolean first_line = TRUE;
224
225 text = g_strdup (""); /* prime pointer for concatenation */
226 for (p_line = lines; p_line != NULL; p_line = p_line->next) {
227
228 /* special case: something like ${ADDRESS2} = "" on line by itself. */
229 /* in such circumstances ignore the line completely. */
230 p_node = (GList *)p_line->data;
231 if (p_node && p_node->next == NULL) {
232 text_node = (glTextNode *) p_node->data;
233 if ( is_empty_field (text_node, record) ) {
234 continue;
235 }
236 }
237
238 /* prepend newline if it's not the first line */
239 if (!first_line) {
240 old_text = text;
241 text = g_strconcat (text, "\n", NULL);
242 g_free (old_text);
243 } else {
244 first_line = FALSE;
245 }
246
247 /* expand each node */
248 for (p_node = (GList *) p_line->data; p_node != NULL;
249 p_node = p_node->next) {
250 text_node = (glTextNode *) p_node->data;
251 old_text = text;
252 expanded_node = gl_text_node_expand (text_node, record);
253 text = g_strconcat (text, expanded_node, NULL);
254 g_free (old_text);
255 g_free (expanded_node);
256 }
257 }
258
259 return text;
260 }
261
262
263 /****************************************************************************/
264 /* Parse a string back into text lines. */
265 /****************************************************************************/
266 GList *
gl_text_node_lines_new_from_text(const gchar * text)267 gl_text_node_lines_new_from_text (const gchar *text)
268 {
269 GList *lines, *nodes;
270 glTextNode *text_node;
271 gchar *p;
272 gint n;
273
274 lines = NULL;
275 nodes = NULL;
276 for (p = (gchar *)text; *p != 0; p += n) {
277 if (*p != '\n') {
278 text_node = extract_text_node (p, &n);
279 nodes = g_list_append (nodes, text_node);
280 } else {
281 n = 1;
282 lines = g_list_append (lines, nodes);
283 nodes = NULL;
284 }
285 }
286 if (p != text && *(p - 1) != '\n') {
287 lines = g_list_append (lines, nodes);
288 }
289
290 return lines;
291 }
292
293
294 /****************************************************************************/
295 /* Copy a list of text lines. */
296 /****************************************************************************/
297 GList *
gl_text_node_lines_dup(GList * src_lines)298 gl_text_node_lines_dup (GList *src_lines)
299 {
300 GList *dst_lines=NULL;
301 GList *p_line, *line, *p_node;
302 glTextNode *node;
303
304 for (p_line = src_lines; p_line != NULL; p_line = p_line->next)
305 {
306 line = NULL;
307 for (p_node = (GList *) p_line->data; p_node != NULL; p_node = p_node->next)
308 {
309 node = gl_text_node_dup ((glTextNode *)p_node->data);
310 line = g_list_append (line, node);
311 }
312 dst_lines = g_list_append (dst_lines, line);
313 }
314
315 return dst_lines;
316 }
317
318
319 /****************************************************************************/
320 /* Free a list of text lines. */
321 /****************************************************************************/
322 void
gl_text_node_lines_free(GList ** lines)323 gl_text_node_lines_free (GList **lines)
324 {
325 GList *p_line, *p_node;
326 glTextNode *text_node;
327
328 for (p_line = *lines; p_line != NULL; p_line = p_line->next)
329 {
330 for (p_node = (GList *) p_line->data; p_node != NULL; p_node = p_node->next)
331 {
332 text_node = (glTextNode *)p_node->data;
333 p_node->data = NULL;
334 gl_text_node_free ( &text_node );
335 }
336 g_list_free ((GList *) p_line->data);
337 p_line->data = NULL;
338 }
339
340 g_list_free (*lines);
341 *lines = NULL;
342 }
343
344
345 /****************************************************************************/
346 /* For debugging: descend and print lines list. */
347 /****************************************************************************/
348 void
gl_text_node_lines_print(GList * lines)349 gl_text_node_lines_print (GList * lines )
350 {
351 GList *p_line, *p_node;
352 glTextNode *text_node;
353 gint i_line, i_node;
354
355 for (p_line=lines, i_line=0; p_line != NULL; p_line=p_line->next, i_line++) {
356 for (p_node = (GList *) p_line->data, i_node=0; p_node != NULL;
357 p_node = p_node->next, i_node++) {
358 text_node = (glTextNode *) p_node->data;
359 g_print( "LINE[%d], NODE[%d] = { %d, \"%s\" }\n",
360 i_line, i_node,
361 text_node->field_flag, text_node->data );
362
363 }
364 }
365
366 }
367
368
369
370 /*
371 * Local Variables: -- emacs
372 * mode: C -- emacs
373 * c-basic-offset: 8 -- emacs
374 * tab-width: 8 -- emacs
375 * indent-tabs-mode: nil -- emacs
376 * End: -- emacs
377 */
378