* New upstream release.
[debian/mimetic.git] / examples / catpart.cxx
1 /***************************************************************************
2     copyright            : (C) 2002-2005 by Stefano Barbato
3     email                : stefano@codesink.org
4
5     $Id: catpart.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 catpart.cc
17  *  extract a Part based on command line parameters
18  *  more info on:
19  *      catpart -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 << "catpart [params] [in_file...]" << endl;
36     cout << "\t-r                look for matches in nested entities"<<endl;
37     cout << "\t-t type[/subtype] matches Content-Type" << endl;
38     cout << "\t-p param[=value]  matches Content-Type param" << endl;
39     cout << "\t-f name[=value]   matches an header field" << endl;
40     cout << "\t-l                don't write anything but filename" << endl;
41     cout << "\t-q                totaly quiet; exit code = num of matches" << endl;
42     cout << "\t-H                ignore header fields" << endl;
43     cout << "\t-B                ignore entities' body" << endl;
44     cout << "\t-C                ignore child entities" << endl;
45     exit(-1);
46 }
47
48
49 struct MatchParamRq
50 {
51     typedef pair<istring,istring> Param;
52     typedef pair<istring,istring> Field;
53     MatchParamRq()
54     : recursive(0), justFilename(0), quiet(0)
55     {
56     }
57     bool operator()(const MimeEntity* pMe) const
58     {
59         // check for content type match
60         const Header& h = pMe->header();
61         if(type.length() && type != h.contentType().type())
62             return false;
63         if(subtype.length() && subtype != h.contentType().subtype())
64             return false;
65
66         bool matched;
67         // check for params matches
68         if(paramList.size())
69         {
70             ContentType::ParamList::const_iterator ctpbit,ctpeit;
71             ctpbit = h.contentType().paramList().begin();
72             ctpeit = h.contentType().paramList().end();
73             matched = 0;
74             for(; !matched && ctpbit != ctpeit; ++ctpbit)
75             {
76                 list<Param>::const_iterator pbit, peit;
77                 pbit = paramList.begin(), peit = paramList.end();
78                 for(; pbit != peit; ++pbit)
79                 {
80                     if(ctpbit->name() != pbit->first)
81                         break;
82                     // case insensitive
83                     istring value = ctpbit->value();
84                     if(value.find(pbit->second) != string::npos)
85                         matched++;
86                 }
87             }
88             if(!matched)
89                 return false;
90         }
91
92         // check for field matches
93         if(fieldList.size())
94         {
95             matched = 0;
96             Header::const_iterator hbit, heit;
97             hbit = h.begin();
98             heit = h.end();
99             for(; !matched && hbit != heit; ++hbit)
100             {
101                 list<Field>::const_iterator fbit, feit;
102                 fbit = fieldList.begin(), feit = fieldList.end();
103                 for(; fbit != feit; ++fbit)
104                 {
105                     if(hbit->name() != fbit->first)
106                         break;
107                     // case insensitive
108                     istring value = hbit->value();
109                     if(value.find(fbit->second) != string::npos)
110                         matched++;
111                 }
112             }
113             if(!matched)
114                 return false;
115         }
116
117         return true;
118         
119     }
120     istring type, subtype;
121     list<Param> paramList;
122     list<Field> fieldList;
123     bool recursive, justFilename, quiet;
124 };
125
126 void die(bool b, const string& msg)
127 {
128     if(b)
129     {
130         cerr << "Error: " << msg << endl << endl;
131         usage();
132     }
133 }
134
135 void printPart(const MimeEntity& me, const MatchParamRq& mpr, string& fqn)
136 {
137     while(mpr(&me))
138     {
139         ++g_matches;
140         if(mpr.quiet)
141             break;
142         if(mpr.justFilename)
143         {
144             if(fqn.length() == 0)
145                 break;
146             cout << fqn << endl;
147             fqn.clear();
148         } else {
149             if(g_matches > 1)
150                 cout << endl;
151             cout << me;
152         }
153         break; // never loop
154     }
155     if(!mpr.recursive)
156         return; // just top level entity
157     MimeEntityList::const_iterator mbit, meit;
158     mbit = me.body().parts().begin(), meit = me.body().parts().end();
159     for(; mbit != meit; ++mbit)
160         printPart(**mbit, mpr, fqn);
161 }
162
163 int main(int argc, char** argv)
164 {
165     std::ios_base::sync_with_stdio(false);
166     MatchParamRq mpr;
167     int ignoreMask = 0;
168
169     int p = 1;
170     while(p < argc)
171     {
172         string param = argv[p];
173         if(param == "-h")
174             usage();
175         else if (param == "-r") 
176             mpr.recursive = 1;
177         else if (param == "-q") 
178             mpr.quiet = 1;
179         else if (param == "-l") 
180             mpr.justFilename = 1;
181         else if (param == "-H") 
182             ignoreMask += imHeader;
183         else if (param == "-B") 
184             ignoreMask += imBody;
185         else if (param == "-C") 
186             ignoreMask += imChildParts;
187         else if (param == "-t") {
188             die( ++p == argc, param + " requires an argument");
189             ContentType ct(argv[p]);
190             die(mpr.type.length() != 0, "just one -t allowed");
191             mpr.type = ct.type();
192             mpr.subtype = ct.subtype();
193         } else if (param == "-p") {
194             die( ++p == argc, param + " requires an argument");
195             string switch_param = argv[p];
196             StringTokenizer stok(&switch_param, "=");
197             pair<string,string> item;
198             stok.next(item.first) && stok.next(item.second);
199             mpr.paramList.push_back(item);
200         } else if (param == "-f") {
201             die( ++p == argc, param + " requires an argument");
202             string switch_param = argv[p];
203             StringTokenizer stok(&switch_param, "=");
204             pair<string,string> item;
205             stok.next(item.first) && stok.next(item.second);
206             mpr.fieldList.push_back(item);
207         } else if( param.length() == 2 && param[0] == '-') {
208             usage();
209         } else {
210             // filename list starts here
211             // first filename: argv[p]
212             break;
213         }
214         ++p;
215     }
216     
217     string fqn;
218     if(argc == p)
219     { // read from stdin
220         istreambuf_iterator<char> bit(cin), eit;
221         MimeEntity me;
222         me.load(bit, eit, ignoreMask);
223         fqn = "stdin";    
224         printPart(me, mpr, fqn);
225     } else
226         for(int fc = p; fc < argc; ++fc)
227         {
228             fqn = argv[fc];
229             File in(fqn);
230             if(!in)
231             {
232                 cerr << "ERR: unable to open file " << argv[fc]
233                      << endl;
234                 continue;
235             }
236             MimeEntity me;
237             me.load(in.begin(), in.end(), ignoreMask);
238             printPart(me, mpr, fqn);
239         }
240     return g_matches;
241 }
242