1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /*
3  * Pan - A Newsreader for Gtk+
4  * Copyright (C) 2002-2006  Charles Kerr <charles@rebelbase.com>
5  *
6  * This program 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; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 #include <config.h>
21 #include <glib.h>
22 #include <glib/gi18n.h>
23 #include <pan/general/macros.h>
24 #include "filter-info.h"
25 
26 using namespace pan;
27 
28 /***
29 ****
30 ***/
31 
32 /*
33  * Copy-and-swap idiom according to
34  * http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom
35  */
36 
FilterInfo(const FilterInfo & that)37 FilterInfo :: FilterInfo (const FilterInfo &that)
38   : _type(that._type)
39   , _ge(that._ge)
40   , _header(that._header)
41   , _text(that._text)
42   , _negate(that._negate)
43   , _needs_body(that._needs_body)
44 {
45   foreach_const (aggregatesp_t, that._aggregates, it) {
46     _aggregates.push_back (new FilterInfo(**it));
47   }
48 }
49 
50 void
swap(FilterInfo & first,FilterInfo & second)51 swap (FilterInfo &first, FilterInfo &second)
52 {
53   using std::swap;
54 
55   swap (first._type,       second._type);
56   swap (first._ge,         second._ge);
57   swap (first._header,     second._header);
58   swap (first._text,       second._text);
59   swap (first._aggregates, second._aggregates);
60   swap (first._negate,     second._negate);
61   swap (first._needs_body, second._needs_body);
62 }
63 
operator =(FilterInfo other)64 FilterInfo &FilterInfo::operator = (FilterInfo other)
65 {
66   swap (*this, other);
67 
68   return *this;
69 }
70 
~FilterInfo()71 FilterInfo :: ~FilterInfo ()
72 {
73   foreach (aggregatesp_t, _aggregates, it) {
74     delete *it;
75   }
76 }
77 
78 void
clear()79 FilterInfo :: clear ()
80 {
81   _type = FilterInfo::TYPE_ERR;
82   _ge = 0;
83   _header.clear ();
84   _text.clear ();
85   foreach (aggregatesp_t, _aggregates, it) {
86     delete *it;
87   }
88   _aggregates.clear ();
89   _negate = false;
90   _needs_body = false;
91 }
92 
93 void
set_type_is(Type type)94 FilterInfo :: set_type_is (Type type) {
95    clear ();
96    _type = type;
97 }
98 void
set_type_ge(Type type,unsigned long ge)99 FilterInfo :: set_type_ge (Type type, unsigned long ge) {
100   clear ();
101   _type = type;
102   _ge = ge;
103 }
104 
105 void
set_type_le(Type type,unsigned long le)106 FilterInfo :: set_type_le (Type type, unsigned long le) {
107   clear ();
108   _type = type;
109   _negate = true;
110   _ge = le+1;  // le N == !ge N+1
111 }
112 
113 void
set_type_aggregate_and()114 FilterInfo :: set_type_aggregate_and () {
115    clear ();
116    _type = AGGREGATE_AND;
117 }
118 void
set_type_aggregate_or()119 FilterInfo :: set_type_aggregate_or () {
120    clear ();
121    _type = AGGREGATE_OR;
122 }
123 void
set_type_text(const Quark & header,const TextMatch::Description & text)124 FilterInfo :: set_type_text (const Quark                   & header,
125                              const TextMatch::Description  & text)
126 {
127   static const Quark subject("Subject"), from("From"),
128     xref("Xref"), references("References"), newsgroups("Newsgroups"),
129     message_Id("Message-Id"), message_ID("Message-ID");
130 
131   clear ();
132   _type = TEXT;
133   _header = header;
134   _text.set (text);
135 
136   if( !(header == subject || header == from || header == message_Id ||
137       header == message_ID || header ==  newsgroups || header == references ||
138       header ==  xref) )
139     _needs_body = true;
140 }
141 
142 /****
143 *****
144 ****/
145 
146 void
set_type_binary()147 FilterInfo :: set_type_binary ()
148 {
149    set_type_is (IS_BINARY);
150 }
151 void
set_type_byte_count_ge(unsigned long ge)152 FilterInfo :: set_type_byte_count_ge (unsigned long ge)
153 {
154    set_type_ge (BYTE_COUNT_GE, ge);
155 }
156 void
set_type_cached()157 FilterInfo :: set_type_cached ()
158 {
159    set_type_is (IS_CACHED);
160 }
161 void
set_type_crosspost_count_ge(unsigned long ge)162 FilterInfo :: set_type_crosspost_count_ge (unsigned long ge)
163 {
164    set_type_ge (CROSSPOST_COUNT_GE, ge);
165 }
166 void
set_type_days_old_ge(unsigned long ge)167 FilterInfo :: set_type_days_old_ge (unsigned long ge)
168 {
169    set_type_ge (DAYS_OLD_GE, ge);
170 }
171 void
set_type_days_old_le(unsigned long le)172 FilterInfo :: set_type_days_old_le (unsigned long le)
173 {
174    set_type_le (DAYS_OLD_GE, le);
175 }
176 void
set_type_line_count_ge(unsigned long ge)177 FilterInfo :: set_type_line_count_ge (unsigned long ge)
178 {
179    set_type_ge (LINE_COUNT_GE, ge);
180 }
181 void
set_type_score_ge(unsigned long ge)182 FilterInfo :: set_type_score_ge (unsigned long ge)
183 {
184    set_type_ge (SCORE_GE, ge);
185 }
186 void
set_type_score_le(unsigned long le)187 FilterInfo :: set_type_score_le (unsigned long le)
188 {
189    set_type_le (SCORE_GE, le);
190 }
191 void
set_type_is_read()192 FilterInfo :: set_type_is_read ()
193 {
194    set_type_is (IS_READ);
195 }
196 void
set_type_is_unread()197 FilterInfo :: set_type_is_unread ()
198 {
199    set_type_is (IS_UNREAD);
200 }
201 void
set_type_posted_by_me()202 FilterInfo :: set_type_posted_by_me ()
203 {
204    set_type_is (IS_POSTED_BY_ME);
205 }
206 
207 std::string
describe() const208 FilterInfo :: describe () const
209 {
210   std::string ret;
211   char buf[4096];
212 
213   if (_type==IS_BINARY && _negate)
214   {
215     ret = _("article doesn’t have attachments");
216   }
217   else if (_type==IS_BINARY)
218   {
219     ret = _("the article has attachments");
220   }
221   else if (_type==IS_CACHED && _negate)
222   {
223     ret = _("the article isn’t cached locally");
224   }
225   else if (_type==IS_CACHED)
226   {
227     ret = _("the article is cached locally");
228   }
229   else if (_type==IS_POSTED_BY_ME && _negate)
230   {
231     ret = _("the article wasn’t posted by you");
232   }
233   else if (_type==IS_POSTED_BY_ME)
234   {
235     ret = _("the article was posted by you");
236   }
237   else if (_type==IS_READ)
238   {
239     ret = _("the article has been read");
240   }
241   else if (_type==IS_UNREAD)
242   {
243     ret = _("the article hasn’t been read");
244   }
245   else if (_type==BYTE_COUNT_GE && _negate)
246   {
247     g_snprintf (buf, sizeof(buf), _("the article is less than %ld bytes long"), _ge);
248     ret = buf;
249   }
250   else if (_type==BYTE_COUNT_GE)
251   {
252     g_snprintf (buf, sizeof(buf), _("the article is at least %ld bytes long"), _ge);
253     ret = buf;
254   }
255   else if (_type==LINE_COUNT_GE && _negate)
256   {
257     g_snprintf (buf, sizeof(buf), _("the article is less than %ld lines long"), _ge);
258     ret = buf;
259   }
260   else if (_type==LINE_COUNT_GE)
261   {
262     g_snprintf (buf, sizeof(buf), _("the article is at least %ld lines long"), _ge);
263     ret = buf;
264   }
265   else if (_type==DAYS_OLD_GE && _negate)
266   {
267     g_snprintf (buf, sizeof(buf), _("the article is less than %ld days old"), _ge);
268     ret = buf;
269   }
270   else if (_type==DAYS_OLD_GE)
271   {
272     g_snprintf (buf, sizeof(buf), _("the article is at least %ld days old"), _ge);
273     ret = buf;
274   }
275   else if (_type==CROSSPOST_COUNT_GE && _negate)
276   {
277     g_snprintf (buf, sizeof(buf), _("the article was posted to less than %ld groups"), _ge);
278     ret = buf;
279   }
280   else if (_type==CROSSPOST_COUNT_GE)
281   {
282     g_snprintf (buf, sizeof(buf), _("the article was posted to at least %ld groups"), _ge);
283     ret = buf;
284   }
285   else if (_type==SCORE_GE && _negate)
286   {
287     g_snprintf (buf, sizeof(buf), _("the article’s score is less than %ld"), _ge);
288     ret = buf;
289   }
290   else if (_type==SCORE_GE)
291   {
292     g_snprintf (buf, sizeof(buf), _("the article’s score is %ld or higher"), _ge);
293     ret = buf;
294   }
295   else if (_type==TEXT && _negate)
296   {
297 #if 0
298     const char * h (_header.c_str());
299     const char * t (_text.get_state().text.c_str());
300     switch (_text.get_state().type) {
301       case TextMatch::CONTAINS:    g_snprintf (buf, sizeof(buf), _("%s doesn’t contain “%s”"), h, t); break;
302       case TextMatch::IS:          g_snprintf (buf, sizeof(buf), _("%s isn’t “%s”"), h, t); break;
303       case TextMatch::BEGINS_WITH: g_snprintf (buf, sizeof(buf), _("%s doesn’t begin with “%s”"), h, t); break;
304       case TextMatch::ENDS_WITH:   g_snprintf (buf, sizeof(buf), _("%s doesn’t end with “%s”"), h, t); break;
305       case TextMatch::REGEX:       g_snprintf (buf, sizeof(buf), _("%s doesn’t match the regex “%s”"), h, t); break;
306     }
307 #else
308     const char * h (_header.c_str());
309     const char * t (_text._impl_text.c_str());
310     switch (_text._impl_type) {
311       case TextMatch::CONTAINS:    g_snprintf (buf, sizeof(buf), _("%s doesn’t contain “%s”"), h, t); break;
312       case TextMatch::IS:          g_snprintf (buf, sizeof(buf), _("%s isn’t “%s”"), h, t); break;
313       case TextMatch::BEGINS_WITH: g_snprintf (buf, sizeof(buf), _("%s doesn’t begin with “%s”"), h, t); break;
314       case TextMatch::ENDS_WITH:   g_snprintf (buf, sizeof(buf), _("%s doesn’t end with “%s”"), h, t); break;
315       case TextMatch::REGEX:       g_snprintf (buf, sizeof(buf), _("%s doesn’t match the regex “%s”"), h, t); break;
316     }
317 #endif
318     ret = buf;
319   }
320   else if (_type==TEXT)
321   {
322     const char * h (_header.c_str());
323     //const char * t (_text.get_state().text.c_str());
324     //switch (_text.get_state().type) {
325     const char * t (_text._impl_text.c_str());
326     switch (_text._impl_type) {
327       case TextMatch::CONTAINS:    g_snprintf (buf, sizeof(buf), _("%s contains “%s”"), h, t); break;
328       case TextMatch::IS:          g_snprintf (buf, sizeof(buf), _("%s is “%s”"), h, t); break;
329       case TextMatch::BEGINS_WITH: g_snprintf (buf, sizeof(buf), _("%s begins with “%s”"), h, t); break;
330       case TextMatch::ENDS_WITH:   g_snprintf (buf, sizeof(buf), _("%s ends with “%s”"), h, t); break;
331       case TextMatch::REGEX:       g_snprintf (buf, sizeof(buf), _("%s matches the regex “%s”"), h, t); break;
332    }
333     ret = buf;
334   }
335   else if (_type==AGGREGATE_AND && _negate)
336   {
337     ret = _("Any of these tests fail:");
338     ret += "\n";
339     foreach_const (aggregatesp_t, _aggregates, it)
340       ret += "   " + (*it)->describe() + "\n";
341   }
342   else if (_type==AGGREGATE_AND)
343   {
344     ret = _("All of these tests pass:");
345     ret += "\n";
346     foreach_const (aggregatesp_t, _aggregates, it)
347       ret += "   " + (*it)->describe() + "\n";
348   }
349   else if (_type==AGGREGATE_OR && _negate)
350   {
351     ret = _("None of these tests pass:");
352     ret += "\n";
353     foreach_const (aggregatesp_t, _aggregates, it)
354       ret += "   " + (*it)->describe() + "\n";
355   }
356   else if (_type==AGGREGATE_OR)
357   {
358     ret = _("Any of these tests pass:");
359     ret += "\n";
360     foreach_const (aggregatesp_t, _aggregates, it)
361       ret += "   " + (*it)->describe() + "\n";
362   }
363 
364   return ret;
365 }
366