* New upstream release.
[debian/mimetic.git] / examples / search.cxx
1 /***************************************************************************
2     copyright            : (C) 2002-2005 by Stefano Barbato
3     email                : stefano@codesink.org
4
5     $Id: search.cxx,v 1.2 2005/02/23 10:26:14 tat Exp $
6  ***************************************************************************/
7
8 /***************************************************************************
9  *                                                                         *
10  *   This program is free software; you can redistribute it and/or modify  *
11  *   it under the terms of the GNU General Public License as published by  *
12  *   the Free Software Foundation; either version 2 of the License, or     *
13  *   (at your option) any later version.                                   *
14  *                                                                         *
15  ***************************************************************************/
16 /** \example search.cc
17  *  extract a Part based on command line parameters
18  *  more info on:
19  *      search -h
20  */ 
21 #include <iostream>
22 #include <sstream>
23 #include <iterator>
24 #include <fstream>
25 #include <cassert>
26 #include <mimetic/mimetic.h>
27
28 using namespace std;
29 using namespace mimetic;
30
31 unsigned int g_matches = 0;
32
33 void usage()
34 {
35     cout << "search [params] [in_file...]" << endl;
36     cout << "\t-I filename       use index (disables '-r')" << endl;
37     cout << "\t-r                looks for matches in nested entities"<<endl;
38     cout << "\t-t type[/subtype] matches Content-Type" << endl;
39     cout << "\t-p param[=value]  matches Content-Type param" << endl;
40     cout << "\t-f name[=value]   matches an header field" << endl;
41     cout << "\t-l                doesn't write anything but filename" << endl;
42     cout << "\t-q                totaly quiet; exit code = num of matches" << endl;
43     exit(-1);
44 }
45
46
47 struct MatchParamRq
48 {
49     typedef pair<istring,istring> Param;
50     typedef pair<istring,istring> Field;
51     MatchParamRq()
52     : recursive(0), quiet(0)
53     {
54     }
55     bool operator()(const MimeEntity* pMe) const
56     {
57         // check for content type match
58         const Header& h = pMe->header();
59         if(type.length() && type != h.contentType().type())
60             return false;
61         if(subtype.length() && subtype != h.contentType().subtype())
62             return false;
63
64         bool matched;
65         // check for params matches
66         if(paramList.size())
67         {
68             ContentType::ParamList::const_iterator ctpbit,ctpeit;
69             ctpbit = h.contentType().paramList().begin();
70             ctpeit = h.contentType().paramList().end();
71             matched = 0;
72             for(; !matched && ctpbit != ctpeit; ++ctpbit)
73             {
74                 list<Param>::const_iterator pbit, peit;
75                 pbit = paramList.begin(), peit = paramList.end();
76                 for(; pbit != peit; ++pbit)
77                 {
78                     if(ctpbit->name() != pbit->first)
79                         break;
80                     // case insensitive
81                     istring value = ctpbit->value();
82                     if(value.find(pbit->second) != string::npos)
83                         matched++;
84                 }
85             }
86             if(!matched)
87                 return false;
88         }
89
90         // check for field matches
91         if(fieldList.size())
92         {
93             matched = 0;
94             Header::const_iterator hbit, heit;
95             hbit = h.begin();
96             heit = h.end();
97             for(; !matched && hbit != heit; ++hbit)
98             {
99                 list<Field>::const_iterator fbit, feit;
100                 fbit = fieldList.begin(), feit = fieldList.end();
101                 for(; fbit != feit; ++fbit)
102                 {
103                     if(hbit->name() != fbit->first)
104                         break;
105                     // case insensitive
106                     istring value = hbit->value();
107                     if(value.find(fbit->second) != string::npos)
108                         matched++;
109                 }
110             }
111             if(!matched)
112                 return false;
113         }
114
115         return true;
116         
117     }
118     istring type, subtype;
119     list<Param> paramList;
120     list<Field> fieldList;
121     bool recursive, quiet;
122 };
123
124 void die(bool b, const string& msg)
125 {
126     if(b)
127     {
128         cerr << "Error: " << msg << endl << endl;
129         usage();
130     }
131 }
132
133 void printPart(const MimeEntity& me, const MatchParamRq& mpr, string& fqn)
134 {
135     while(mpr(&me))
136     {
137         ++g_matches;
138         if(mpr.quiet)
139             break;
140         if(fqn.length() == 0)
141             break;
142         cout << fqn << endl;
143         fqn.clear();
144         break; // never loop
145     }
146     if(!mpr.recursive)
147         return; // just top level entity
148     MimeEntityList::const_iterator mbit, meit;
149     mbit = me.body().parts().begin(), meit = me.body().parts().end();
150     for(; mbit != meit; ++mbit)
151         printPart(**mbit, mpr, fqn);
152 }
153
154 // search the whole index for matches
155 void searchIndex(const MimeEntity& me, const MatchParamRq& mpr)
156 {
157     cout << "PARTS COUNT: " << me.body().parts().size() << endl;
158     MimeEntityList::const_iterator mbit, meit;
159     mbit = me.body().parts().begin(), meit = me.body().parts().end();
160     for(; mbit != meit; ++mbit)
161     {
162         MimeEntity* pMe = *mbit;
163         if(mpr(pMe))
164             cout << pMe->header().field("x-filename").value() << endl;
165     }
166 }
167 int main(int argc, char** argv)
168 {
169     std::ios_base::sync_with_stdio(false);
170     MatchParamRq mpr;
171     int ignoreMask = imChildParts | imBody;
172     string indexFqn;
173     bool useIndex = 0;
174
175     int p = 1;
176     while(p < argc)
177     {
178         string param = argv[p];
179         if(param == "-h")
180             usage();
181         else if (param == "-r") {
182             mpr.recursive = 1;
183             ignoreMask = ignoreMask & ~imChildParts;
184         } else if (param == "-q") {
185             mpr.quiet = 1;
186         } else if (param == "-t") {
187             die( ++p == argc, param + " requires an argument");
188             ContentType ct(argv[p]);
189             die(mpr.type.length() != 0, "just one -t allowed");
190             mpr.type = ct.type();
191             mpr.subtype = ct.subtype();
192         } else if (param == "-I") {
193             die( ++p == argc, param + " requires an argument");
194             indexFqn = argv[p];
195             useIndex = 1;
196         } else if (param == "-p") {
197             die( ++p == argc, param + " requires an argument");
198             string switch_param = argv[p];
199             StringTokenizer stok(&switch_param, "=");
200             pair<string,string> item;
201             stok.next(item.first) && stok.next(item.second);
202             mpr.paramList.push_back(item);
203         } else if (param == "-f") {
204             die( ++p == argc, param + " requires an argument");
205             string switch_param = argv[p];
206             StringTokenizer stok(&switch_param, "=");
207             pair<string,string> item;
208             stok.next(item.first) && stok.next(item.second);
209             mpr.fieldList.push_back(item);
210         } else if( param.length() == 2 && param[0] == '-') {
211             usage();
212         } else {
213             // filename list starts here
214             // first filename: argv[p]
215             break;
216         }
217         ++p;
218     }
219     
220     string fqn;
221     if(useIndex) {
222         File in(indexFqn);
223         die(!in, "Error opening index file");
224         mpr.recursive = 0;
225         MimeEntity idxEntity;
226         idxEntity.load(in.begin(), in.end());
227         searchIndex(idxEntity, mpr);
228     } else if(argc == p) { 
229         // read from stdin
230         istreambuf_iterator<char> bit(cin), eit;
231         MimeEntity me;
232         me.load(bit, eit, ignoreMask);
233         fqn = "stdin";    
234         printPart(me, mpr, fqn);
235     } else 
236         for(int fc = p; fc < argc; ++fc)
237         {
238             fqn = argv[fc];
239             File in(fqn);
240             if(!in)
241             {
242                 cerr << "ERR: unable to open file " << argv[fc]
243                      << endl;
244                 continue;
245             }
246             MimeEntity me;
247             me.load(in.begin(), in.end(), ignoreMask);
248             printPart(me,mpr, fqn);
249         }
250     return g_matches;
251 }
252