1 /*
2 etree.* - handles expression trees..
3 Copyright (C) 1999,2001-2004 Matthew Mueller <donut AT dakotacom.net>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
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, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 #include "etree.h"
20 #include "cache.h"
21 #include "grouplist.h"
22 #include "myregex.h"
23 #include "misc.h"
24 #include "nget.h"
25 #include "strreps.h"
26 #include "getter.h"
27 #include <stack>
28
29 #define MAKE_BINARY_OP(name,op) template <class T,class T2=T> \
30 struct Op_ ## name { \
31 bool operator()(const T v1,const T2 v2) const {return v1 op v2;} \
32 };
33 MAKE_BINARY_OP(gt,>);
34 MAKE_BINARY_OP(ge,>=);
35 MAKE_BINARY_OP(lt,<);
36 MAKE_BINARY_OP(le,<=);
37 MAKE_BINARY_OP(eq,==);
38 MAKE_BINARY_OP(ne,!=);
39
40 //use real operators here (rather than a predComparison with Op template) to take advantage of short-circuit evaluation.
41 template <class ClassType>
42 class predAnd : public pred<ClassType> {
43 private:
44 pred<ClassType> *p1, *p2;
45 public:
predAnd(pred<ClassType> * n1,pred<ClassType> * n2)46 predAnd(pred<ClassType> *n1, pred<ClassType> *n2):p1(n1), p2(n2) { }
~predAnd()47 virtual ~predAnd() {delete p1; delete p2;}
operator ()(ClassType * f) const48 virtual bool operator()(ClassType* f) const {
49 return (*p1)(f) && (*p2)(f);
50 }
51 };
52 template <class ClassType>
53 class predOr : public pred<ClassType> {
54 private:
55 pred<ClassType> *p1, *p2;
56 public:
predOr(pred<ClassType> * n1,pred<ClassType> * n2)57 predOr(pred<ClassType> *n1, pred<ClassType> *n2):p1(n1), p2(n2) { }
~predOr()58 virtual ~predOr() {delete p1; delete p2;}
operator ()(ClassType * f) const59 virtual bool operator()(ClassType* f) const {
60 return (*p1)(f) || (*p2)(f);
61 }
62 };
63
64 template <template <class A, class B> class Op, template <class D, class E> class Getter, class T, class ClassType, class T2=T>
65 class Comparison : public pred<ClassType> {
66 private:
67 typedef Getter<T, ClassType> getterT;
68 Op<typename getterT::T,T2> op;
69 getterT getter;
70 T2 v;
71 public:
Comparison(typename getterT::init_t gette,const T2 v2)72 Comparison(typename getterT::init_t gette, const T2 v2):getter(gette), v(v2){}
operator ()(ClassType * f) const73 virtual bool operator()(ClassType* f) const {
74 return op(getter(f), v);
75 }
76 };
77 template <template <class A, class B> class Op, class ClassType, class RetType>
new_comparison(RetType (ClassType::* member),RetType v)78 pred<ClassType> *new_comparison(RetType (ClassType::*member), RetType v){
79 return new Comparison<Op, MemGetter, RetType, ClassType>(member, v);
80 }
81 template <template <class A, class B> class Op, class ClassType, class RetType>
new_comparison(RetType (ClassType::* memberf)(void)const,RetType v)82 pred<ClassType> *new_comparison(RetType (ClassType::*memberf)(void) const, RetType v){
83 return new Comparison<Op, MemfuncGetter, RetType, ClassType>(memberf, v);
84 }
85 template <class ClassType, class getterT, class T2>
comparison(const string & opstr,getterT get,T2 v)86 pred<ClassType> *comparison(const string &opstr, getterT get, T2 v) {
87 if (opstr.compare("==")==0) return new_comparison<Op_eq,ClassType>(get, v);
88 else if (opstr.compare("!=")==0) return new_comparison<Op_ne,ClassType>(get, v);
89 else if (opstr.compare("<")==0) return new_comparison<Op_lt,ClassType>(get, v);
90 else if (opstr.compare("<=")==0) return new_comparison<Op_le,ClassType>(get, v);
91 else if (opstr.compare(">")==0) return new_comparison<Op_gt,ClassType>(get, v);
92 else if (opstr.compare(">=")==0) return new_comparison<Op_ge,ClassType>(get, v);
93 throw UserExFatal(Ex_INIT, "invalid op %s for comparison", opstr.c_str());
94 }
95
96 //template <template <class A, class B> class Op, template <class D, class E, class F> class Getter, class getterT, class T>
97 template <template <class A, class B> class Op, template <class D, class E> class Getter, class T, class ClassType>
98 class Comparison_re : public pred<ClassType> {
99 private:
100 typedef Getter<T, ClassType> getterT;
101 Op<typename getterT::T,const c_regex_nosub&> op;
102 getterT getter;
103 c_regex_nosub re;
104 public:
Comparison_re(typename getterT::init_t gette,const char * pattern,int flags)105 Comparison_re(typename getterT::init_t gette, const char *pattern, int flags):getter(gette), re(pattern, flags){}
operator ()(ClassType * f) const106 virtual bool operator()(ClassType* f) const {
107 return op(getter(f), re);
108 }
109 };
110 template <template <class A, class B> class Op, class ClassType, class RetType>
new_comparison_re(RetType (ClassType::* member),const char * pattern,int flags)111 pred<ClassType> *new_comparison_re(RetType (ClassType::*member), const char *pattern, int flags){
112 return new Comparison_re<Op, MemGetter, RetType, ClassType>(member, pattern, flags);
113 }
114 template <template <class A, class B> class Op, class ClassType, class RetType>
new_comparison_re(RetType (ClassType::* memberf)(void)const,const char * pattern,int flags)115 pred<ClassType> *new_comparison_re(RetType (ClassType::*memberf)(void) const, const char *pattern, int flags){
116 return new Comparison_re<Op, MemfuncGetter, RetType, ClassType>(memberf, pattern, flags);
117 }
118 template <class ClassType, class getterT>
comparison_re(const string & opstr,getterT get,const char * pattern,int flags)119 pred<ClassType> *comparison_re(const string &opstr, getterT get, const char *pattern, int flags) {
120 //typedef const string &T;
121 if (opstr.compare("==")==0 || opstr.compare("=~")==0)
122 return new_comparison_re<Op_eq,ClassType>(get, pattern, flags);
123 //return new Comparison_re<Op_eq,Getter,const getterT,T>(get, pattern, flags);
124 else if (opstr.compare("!=")==0 || opstr.compare("!~")==0)
125 return new_comparison_re<Op_ne,ClassType>(get, pattern, flags);
126 //return new Comparison_re<Op_ne,Getter,const getterT,T>(get, pattern, flags);
127 throw UserExFatal(Ex_INIT, "invalid op %s for comparison_re", opstr.c_str());
128 }
129
invert_op(string o)130 string invert_op(string o) {
131 if (o == "<") return ">";
132 else if (o == "<=") return ">=";
133 else if (o == ">") return "<";
134 else if (o == ">=") return "<=";
135 else return o;
136 }
137
138
139 template <class ClassType, pred<ClassType> *(*comparison_maker)(const string &i, const string *x, const string *y, int re_flags)>
make_pred(const arglist_t & e_parts,int gflags)140 pred<ClassType> * make_pred(const arglist_t &e_parts, int gflags) {
141 const string *x=NULL,*y=NULL;
142 arglist_t::const_iterator i=e_parts.begin();
143 int re_flags = REG_EXTENDED | ((gflags&GETFILES_CASESENSITIVE)?0:REG_ICASE);
144 pred<ClassType> * p=NULL;
145 stack<pred<ClassType> *> pstack;
146 for (;i!=e_parts.end();++i){
147 if (!x){
148 PDEBUG(DEBUG_MIN,"x %s",(*i).c_str());
149 x=&(*i);
150 if (*x=="&&" || *x=="and" || *x=="||" || *x=="or"){
151 if (pstack.size()<2)
152 throw UserExFatal(Ex_INIT, "not enough arguments for %s", x->c_str());
153 pred<ClassType> *py=pstack.top(); pstack.pop();
154 pred<ClassType> *px=pstack.top(); pstack.pop();
155 if (x->compare("&&")==0 || x->compare("and")==0)
156 p = new predAnd<ClassType>(px, py);
157 else
158 p = new predOr<ClassType>(px, py);
159 pstack.push(p);
160 x=NULL;
161 }
162 }else if (!y){
163 PDEBUG(DEBUG_MIN,"y %s",(*i).c_str());
164 y=&(*i);
165 } else {
166 PDEBUG(DEBUG_MIN,"z %s",(*i).c_str());
167 p = comparison_maker((*i), x, y, re_flags);
168 if (!p)
169 throw UserExFatal(Ex_INIT, "no match type %s", x->c_str());
170 pstack.push(p);
171 x=y=NULL;
172 }
173 }
174 if (pstack.size()>1 || x || y){
175 throw UserExFatal(Ex_INIT, "unfinished expression");
176 }
177 if (pstack.empty())
178 throw UserExFatal(Ex_INIT, "empty expression");
179 return pstack.top();
180 }
181
operator ==(const t_references & a,const c_regex_nosub & r)182 inline bool operator == (const t_references &a, const c_regex_nosub &r) {
183 for (t_references::const_iterator i = a.begin(); i!=a.end(); ++i)
184 if (*i == r)
185 return true;
186 return false;
187 }
operator !=(const t_references & a,const c_regex_nosub & r)188 inline bool operator != (const t_references &a, const c_regex_nosub &r) {
189 return (!(a==r));
190 }
nntp_file_comparison_maker(const string & i,const string * x,const string * y,int re_flags)191 nntp_file_pred *nntp_file_comparison_maker(const string &i, const string *x, const string *y, int re_flags) {
192 const char *n = x->c_str();
193 if (strcasecmp(n, "subject")==0)
194 return comparison_re<const c_nntp_file,string c_nntp_file::*>(i, &c_nntp_file::subject, y->c_str(), re_flags);
195 else if (strcasecmp(n, "author")==0)
196 return comparison_re<const c_nntp_file,string c_nntp_file::*>(i, &c_nntp_file::author, y->c_str(), re_flags);
197 else if (strcasecmp(n, "mid")==0 || strcasecmp(n, "messageid")==0)
198 return comparison_re<const c_nntp_file>(i, &c_nntp_file::bamid, y->c_str(), re_flags);
199 else if (strcasecmp(n, "bytes")==0)
200 return comparison<const c_nntp_file>(i, &c_nntp_file::bytes, atoul(y->c_str()));
201 else if (strcasecmp(n, "lines")==0)
202 return comparison<const c_nntp_file>(i, &c_nntp_file::lines, atoul(y->c_str()));
203 else if (strcasecmp(n, "req")==0)
204 return comparison<const c_nntp_file,int c_nntp_file::*>(i, &c_nntp_file::req, atoi(y->c_str()));
205 else if (strcasecmp(n, "have")==0)
206 return comparison<const c_nntp_file>(i, &c_nntp_file::have, atoi(y->c_str()));
207 else if (strcasecmp(n, "date")==0)
208 return comparison<const c_nntp_file>(i, &c_nntp_file::badate, decode_textdate(y->c_str()));
209 else if (strcasecmp(n, "age")==0)
210 //rather than taking a relative age and converting each date into a relative age and then comparing, decode_textage returns an absolute date (time_t) which simplifies comparisons, but to get intuitive usage we have to invert <,> operators.
211 return comparison<const c_nntp_file>(invert_op(i), &c_nntp_file::badate, decode_textage(y->c_str()));
212 else if (strcasecmp(n, "update")==0)
213 return comparison<const c_nntp_file>(i, &c_nntp_file::update, decode_textdate(y->c_str()));
214 else if (strcasecmp(n, "updateage")==0)
215 return comparison<const c_nntp_file>(invert_op(i), &c_nntp_file::update, decode_textage(y->c_str()));
216 else if (strcasecmp(n, "references")==0)
217 return comparison_re<const c_nntp_file,t_references c_nntp_file::*>(i, &c_nntp_file::references, y->c_str(), re_flags);
218 else
219 return NULL;
220 }
221
make_nntpfile_pred(const arglist_t & e_parts,int gflags)222 nntp_file_pred * make_nntpfile_pred(const arglist_t &e_parts, int gflags) {
223 return make_pred<const c_nntp_file, nntp_file_comparison_maker>(e_parts, gflags);
224 }
make_nntpfile_pred(const char * optarg,int gflags)225 nntp_file_pred * make_nntpfile_pred(const char *optarg, int gflags) {
226 arglist_t e_parts;
227 parseargs(e_parts, optarg);
228 return make_nntpfile_pred(e_parts, gflags);
229 }
230
231 //Somewhat hacky way to allow desc keyword. Works quite nicely, but this method won't be usable if more than a single attribute of the class needs to be tested.
operator ==(const t_server_group_description_map & m,const c_regex_nosub & r)232 inline bool operator == (const t_server_group_description_map &m, const c_regex_nosub &r) {
233 for (t_server_group_description_map::const_iterator i = m.begin(); i!=m.end(); ++i)
234 if (i->second->description == r)
235 return true;
236 return false;
237 }
operator !=(const t_server_group_description_map & m,const c_regex_nosub & r)238 inline bool operator != (const t_server_group_description_map &m, const c_regex_nosub &r) {
239 return (!(m==r));
240 }
grouplist_comparison_maker(const string & i,const string * x,const string * y,int re_flags)241 nntp_grouplist_pred *grouplist_comparison_maker(const string &i, const string *x, const string *y, int re_flags) {
242 const char *n = x->c_str();
243 if (strcasecmp(n, "group")==0)
244 return comparison_re<const c_group_availability>(i, &c_group_availability::groupname, y->c_str(), re_flags);
245 else if (strcasecmp(n, "desc")==0)
246 return comparison_re<const c_group_availability>(i, &c_group_availability::servergroups, y->c_str(), re_flags);
247 else
248 return NULL;
249 }
250
make_grouplist_pred(const arglist_t & e_parts,int gflags)251 nntp_grouplist_pred * make_grouplist_pred(const arglist_t &e_parts, int gflags) {
252 return make_pred<const c_group_availability, grouplist_comparison_maker>(e_parts, gflags);
253 }
make_grouplist_pred(const char * optarg,int gflags)254 nntp_grouplist_pred * make_grouplist_pred(const char *optarg, int gflags) {
255 arglist_t e_parts;
256 parseargs(e_parts, optarg);
257 return make_grouplist_pred(e_parts, gflags);
258 }
259
260