Added base path to disco struct
[pandora-libraries.git] / lib / pnd_discovery.c
1
2 #include <stdio.h> /* for FILE etc */
3 #include <stdlib.h> /* for malloc */
4
5 #define __USE_GNU /* for strcasestr */
6 #include <string.h> /* for making ftw.h happy */
7
8 #define _XOPEN_SOURCE 500
9 #define __USE_XOPEN_EXTENDED
10 #include <ftw.h> /* for nftw, tree walker */
11
12 #include "pnd_container.h"
13 #include "pnd_pxml.h"
14 #include "pnd_discovery.h"
15 #include "pnd_pathiter.h"
16 #include "pnd_apps.h"
17 #include "pnd_pndfiles.h"
18
19 // need these 'globals' due to the way nftw and ftw work :/
20 static pnd_box_handle disco_box;
21 static char *disco_overrides = NULL;
22
23 void pnd_disco_destroy ( pnd_disco_t *p ) {
24
25   if ( p -> title_en ) {
26     free ( p -> title_en );
27   }
28
29   if ( p -> icon ) {
30     free ( p -> icon );
31   }
32
33   if ( p -> exec ) {
34     free ( p -> exec );
35   }
36
37   if ( p -> unique_id ) {
38     free ( p -> unique_id );
39   }
40
41   if ( p -> main_category ) {
42     free ( p -> main_category );
43   }
44
45   if ( p -> clockspeed ) {
46     free ( p -> clockspeed );
47   }
48
49   return;
50 }
51
52 static int pnd_disco_callback ( const char *fpath, const struct stat *sb,
53                                 int typeflag, struct FTW *ftwbuf )
54 {
55   unsigned char valid = 0; // 1 for plaintext PXML, 2 for PND...
56   pnd_pxml_handle pxmlh = 0;
57
58   //printf ( "disco root callback encountered '%s'\n", fpath );
59
60   // PXML.xml is a possible application candidate (and not a dir named PXML.xml :)
61   if ( typeflag & FTW_D ) {
62     //printf ( " .. is dir, skipping\n" );
63     return ( 0 ); // skip directories and other non-regular files
64   }
65
66   // PND/PNZ file and others may be valid as well .. but lets leave that for now
67   //   printf ( "%s %s\n", fpath + ftwbuf -> base, PND_PACKAGE_FILEEXT );
68   if ( strcasecmp ( fpath + ftwbuf -> base, PXML_FILENAME ) == 0 ) {
69     valid = 1;
70   } else if ( strcasestr ( fpath + ftwbuf -> base, PND_PACKAGE_FILEEXT "\0" ) ) {
71     valid = 2;
72   }
73
74   // if not a file of interest, just keep looking until we run out
75   if ( ! valid ) {
76     //printf ( " .. bad filename, skipping\n" );
77     return ( 0 );
78   }
79
80   // potentially a valid application
81   if ( valid == 1 ) {
82     // Plaintext PXML file
83     //printf ( "PXML: disco callback encountered '%s'\n", fpath );
84
85     // pick up the PXML if we can
86     pxmlh = pnd_pxml_fetch ( (char*) fpath );
87
88   } else if ( valid == 2 ) {
89     // PND ... ??
90     FILE *f;
91     char pxmlbuf [ 32 * 1024 ]; // TBD: assuming 32k pxml accrual buffer is a little lame
92
93     //printf ( "PND: disco callback encountered '%s'\n", fpath );
94
95     // is this a valid .pnd file? The filename is a candidate already, but verify..
96     // .. presence of PXML appended, or at least contained within?
97     // .. presence of an icon appended after PXML?
98
99     // open it up..
100     f = fopen ( fpath, "r" );
101
102     // try to locate the PXML portion
103     if ( ! pnd_pnd_seek_pxml ( f ) ) {
104       fclose ( f );
105       return ( 0 ); // pnd or not, but not to spec. Pwn'd the pnd?
106     }
107
108     // accrue it into a buffer
109     if ( ! pnd_pnd_accrue_pxml ( f, pxmlbuf, 32 * 1024 ) ) {
110       fclose ( f );
111       return ( 0 );
112     }
113
114     //printf ( "buffer is %s\n", pxmlbuf );
115     //fflush ( stdout );
116
117     // by now, we have <PXML> .. </PXML>, try to parse..
118     pxmlh = pnd_pxml_fetch_buffer ( (char*) fpath, pxmlbuf );
119
120   }
121
122   // pxmlh is useful?
123   if ( pxmlh ) {
124
125     // look for any overrides, if requested
126     pnd_pxml_merge_override ( pxmlh, disco_overrides );
127
128     // check for validity and add to resultset if it looks executable
129     if ( pnd_is_pxml_valid_app ( pxmlh ) ) {
130       pnd_disco_t *p;
131
132       p = pnd_box_allocinsert ( disco_box, (char*) fpath, sizeof(pnd_disco_t) );
133
134       // base path
135       p -> path_to_object = strdup ( fpath );
136
137       // PXML fields
138       if ( pnd_pxml_get_app_name_en ( pxmlh ) ) {
139         p -> title_en = strdup ( pnd_pxml_get_app_name_en ( pxmlh ) );
140       }
141       if ( pnd_pxml_get_icon ( pxmlh ) ) {
142         p -> icon = strdup ( pnd_pxml_get_icon ( pxmlh ) );
143       }
144       if ( pnd_pxml_get_exec ( pxmlh ) ) {
145         p -> exec = strdup ( pnd_pxml_get_exec ( pxmlh ) );
146       }
147       if ( pnd_pxml_get_unique_id ( pxmlh ) ) {
148         p -> unique_id = strdup ( pnd_pxml_get_unique_id ( pxmlh ) );
149       }
150       if ( pnd_pxml_get_main_category ( pxmlh ) ) {
151         p -> main_category = strdup ( pnd_pxml_get_main_category ( pxmlh ) );
152       }
153       if ( pnd_pxml_get_clockspeed ( pxmlh ) ) {
154         p -> clockspeed = strdup ( pnd_pxml_get_clockspeed ( pxmlh ) ); 
155       }
156       if ( pnd_pxml_get_startdir ( pxmlh ) ) {
157         p -> startdir = strdup ( pnd_pxml_get_startdir ( pxmlh ) ); 
158       }
159
160     } else {
161       //printf ( "Invalid PXML; skipping.\n" );
162     }
163
164     // ditch pxml
165     pnd_pxml_delete ( pxmlh );
166
167   } // got a pxmlh
168
169   return ( 0 ); // continue the tree walk
170 }
171
172 pnd_box_handle pnd_disco_search ( char *searchpath, char *overridespath ) {
173
174   //printf ( "Searchpath to discover: '%s'\n", searchpath );
175
176   // alloc a container for the result set
177   disco_box = pnd_box_new ( "discovery" );
178   disco_overrides = overridespath;
179
180   /* iterate across the paths within the searchpath, attempting to locate applications
181    */
182
183   SEARCHPATH_PRE
184   {
185
186     // invoke the dir walking function; thankfully Linux includes a pretty good one
187     nftw ( buffer,               // path to descend
188            pnd_disco_callback,   // callback to do processing
189            10,                   // no more than X open fd's at once
190            FTW_PHYS );           // do not follow symlinks
191
192   }
193   SEARCHPATH_POST
194
195   // return whatever we found, or NULL if nada
196   if ( ! pnd_box_get_head ( disco_box ) ) {
197     pnd_box_delete ( disco_box );
198     disco_box = NULL;
199   }
200
201   return ( disco_box );
202 }
203
204 unsigned char pnd_emit_dotdesktop ( char *targetpath, pnd_disco_t *p ) {
205   char filename [ FILENAME_MAX ];
206   char buffer [ 1024 ];
207   FILE *f;
208
209   // specification
210   // http://standards.freedesktop.org/desktop-entry-spec
211
212   // validation
213
214   if ( ! p -> unique_id ) {
215     return ( 0 );
216   }
217
218   if ( ! p -> exec ) {
219     return ( 0 );
220   }
221
222   // set up
223
224   sprintf ( filename, "%s/%s.desktop", targetpath, p -> unique_id );
225
226   // emit
227
228   //printf ( "EMIT DOTDESKTOP '%s'\n", filename );
229
230   f = fopen ( filename, "w" );
231
232   if ( ! f ) {
233     return ( 0 );
234   }
235
236   if ( p -> title_en ) {
237     snprintf ( buffer, 1020, "Name=%s\n", p -> title_en );
238     fprintf ( f, "%s", buffer );
239   }
240
241   fprintf ( f, "Type=Application\n" );
242   fprintf ( f, "Version=1.0\n" );
243
244   if ( p -> icon ) {
245     snprintf ( buffer, 1020, "Icon=%s\n", p -> icon );
246     fprintf ( f, "%s", buffer );
247   }
248
249 #if 0
250   if ( p -> description_en ) {
251     snprintf ( buffer, 1020, "Comment=%s\n", p -> description_en );
252     fprintf ( f, "%s", buffer );
253   }
254 #endif
255
256 #if 0
257   if ( p -> startdir ) {
258     snprintf ( buffer, 1020, "Path=%s\n", p -> startdir );
259     fprintf ( f, "%s", buffer );
260   } else {
261     fprintf ( f, "Path=%s\n", PND_DEFAULT_WORKDIR );
262   }
263 #endif
264
265   if ( p -> exec ) {
266     snprintf ( buffer, 1020, "Exec=%s\n", p -> exec );
267     fprintf ( f, "%s", buffer );
268   }
269
270   fprintf ( f, "_Source=libpnd\n" ); // should we need to know 'who' created the file during trimming
271
272   fclose ( f );
273
274   return ( 1 );
275 }