strip whitespace from subscribed URLs
[claws.git] / src / plugins / rssyl / strutils.c
1 /*
2  * Claws-Mail-- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2005 Andrej Kacian <andrej@kacian.sk>
4  *
5  * - a strreplace function (something like sed's s/foo/bar/g)
6  *
7  * This program 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 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program 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 this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 #endif
25
26 /* Global includes */
27 #include <glib.h>
28 #include <stdlib.h>
29 #include <ctype.h>
30
31 /* Claws Mail includes */
32 #include <common/utils.h>
33 #include <entity.h>
34
35 /* Local includes */
36 /* (shouldn't be any) */
37
38 gchar *rssyl_strreplace(gchar *source, gchar *pattern,
39                 gchar *replacement)
40 {
41         gchar *new, *w_new = NULL, *c;
42         guint count = 0, final_length;
43         size_t len_pattern, len_replacement;
44
45         /*
46         debug_print("RSSyl: ======= strreplace: '%s': '%s'->'%s'\n", source, pattern,
47                         replacement);
48         */
49
50         if( source == NULL || pattern == NULL ) {
51                 debug_print("RSSyl: source or pattern is NULL!!!\n");
52                 return source;
53         }
54
55         if( !g_utf8_validate(source, -1, NULL) ) {
56                 debug_print("RSSyl: source is not an UTF-8 encoded text\n");
57                 return source;
58         }
59
60         if( !g_utf8_validate(pattern, -1, NULL) ) {
61                 debug_print("RSSyl: pattern is not an UTF-8 encoded text\n");
62                 return source;
63         }
64
65         len_pattern = strlen(pattern);
66         len_replacement = strlen(replacement);
67
68         c = source;
69         while( ( c = g_strstr_len(c, strlen(c), pattern) ) ) {
70                 count++;
71                 c += len_pattern;
72         }
73
74         /*
75         debug_print("RSSyl: ==== count = %d\n", count);
76         */
77
78         final_length = strlen(source)
79                 - ( count * len_pattern )
80                 + ( count * len_replacement );
81
82         new = malloc(final_length + 1);
83         memset(new, '\0', final_length + 1);
84
85         /* 'c' will be our iterator over original string
86          * 'w_new' our iterator over the new string */
87         c = source;
88         w_new = new;
89
90         /* Go until either end of string is reached, or until the
91          * remaining text is shorter than the pattern. */
92         while( *c != '\0' && strlen(c) <= len_pattern) {
93                 if( !memcmp(c, pattern, len_pattern) ) {
94                         int i;
95                         for (i = 0; i < len_replacement; i++) {
96                                 *w_new = replacement[i];
97                                 w_new++;
98                         }
99                         c = c + len_pattern;
100                 } else {
101                         *w_new = *c;
102                         w_new++;
103                         c++;
104                 }
105         }
106
107         /* We broke off the above cycle because remaining text was not
108          * long enough for the pattern, so now we need to append the
109          * remaining text to the new string. */
110         if (*c != '\0') {
111                 strncat(new, c, final_length - strlen(new));
112         }
113
114         return new;
115 }
116
117 typedef struct _RSSyl_HTMLSymbol RSSyl_HTMLSymbol;
118 struct _RSSyl_HTMLSymbol
119 {
120         gchar *const key;
121         gchar *const val;
122 };
123
124 static RSSyl_HTMLSymbol tag_list[] = {
125         { "<cite>", "\"" },
126         { "</cite>", "\"" },
127         { "<i>", "" },
128         { "</i>", "" },
129         { "<em>", "" },
130         { "</em>", "" },
131         { "<b>", "" },
132         { "</b>", "" },
133         { "<nobr>", "" },
134         { "</nobr>", "" },
135         { "<wbr>", "" },
136         { NULL, NULL }
137 };
138
139 static gchar *rssyl_replace_chrefs(gchar *string)
140 {
141         char *new = g_malloc0(strlen(string) + 1), *ret;
142         gchar *entity;
143         int i, ii;
144
145         /* &xx; */
146         ii = 0;
147         for (i = 0; i < strlen(string); ++i) {
148                 if (string[i] == '&') {
149                         entity = entity_decode(&(string[i]));
150                         if (entity != NULL) {
151                                 g_strlcat(new, entity, strlen(string));
152                                 ii += strlen(entity);
153                                 g_free(entity);
154                                 entity = NULL;
155                                 while (string[++i] != ';');
156                                 --i; /* loop will inc it again */
157                         } else {
158                                 new[ii++] = string[i];
159                         }
160                 } else {
161                         new[ii++] = string[i];
162                 }
163         }
164
165         ret = g_strdup(new);
166         g_free(new);
167         return ret;
168 }
169
170 gchar *rssyl_replace_html_stuff(gchar *text,
171                 gboolean symbols, gboolean tags)
172 {
173         gchar *tmp = NULL, *wtext = NULL;
174         gint i;
175
176         g_return_val_if_fail(text != NULL, NULL);
177
178         if( symbols ) {
179                 wtext = rssyl_replace_chrefs(text);
180         } else {
181                 wtext = g_strdup(text);
182         }
183
184         /* TODO: rewrite this part to work similarly to rssyl_replace_chrefs() */
185         if( tags ) {
186                 for( i = 0; tag_list[i].key != NULL; i++ ) {
187                         if( g_strstr_len(text, strlen(text), tag_list[i].key) ) {
188                                 tmp = rssyl_strreplace(wtext, tag_list[i].key, tag_list[i].val);
189                                 g_free(wtext);
190                                 wtext = g_strdup(tmp);
191                                 g_free(tmp);
192                         }
193                 }
194         }
195
196         return wtext;
197 }
198
199 static gchar *rssyl_sanitize_string(gchar *str, gboolean strip_nl)
200 {
201         gchar *new = NULL, *c = str, *n = NULL;
202
203         if( str == NULL )
204                 return NULL;
205
206         n = new = malloc(strlen(str) + 1);
207         memset(new, '\0', strlen(str) + 1);
208
209         while( *c != '\0' ) {
210                 if( !isspace(*c) || *c == ' ' || (!strip_nl && *c == '\n') ) {
211                         *n = *c;
212                         n++;
213                 }
214                 c++;
215         }
216
217         return new;
218 }
219
220 /* rssyl_format_string()
221  * - return value needs to be freed
222  */
223 gchar *rssyl_format_string(gchar *str, gboolean replace_html,
224                 gboolean strip_nl)
225 {
226         gchar *res = NULL, *tmp = NULL;
227
228         g_return_val_if_fail(str != NULL, NULL);
229
230         if (replace_html)
231                 tmp = rssyl_replace_html_stuff(str, TRUE, TRUE);
232         else
233                 tmp = g_strdup(str);
234
235         res = rssyl_sanitize_string(tmp, strip_nl);
236         g_free(tmp);
237
238         g_strstrip(res);
239
240         return res;
241 }
242
243 /* this functions splits a string into an array of string, by 
244  * returning an array of pointers to positions of the delimiter
245  * in the original string and replacing this delimiter with a
246  * NULL. It does not duplicate memory, hence you should only
247  * free the array and not its elements, and you should not
248  * free the original string before you're done with the array.
249  * maybe could be part of the core (utils.c).
250  */
251 gchar **strsplit_no_copy(gchar *str, char delimiter)
252 {
253         gchar **array = g_new(gchar *, 1);
254         int i = 0;
255         gchar *cur = str, *next;
256         
257         array[i] = cur;
258         i++;
259         while ((next = strchr(cur, delimiter)) != NULL) {
260                 *(next) = '\0';
261                 array = g_realloc(array, (sizeof(gchar *)) * (i + 1));
262                 array[i] = next + 1;
263                 cur = next + 1;
264                 i++;
265         }
266         array = g_realloc(array, (sizeof(gchar *)) * (i + 1));
267         array[i] = NULL;
268         return array;
269 }
270
271 /* This is a very dumb function - it just strips <, > and everything between
272  * them. */
273 void strip_html(gchar *str)
274 {
275         gchar *p = str;
276         gboolean intag = FALSE;
277
278         while (*p) {
279                 if (*p == '<')
280                         intag = TRUE;
281                 else if (*p == '>')
282                         intag = FALSE;
283
284                 if (*p == '<' || *p == '>' || intag)
285                         memmove(p, p + 1, strlen(p));
286                 else
287                         p++;
288         }
289 }
290
291 gchar *my_normalize_url(const gchar *url)
292 {
293         gchar *myurl = NULL;
294
295         if (!strncmp(url, "feed://", 7))
296                 myurl = g_strdup(url+7);
297         else if (!strncmp(url, "feed:", 5))
298                 myurl = g_strdup(url+5);
299         else
300                 myurl = g_strdup(url);
301
302         return g_strstrip(myurl);
303 }