1 : /// -*- C++ -*- (c) 2006, 2007 Petr Rockai <me@mornfall.net>
2 :
3 : #include <wibble/string.h>
4 :
5 : #include <ept/token.h>
6 : #include <ept/core/desktopfile.h>
7 : #include <ept/core/source.h>
8 :
9 : #include <set>
10 : #include <vector>
11 : #include <fstream>
12 : #include <sstream>
13 : #include <iterator>
14 : #include <functional>
15 :
16 : #include <dirent.h>
17 :
18 : #ifndef EPT_CORE_DESKTOP_H
19 : #define EPT_CORE_DESKTOP_H
20 :
21 : namespace ept {
22 : namespace core {
23 : namespace desktop {
24 :
25 : typedef enum { Name, Group, ShortDescription, Package, Icon } PropertyId;
26 :
27 : template< PropertyId > struct PropertyType {};
28 : template<> struct PropertyType< Name > { typedef std::string T; };
29 : template<> struct PropertyType< Group > { typedef std::string T; };
30 : template<> struct PropertyType< ShortDescription > { typedef std::string T; };
31 : template<> struct PropertyType< Package > { typedef ept::Token T; };
32 : template<> struct PropertyType< Icon > { typedef std::string T; };
33 :
34 : typedef std::set< std::string > Categories;
35 :
36 64 : struct Category {
37 : std::string name;
38 112 : operator std::string() const { return name; }
39 : };
40 :
41 80 : inline std::istream &operator >>( std::istream &i, Category &cat ) {
42 : char c;
43 80 : cat.name = "";
44 468 : while ( i.peek() != EOF ) {
45 356 : c = i.get();
46 356 : if ( c == ';' ) return i;
47 308 : cat.name += c;
48 : }
49 32 : return i;
50 : }
51 :
52 567 : struct Entry : wibble::mixin::Comparable< Entry > {
53 18 : Entry() {}
54 : Entry( std::string n, std::string g,
55 : std::string p, std::string d , std::string i )
56 : : m_name( n ),
57 : m_package( p ),
58 : m_description( d ),
59 : m_icon( i )
60 : { setCategories( g ); }
61 :
62 16 : void load( std::string file ) {
63 16 : m_id = file;
64 16 : std::ifstream i( file.c_str() );
65 16 : if ( !i.is_open() )
66 0 : return; // throw?
67 16 : desktop::File e;
68 16 : i >> e;
69 16 : i.close();
70 16 : desktop::File::Group &g = e.group( "Desktop Entry" );
71 32 : m_name = g.entry( "Name" ).value;
72 32 : m_description = g.entry( "Comment" ).value;
73 32 : if ( m_description == "" )
74 8 : m_description = g.entry( "GenericName" ).value;
75 16 : m_package = g.entry( "X-AppInstall-Package" ).value;
76 : // m_group = g.entry( "Categories" ).value;
77 32 : m_icon = g.entry( "Icon" ).value;
78 32 : setCategories( g.entry( "Categories" ).value );
79 : }
80 :
81 16 : void setCategories( std::string s ) {
82 16 : std::istringstream i( s );
83 16 : m_categories.clear();
84 : std::remove_copy_if(
85 : std::istream_iterator< Category >( i ),
86 : std::istream_iterator< Category >(),
87 : std::inserter( m_categories, m_categories.begin() ),
88 16 : std::bind1st( std::equal_to< std::string >(), "" ) );
89 16 : }
90 :
91 16 : Categories categories() const { return m_categories; }
92 : bool inCategory( std::string c ) const {
93 : return m_categories.find( c ) != m_categories.end();
94 : }
95 34 : std::string id() const { return m_id; }
96 6 : std::string name() const { return m_name; }
97 : std::string package() const { return m_package; }
98 : std::string description() const { return m_description; }
99 : std::string icon() const { return m_icon; }
100 : bool operator< ( const Entry &o ) const {
101 : if ( m_name < o.m_name ) return true;
102 : if ( m_name == o.m_name )
103 : if ( m_package < o.m_package ) return true;
104 : return false;
105 : }
106 : protected:
107 : std::string m_name, m_package, m_description, m_icon, m_id;
108 : bool m_supported, m_free;
109 : Categories m_categories;
110 : };
111 :
112 450 : struct InternalList {
113 : std::string dir;
114 : std::string current;
115 : mutable Entry entry;
116 : off_t offset;
117 : mutable bool loaded;
118 :
119 9 : InternalList() : dir( "" ), offset( -2 ), loaded( false ) {}
120 4 : InternalList( std::string d ) : dir( d ), offset( -1 ), loaded( false )
121 : {
122 4 : firstFile();
123 4 : }
124 :
125 56 : Entry head() const {
126 56 : if (!loaded)
127 16 : entry.load( current );
128 56 : loaded = true;
129 56 : return entry;
130 : }
131 :
132 33 : bool empty() const {
133 33 : return (offset == -2);
134 : }
135 :
136 4 : void firstFile() {
137 4 : offset = -1;
138 4 : nextFile();
139 4 : }
140 :
141 16 : InternalList tail() const {
142 16 : InternalList r = *this;
143 16 : r.nextFile();
144 0 : return r;
145 : }
146 :
147 20 : void nextFile() {
148 20 : loaded = false;
149 20 : DIR *d = opendir( dir.c_str() );
150 20 : if ( !d ) {
151 0 : offset = -2;
152 0 : closedir( d );
153 0 : return;
154 : }
155 :
156 20 : if ( offset != -1 )
157 16 : seekdir( d, offset );
158 :
159 20 : dirent *ent = 0;
160 48 : while ( ( ent = readdir( d ) ) != 0 ) {
161 24 : std::string name( ent->d_name );
162 48 : if ( name == "." || name == ".." )
163 8 : continue;
164 16 : if ( !wibble::str::endsWith( name, ".desktop" ) )
165 : continue;
166 16 : current = dir + "/" + name;
167 16 : offset = telldir( d );
168 16 : closedir( d );
169 16 : return;
170 : }
171 4 : closedir( d );
172 4 : offset = -2;
173 : }
174 : };
175 :
176 : struct Setup {
177 : typedef ept::Token Token;
178 : typedef Entry Internal;
179 : typedef desktop::PropertyId PropertyId;
180 : typedef desktop::InternalList InternalList;
181 : };
182 :
183 5 : struct GroupPolicy {
184 16 : virtual std::string group( const Entry &e )
185 : {
186 16 : return wibble::str::fmt( e.categories() );
187 : }
188 5 : virtual ~GroupPolicy() {}
189 : };
190 :
191 : struct Source : core::Source< Source, Setup, PropertyType >
192 5 : {
193 : std::string m_dir;
194 :
195 : GroupPolicy m_defaultPolicy;
196 : GroupPolicy *m_policy;
197 :
198 5 : Source( std::string dir ) : m_dir( dir ),
199 5 : m_policy( &m_defaultPolicy ) {}
200 :
201 4 : InternalList listInternal() {
202 4 : return InternalList( m_dir );
203 : }
204 :
205 34 : Token getToken( Entry i ) {
206 34 : Token t;
207 34 : t._id = std::string( "desktop:" ) + i.id();
208 0 : return t;
209 : }
210 :
211 : Entry lookupToken( Token t ) {
212 : Entry e;
213 : e.load( t.desktop() );
214 : return e;
215 : }
216 :
217 : void setGroupPolicy( GroupPolicy *p ) {
218 : m_policy = p;
219 : }
220 :
221 : template< PropertyId p >
222 : typename PropertyType< p >::T getInternal( Entry );
223 :
224 56 : struct IsInGroup {
225 : std::string g;
226 2 : IsInGroup( std::string _g = "" ) : g( _g ) {}
227 8 : bool operator()( Token, std::string gr ) const {
228 8 : return gr == g;
229 : }
230 : };
231 :
232 1 : PropertyFilter< Group, IsInGroup >::T group( std::string id )
233 : {
234 1 : return propertyFilter< Group >( IsInGroup( id ) );
235 : }
236 :
237 4 : static std::string projectGroup( ComposedList< Name > t ) {
238 4 : return t.get< Group >();
239 : }
240 :
241 : list::Unique< list::Sorted<
242 : list::Map< ComposedList< Name >,
243 : __typeof( std::ptr_fun( &projectGroup ) ) > > >
244 1 : groupList() {
245 : return list::unique(
246 : list::sort( list::map( list< Name >(),
247 1 : std::ptr_fun( &projectGroup ) ) ) );
248 : }
249 : };
250 :
251 6 : template<> inline std::string Source::getInternal< Name >( Entry e ) {
252 6 : return e.name();
253 : }
254 :
255 : template<> inline std::string Source::getInternal< Icon >( Entry e ) {
256 : return e.icon();
257 : }
258 :
259 : template<> inline ept::Token Source::getInternal< Package >( Entry e ) {
260 : ept::Token t;
261 : t._id = e.package();
262 : return t;
263 : }
264 :
265 16 : template<> inline std::string Source::getInternal< Group >( Entry e ) {
266 16 : return m_policy->group( e );
267 : }
268 :
269 : template<> inline std::string Source::getInternal< ShortDescription >( Entry e ) {
270 : return e.description();
271 : }
272 :
273 : }
274 : }
275 : }
276 :
277 : #endif
|