PageRenderTime 26ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/src/gui/gui-filter.c

#
C | 551 lines | 393 code | 78 blank | 80 comment | 92 complexity | 37f2c3010e90b136863650e962ad27f2 MD5 | raw file
Possible License(s): GPL-3.0
  1. /*
  2. * Copyright (C) 2003-2012 Sebastien Helleu <flashcode@flashtux.org>
  3. *
  4. * This file is part of WeeChat, the extensible chat client.
  5. *
  6. * WeeChat is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * WeeChat 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 General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with WeeChat. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. /*
  20. * gui-filter.c: filter functions (used by all GUI)
  21. */
  22. #ifdef HAVE_CONFIG_H
  23. #include "config.h"
  24. #endif
  25. #include <stdlib.h>
  26. #include <stddef.h>
  27. #include <string.h>
  28. #include <regex.h>
  29. #include "../core/weechat.h"
  30. #include "../core/wee-config.h"
  31. #include "../core/wee-hdata.h"
  32. #include "../core/wee-hook.h"
  33. #include "../core/wee-infolist.h"
  34. #include "../core/wee-log.h"
  35. #include "../core/wee-string.h"
  36. #include "../plugins/plugin.h"
  37. #include "gui-filter.h"
  38. #include "gui-buffer.h"
  39. #include "gui-line.h"
  40. struct t_gui_filter *gui_filters = NULL; /* first filter */
  41. struct t_gui_filter *last_gui_filter = NULL; /* last filter */
  42. int gui_filters_enabled = 1; /* filters enabled? */
  43. /*
  44. * gui_filter_line_has_tag_no_filter: return 1 if line has tag "no_filter",
  45. * which means that line should never
  46. * been filtered (always displayed)
  47. */
  48. int
  49. gui_filter_line_has_tag_no_filter (struct t_gui_line *line)
  50. {
  51. int i;
  52. for (i = 0; i < line->data->tags_count; i++)
  53. {
  54. if (strcmp (line->data->tags_array[i], GUI_FILTER_TAG_NO_FILTER) == 0)
  55. return 1;
  56. }
  57. /* tag not found, line may be filtered */
  58. return 0;
  59. }
  60. /*
  61. * gui_filter_check_line: return 1 if a line should be displayed, or
  62. * 0 if line is hidden (tag or regex found)
  63. */
  64. int
  65. gui_filter_check_line (struct t_gui_line *line)
  66. {
  67. struct t_gui_filter *ptr_filter;
  68. int rc;
  69. /* line is always displayed if filters are disabled */
  70. if (!gui_filters_enabled)
  71. return 1;
  72. if (gui_filter_line_has_tag_no_filter (line))
  73. return 1;
  74. for (ptr_filter = gui_filters; ptr_filter;
  75. ptr_filter = ptr_filter->next_filter)
  76. {
  77. if (ptr_filter->enabled)
  78. {
  79. /* check buffer */
  80. if (gui_buffer_match_list_split (line->data->buffer,
  81. ptr_filter->num_buffers,
  82. ptr_filter->buffers))
  83. {
  84. if ((strcmp (ptr_filter->tags, "*") == 0)
  85. || (gui_line_match_tags (line,
  86. ptr_filter->tags_count,
  87. ptr_filter->tags_array)))
  88. {
  89. /* check line with regex */
  90. rc = 1;
  91. if (!ptr_filter->regex_prefix && !ptr_filter->regex_message)
  92. rc = 0;
  93. if (gui_line_match_regex (line,
  94. ptr_filter->regex_prefix,
  95. ptr_filter->regex_message))
  96. {
  97. rc = 0;
  98. }
  99. if (ptr_filter->regex && (ptr_filter->regex[0] == '!'))
  100. rc ^= 1;
  101. if (rc == 0)
  102. return 0;
  103. }
  104. }
  105. }
  106. }
  107. /* no tag or regex matching, then line is displayed */
  108. return 1;
  109. }
  110. /*
  111. * gui_filter_buffer: filter a buffer, using message filters
  112. */
  113. void
  114. gui_filter_buffer (struct t_gui_buffer *buffer)
  115. {
  116. struct t_gui_line *ptr_line;
  117. int line_displayed, lines_hidden;
  118. lines_hidden = 0;
  119. buffer->lines->prefix_max_length = CONFIG_INTEGER(config_look_prefix_align_min);
  120. for (ptr_line = buffer->lines->first_line; ptr_line;
  121. ptr_line = ptr_line->next_line)
  122. {
  123. line_displayed = gui_filter_check_line (ptr_line);
  124. if (line_displayed
  125. && (ptr_line->data->prefix_length > buffer->lines->prefix_max_length))
  126. {
  127. buffer->lines->prefix_max_length = ptr_line->data->prefix_length;
  128. }
  129. /* force chat refresh if at least one line changed */
  130. if (ptr_line->data->displayed != line_displayed)
  131. gui_buffer_ask_chat_refresh (buffer, 2);
  132. ptr_line->data->displayed = line_displayed;
  133. if (!line_displayed)
  134. lines_hidden = 1;
  135. }
  136. if (buffer->lines->lines_hidden != lines_hidden)
  137. {
  138. buffer->lines->lines_hidden = lines_hidden;
  139. hook_signal_send ("buffer_lines_hidden",
  140. WEECHAT_HOOK_SIGNAL_POINTER, buffer);
  141. }
  142. }
  143. /*
  144. * gui_filter_all_buffers: filter all buffers, using message filters
  145. */
  146. void
  147. gui_filter_all_buffers ()
  148. {
  149. struct t_gui_buffer *ptr_buffer;
  150. for (ptr_buffer = gui_buffers; ptr_buffer;
  151. ptr_buffer = ptr_buffer->next_buffer)
  152. {
  153. gui_filter_buffer (ptr_buffer);
  154. }
  155. }
  156. /*
  157. * gui_filter_global_enable: enable message filtering
  158. */
  159. void
  160. gui_filter_global_enable ()
  161. {
  162. if (!gui_filters_enabled)
  163. {
  164. gui_filters_enabled = 1;
  165. gui_filter_all_buffers ();
  166. hook_signal_send ("filters_enabled",
  167. WEECHAT_HOOK_SIGNAL_STRING, NULL);
  168. }
  169. }
  170. /*
  171. * gui_filter_global_disable: disable message filtering
  172. */
  173. void
  174. gui_filter_global_disable ()
  175. {
  176. if (gui_filters_enabled)
  177. {
  178. gui_filters_enabled = 0;
  179. gui_filter_all_buffers ();
  180. hook_signal_send ("filters_disabled",
  181. WEECHAT_HOOK_SIGNAL_STRING, NULL);
  182. }
  183. }
  184. /*
  185. * gui_filter_search_by_name: search a filter by name
  186. */
  187. struct t_gui_filter *
  188. gui_filter_search_by_name (const char *name)
  189. {
  190. struct t_gui_filter *ptr_filter;
  191. for (ptr_filter = gui_filters; ptr_filter;
  192. ptr_filter = ptr_filter->next_filter)
  193. {
  194. if (strcmp (ptr_filter->name, name) == 0)
  195. return ptr_filter;
  196. }
  197. /* filter not found */
  198. return NULL;
  199. }
  200. /*
  201. * gui_filter_new: create a new filter
  202. */
  203. struct t_gui_filter *
  204. gui_filter_new (int enabled, const char *name, const char *buffer_name,
  205. const char *tags, const char *regex)
  206. {
  207. struct t_gui_filter *new_filter;
  208. regex_t *regex1, *regex2;
  209. char *pos_tab, *regex_prefix;
  210. const char *ptr_start_regex, *pos_regex_message;
  211. if (!name || !buffer_name || !tags || !regex)
  212. return NULL;
  213. if (gui_filter_search_by_name (name))
  214. return NULL;
  215. ptr_start_regex = regex;
  216. if ((ptr_start_regex[0] == '!')
  217. || ((ptr_start_regex[0] == '\\') && (ptr_start_regex[1] == '!')))
  218. {
  219. ptr_start_regex++;
  220. }
  221. regex1 = NULL;
  222. regex2 = NULL;
  223. if (strcmp (ptr_start_regex, "*") != 0)
  224. {
  225. pos_tab = strstr (ptr_start_regex, "\\t");
  226. if (pos_tab)
  227. {
  228. regex_prefix = string_strndup (ptr_start_regex,
  229. pos_tab - ptr_start_regex);
  230. pos_regex_message = pos_tab + 2;
  231. }
  232. else
  233. {
  234. regex_prefix = NULL;
  235. pos_regex_message = ptr_start_regex;
  236. }
  237. if (regex_prefix)
  238. {
  239. regex1 = malloc (sizeof (*regex1));
  240. if (regex1)
  241. {
  242. if (string_regcomp (regex1, regex_prefix,
  243. REG_EXTENDED | REG_ICASE | REG_NOSUB) != 0)
  244. {
  245. free (regex_prefix);
  246. free (regex1);
  247. return NULL;
  248. }
  249. }
  250. }
  251. regex2 = malloc (sizeof (*regex2));
  252. if (regex2)
  253. {
  254. if (string_regcomp (regex2, pos_regex_message,
  255. REG_EXTENDED | REG_ICASE | REG_NOSUB) != 0)
  256. {
  257. if (regex_prefix)
  258. free (regex_prefix);
  259. if (regex1)
  260. free (regex1);
  261. free (regex2);
  262. return NULL;
  263. }
  264. }
  265. if (regex_prefix)
  266. free (regex_prefix);
  267. }
  268. /* create new filter */
  269. new_filter = malloc (sizeof (*new_filter));
  270. if (new_filter)
  271. {
  272. /* init filter */
  273. new_filter->enabled = enabled;
  274. new_filter->name = strdup (name);
  275. new_filter->buffer_name = strdup ((buffer_name) ? buffer_name : "*");
  276. new_filter->buffers = string_split (new_filter->buffer_name,
  277. ",", 0, 0,
  278. &new_filter->num_buffers);
  279. if (tags)
  280. {
  281. new_filter->tags = (tags) ? strdup (tags) : NULL;
  282. new_filter->tags_array = string_split (tags, ",", 0, 0,
  283. &new_filter->tags_count);
  284. }
  285. else
  286. {
  287. new_filter->tags = NULL;
  288. new_filter->tags_count = 0;
  289. new_filter->tags_array = NULL;
  290. }
  291. new_filter->regex = strdup (regex);
  292. new_filter->regex_prefix = regex1;
  293. new_filter->regex_message = regex2;
  294. /* add filter to filters list */
  295. new_filter->prev_filter = last_gui_filter;
  296. if (gui_filters)
  297. last_gui_filter->next_filter = new_filter;
  298. else
  299. gui_filters = new_filter;
  300. last_gui_filter = new_filter;
  301. new_filter->next_filter = NULL;
  302. hook_signal_send ("filter_added",
  303. WEECHAT_HOOK_SIGNAL_POINTER, new_filter);
  304. }
  305. return new_filter;
  306. }
  307. /*
  308. * gui_filter_rename: rename a filter
  309. */
  310. int
  311. gui_filter_rename (struct t_gui_filter *filter, const char *new_name)
  312. {
  313. if (!filter || !new_name)
  314. return 0;
  315. if (gui_filter_search_by_name (new_name))
  316. return 0;
  317. free (filter->name);
  318. filter->name = strdup (new_name);
  319. return 1;
  320. }
  321. /*
  322. * gui_filter_free: remove a filter
  323. */
  324. void
  325. gui_filter_free (struct t_gui_filter *filter)
  326. {
  327. hook_signal_send ("filter_removing",
  328. WEECHAT_HOOK_SIGNAL_POINTER, filter);
  329. /* free data */
  330. if (filter->name)
  331. free (filter->name);
  332. if (filter->buffer_name)
  333. free (filter->buffer_name);
  334. if (filter->buffers)
  335. string_free_split (filter->buffers);
  336. if (filter->tags)
  337. free (filter->tags);
  338. if (filter->tags_array)
  339. string_free_split (filter->tags_array);
  340. if (filter->regex)
  341. free (filter->regex);
  342. if (filter->regex_prefix)
  343. {
  344. regfree (filter->regex_prefix);
  345. free (filter->regex_prefix);
  346. }
  347. if (filter->regex_message)
  348. {
  349. regfree (filter->regex_message);
  350. free (filter->regex_message);
  351. }
  352. /* remove filter from filters list */
  353. if (filter->prev_filter)
  354. (filter->prev_filter)->next_filter = filter->next_filter;
  355. if (filter->next_filter)
  356. (filter->next_filter)->prev_filter = filter->prev_filter;
  357. if (gui_filters == filter)
  358. gui_filters = filter->next_filter;
  359. if (last_gui_filter == filter)
  360. last_gui_filter = filter->prev_filter;
  361. free (filter);
  362. hook_signal_send ("filter_removed", WEECHAT_HOOK_SIGNAL_STRING, NULL);
  363. }
  364. /*
  365. * gui_filter_free_all: remove all filters
  366. */
  367. void
  368. gui_filter_free_all ()
  369. {
  370. while (gui_filters)
  371. {
  372. gui_filter_free (gui_filters);
  373. }
  374. }
  375. /*
  376. * gui_filter_hdata_filter_cb: return hdata for filter
  377. */
  378. struct t_hdata *
  379. gui_filter_hdata_filter_cb (void *data, const char *hdata_name)
  380. {
  381. struct t_hdata *hdata;
  382. /* make C compiler happy */
  383. (void) data;
  384. hdata = hdata_new (NULL, hdata_name, "prev_filter", "next_filter");
  385. if (hdata)
  386. {
  387. HDATA_VAR(struct t_gui_filter, enabled, INTEGER, NULL);
  388. HDATA_VAR(struct t_gui_filter, name, STRING, NULL);
  389. HDATA_VAR(struct t_gui_filter, buffer_name, STRING, NULL);
  390. HDATA_VAR(struct t_gui_filter, num_buffers, INTEGER, NULL);
  391. HDATA_VAR(struct t_gui_filter, buffers, POINTER, NULL);
  392. HDATA_VAR(struct t_gui_filter, tags, STRING, NULL);
  393. HDATA_VAR(struct t_gui_filter, tags_count, INTEGER, NULL);
  394. HDATA_VAR(struct t_gui_filter, tags_array, POINTER, NULL);
  395. HDATA_VAR(struct t_gui_filter, regex, STRING, NULL);
  396. HDATA_VAR(struct t_gui_filter, regex_prefix, POINTER, NULL);
  397. HDATA_VAR(struct t_gui_filter, regex_message, POINTER, NULL);
  398. HDATA_VAR(struct t_gui_filter, prev_filter, POINTER, hdata_name);
  399. HDATA_VAR(struct t_gui_filter, next_filter, POINTER, hdata_name);
  400. HDATA_LIST(gui_filters);
  401. HDATA_LIST(last_gui_filter);
  402. }
  403. return hdata;
  404. }
  405. /*
  406. * gui_filter_add_to_infolist: add a filter in an infolist
  407. * return 1 if ok, 0 if error
  408. */
  409. int
  410. gui_filter_add_to_infolist (struct t_infolist *infolist,
  411. struct t_gui_filter *filter)
  412. {
  413. struct t_infolist_item *ptr_item;
  414. char option_name[64];
  415. int i;
  416. if (!infolist || !filter)
  417. return 0;
  418. ptr_item = infolist_new_item (infolist);
  419. if (!ptr_item)
  420. return 0;
  421. if (!infolist_new_var_integer (ptr_item, "enabled", filter->enabled))
  422. return 0;
  423. if (!infolist_new_var_string (ptr_item, "name", filter->name))
  424. return 0;
  425. if (!infolist_new_var_string (ptr_item, "buffer_name", filter->buffer_name))
  426. return 0;
  427. if (!infolist_new_var_string (ptr_item, "tags", filter->tags))
  428. return 0;
  429. if (!infolist_new_var_integer (ptr_item, "tags_count", filter->tags_count))
  430. return 0;
  431. for (i = 0; i < filter->tags_count; i++)
  432. {
  433. snprintf (option_name, sizeof (option_name), "tag_%05d", i + 1);
  434. if (!infolist_new_var_string (ptr_item, option_name,
  435. filter->tags_array[i]))
  436. return 0;
  437. }
  438. if (!infolist_new_var_string (ptr_item, "regex", filter->regex))
  439. return 0;
  440. return 1;
  441. }
  442. /*
  443. * gui_filter_print_log: print filter infos in log (usually for crash dump)
  444. */
  445. void
  446. gui_filter_print_log ()
  447. {
  448. struct t_gui_filter *ptr_filter;
  449. int i;
  450. log_printf ("");
  451. log_printf ("gui_filters_enabled = %d", gui_filters_enabled);
  452. for (ptr_filter = gui_filters; ptr_filter;
  453. ptr_filter = ptr_filter->next_filter)
  454. {
  455. log_printf ("");
  456. log_printf ("[filter (addr:0x%lx)]", ptr_filter);
  457. log_printf (" enabled. . . . . . . . : %d", ptr_filter->enabled);
  458. log_printf (" name . . . . . . . . . : '%s'", ptr_filter->name);
  459. log_printf (" buffer_name. . . . . . : '%s'", ptr_filter->buffer_name);
  460. log_printf (" num_buffers. . . . . . : %d", ptr_filter->num_buffers);
  461. log_printf (" buffers. . . . . . . . : 0x%lx", ptr_filter->buffers);
  462. for (i = 0; i < ptr_filter->num_buffers; i++)
  463. {
  464. log_printf (" buffers[%03d] . . . . . : '%s'", i, ptr_filter->buffers[i]);
  465. }
  466. log_printf (" tags . . . . . . . . . : '%s'", ptr_filter->tags);
  467. log_printf (" regex. . . . . . . . . : '%s'", ptr_filter->regex);
  468. log_printf (" regex_prefix . . . . . : 0x%lx", ptr_filter->regex_prefix);
  469. log_printf (" regex_message. . . . . : 0x%lx", ptr_filter->regex_message);
  470. log_printf (" prev_filter. . . . . . : 0x%lx", ptr_filter->prev_filter);
  471. log_printf (" next_filter. . . . . . : 0x%lx", ptr_filter->next_filter);
  472. }
  473. }