1 /*
2 * irssi - Implements several irssi features for Purple
3 * Copyright (C) 2005-2008 Gary Kramlich <grim@reaperworld.com>
4 * Copyright (C) 2006-2008 John Bailey <rekkanoryo@rekkanoryo.org>
5 * Copyright (C) 2006-2008 Sadrul Habib Chowdhury <sadrul@users.sourceforge.net>
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of the
10 * License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * 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., 51 Franklin Street, Fifth Floor, Boston, MA
20 * 02111-1301, USA.
21 */
22
23 /* If you can't figure out what this line is for, DON'T TOUCH IT. */
24 #include "../common/pp_internal.h"
25
26 #ifdef HAVE_REGEX_H
27 # include <regex.h>
28 #endif
29
30 #include <plugin.h>
31 #include <cmds.h>
32
33 #include "textfmt.h"
34
35 /******************************************************************************
36 * Globals
37 *****************************************************************************/
38 #define EXPRESSION "(^|[ ])(%s)([^ ]+)(%s)($|[ ])"
39
40 enum {
41 GROUP_ALL = 0,
42 GROUP_LEADING,
43 GROUP_START_TOKEN,
44 GROUP_TEXT,
45 GROUP_END_TOKEN,
46 GROUP_TRAILING,
47 GROUP_TOTAL
48 };
49
50 /******************************************************************************
51 * Helpers
52 *****************************************************************************/
53
54 #ifdef HAVE_REGEX_H
55
56 #define FORMAT(account, message) { \
57 if(!(message)) \
58 return FALSE; \
59 if(!(*(message))) \
60 return FALSE; \
61 if(!(account)) \
62 return FALSE; \
63 \
64 if(!(account)->gc) \
65 return FALSE; \
66 \
67 if(!((account)->gc->flags & PURPLE_CONNECTION_HTML)) \
68 return FALSE; \
69 \
70 if(!(message)) \
71 return FALSE; \
72 \
73 *(message) = irssi_textfmt_regex(*(message)); \
74 \
75 return FALSE; \
76 }
77
78 #define APPEND_GROUP(group) { \
79 t = iter + matches[(group)].rm_so; \
80 offset = matches[(group)].rm_eo - matches[(group)].rm_so; \
81 str = g_string_append_len(str, t, offset); \
82 }
83
84 static gchar *
irssi_textfmt_regex_helper(gchar * message,const gchar * token,const gchar * tag)85 irssi_textfmt_regex_helper(gchar *message, const gchar *token,
86 const gchar *tag)
87 {
88 GString *str = NULL;
89 gchar *ret = NULL, *expr = NULL, *iter = message, *t = NULL;
90 regex_t regex;
91 regmatch_t matches[GROUP_TOTAL];
92
93 /* build our expression */
94 expr = g_strdup_printf(EXPRESSION, token, token);
95
96 /* compile the expression.
97 * We may want to move this so we only do it once for each type, but this
98 * is pretty cheap, resource-wise.
99 */
100 if(regcomp(®ex, expr, REG_EXTENDED) != 0) {
101 g_free(expr);
102 return message;
103 }
104
105 /* ok, we compiled fine, lets clean up our expression */
106 g_free(expr);
107
108 /* now let's replce the matches.
109 * If we don't have any, drop out right away.
110 */
111 if(regexec(®ex, iter, GROUP_TOTAL, matches, 0) != 0) {
112 regfree(®ex);
113 return message;
114 }
115
116 /* create our GString. Heh heh */
117 str = g_string_new("");
118
119 /* now loop through the string looking for more matches */
120 do {
121 gint offset = 0;
122
123 if(matches[0].rm_eo == -1)
124 break;
125
126 /* append everything up to the match */
127 str = g_string_append_len(str, iter, matches[GROUP_ALL].rm_so);
128
129 /* determine the leading white space */
130 APPEND_GROUP(GROUP_LEADING);
131
132 /* append the starting tag */
133 g_string_append_printf(str, "<%s>", tag);
134
135 /* append the starting token */
136 APPEND_GROUP(GROUP_START_TOKEN);
137
138 /* append the text */
139 APPEND_GROUP(GROUP_TEXT);
140
141 /* append the ending token */
142 APPEND_GROUP(GROUP_END_TOKEN);
143
144 /* append the ending tag */
145 g_string_append_printf(str, "</%s>", tag);
146
147 /* append the trailing whitespace */
148 APPEND_GROUP(GROUP_TRAILING);
149
150 /* move iter to the end of the match */
151 iter += matches[GROUP_ALL].rm_eo;
152 } while(regexec(®ex, iter, GROUP_TOTAL, matches, REG_NOTBOL) == 0);
153
154 regfree(®ex);
155
156 /* at this point, iter is either the remains of the text, of a single null
157 * terminator. So throw it onto the GString.
158 */
159 str = g_string_append(str, iter);
160
161 /* kill the passed in message */
162 g_free(message);
163
164 /* take off our GString */
165 ret = str->str;
166 g_string_free(str, FALSE);
167
168 return ret;
169 }
170
171 static gchar *
irssi_textfmt_regex(gchar * message)172 irssi_textfmt_regex(gchar *message) {
173 message = irssi_textfmt_regex_helper(message, "\\*", "b");
174 message = irssi_textfmt_regex_helper(message, "/", "i");
175 message = irssi_textfmt_regex_helper(message, "-", "s");
176 message = irssi_textfmt_regex_helper(message, "_", "u");
177
178 #if 0
179 /* don't add these until imhtml support them again */
180 message = irssi_textfmt_regex_helper(message, "\\^", "sup");
181 message = irssi_textfmt_regex_helper(message, "~", "sub");
182 message = irssi_textfmt_regex_helper(message, "!", "pre");
183 #endif
184
185 return message;
186 }
187
188 #endif
189
190 /******************************************************************************
191 * Callbacks
192 *****************************************************************************/
193 #ifdef HAVE_REGEX_H
194
195 static gboolean
irssi_textfmt_writing_cb(PurpleAccount * account,const gchar * who,gchar ** message,PurpleConversation * conv,PurpleMessageFlags flags)196 irssi_textfmt_writing_cb(PurpleAccount *account, const gchar *who,
197 gchar **message, PurpleConversation *conv,
198 PurpleMessageFlags flags)
199 {
200 if(!(flags & (PURPLE_MESSAGE_SEND | PURPLE_MESSAGE_RECV)))
201 {
202 return FALSE;
203 }
204
205 FORMAT(account, message);
206 }
207
208 static gboolean
irssi_textfmt_sending_im_cb(PurpleAccount * account,const gchar * receiver,gchar ** message)209 irssi_textfmt_sending_im_cb(PurpleAccount *account, const gchar *receiver,
210 gchar **message)
211 {
212 FORMAT(account, message);
213 }
214
215 static gboolean
irssi_textfmt_sending_chat_cb(PurpleAccount * account,gchar ** message,gint id)216 irssi_textfmt_sending_chat_cb(PurpleAccount *account, gchar **message, gint id) {
217 FORMAT(account, message);
218 }
219
220 #endif
221
222 /******************************************************************************
223 * "API"
224 *****************************************************************************/
225 void
irssi_textfmt_init(PurplePlugin * plugin)226 irssi_textfmt_init(PurplePlugin *plugin) {
227 #ifdef HAVE_REGEX_H
228 void *handle;
229
230 handle = purple_conversations_get_handle();
231
232 purple_signal_connect(handle, "writing-im-msg", plugin,
233 PURPLE_CALLBACK(irssi_textfmt_writing_cb), NULL);
234 purple_signal_connect(handle, "writing-chat-msg", plugin,
235 PURPLE_CALLBACK(irssi_textfmt_writing_cb), NULL);
236 purple_signal_connect(handle, "sending-im-msg", plugin,
237 PURPLE_CALLBACK(irssi_textfmt_sending_im_cb), NULL);
238 purple_signal_connect(handle, "sending-chat-msg", plugin,
239 PURPLE_CALLBACK(irssi_textfmt_sending_chat_cb), NULL);
240 #endif
241 }
242
243 void
irssi_textfmt_uninit(PurplePlugin * plugin)244 irssi_textfmt_uninit(PurplePlugin *plugin) {
245 /* Nothing to do here, purple kills our callbacks for us. */
246 }
247
248