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