2 #include <stdio.h> /* for FILE etc */
3 #include <stdlib.h> /* for malloc */
5 #define __USE_GNU /* for strcasestr */
6 #include <string.h> /* for making ftw.h happy */
8 #define _XOPEN_SOURCE 500
9 #define __USE_XOPEN_EXTENDED
10 #include <ftw.h> /* for nftw, tree walker */
12 #include "pnd_container.h"
14 #include "pnd_discovery.h"
15 #include "pnd_pathiter.h"
17 #include "pnd_pndfiles.h"
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;
23 void pnd_disco_destroy ( pnd_disco_t *p ) {
25 if ( p -> title_en ) {
26 free ( p -> title_en );
37 if ( p -> unique_id ) {
38 free ( p -> unique_id );
41 if ( p -> main_category ) {
42 free ( p -> main_category );
45 if ( p -> clockspeed ) {
46 free ( p -> clockspeed );
52 static int pnd_disco_callback ( const char *fpath, const struct stat *sb,
53 int typeflag, struct FTW *ftwbuf )
55 unsigned char valid = pnd_object_type_unknown;
56 pnd_pxml_handle pxmlh = 0;
57 unsigned int pxml_close_pos = 0;
59 //printf ( "disco root callback encountered '%s'\n", fpath );
61 // PXML.xml is a possible application candidate (and not a dir named PXML.xml :)
62 if ( typeflag & FTW_D ) {
63 //printf ( " .. is dir, skipping\n" );
64 return ( 0 ); // skip directories and other non-regular files
67 // PND/PNZ file and others may be valid as well .. but lets leave that for now
68 // printf ( "%s %s\n", fpath + ftwbuf -> base, PND_PACKAGE_FILEEXT );
69 if ( strcasecmp ( fpath + ftwbuf -> base, PXML_FILENAME ) == 0 ) {
70 valid = pnd_object_type_directory;
71 } else if ( strcasestr ( fpath + ftwbuf -> base, PND_PACKAGE_FILEEXT "\0" ) ) {
72 valid = pnd_object_type_pnd;
75 // if not a file of interest, just keep looking until we run out
77 //printf ( " .. bad filename, skipping\n" );
81 // potentially a valid application
82 if ( valid == pnd_object_type_directory ) {
83 // Plaintext PXML file
84 //printf ( "PXML: disco callback encountered '%s'\n", fpath );
86 // pick up the PXML if we can
87 pxmlh = pnd_pxml_fetch ( (char*) fpath );
89 } else if ( valid == pnd_object_type_pnd ) {
92 char pxmlbuf [ 32 * 1024 ]; // TBD: assuming 32k pxml accrual buffer is a little lame
94 //printf ( "PND: disco callback encountered '%s'\n", fpath );
96 // is this a valid .pnd file? The filename is a candidate already, but verify..
97 // .. presence of PXML appended, or at least contained within?
98 // .. presence of an icon appended after PXML?
101 f = fopen ( fpath, "r" );
103 // try to locate the PXML portion
104 if ( ! pnd_pnd_seek_pxml ( f ) ) {
106 return ( 0 ); // pnd or not, but not to spec. Pwn'd the pnd?
109 // accrue it into a buffer
110 if ( ! pnd_pnd_accrue_pxml ( f, pxmlbuf, 32 * 1024 ) ) {
115 //printf ( "buffer is %s\n", pxmlbuf );
119 // for convenience, lets skip along past trailing newlines/CR's in hopes of finding icon data?
121 unsigned int pos = ftell ( f );
122 char pngbuffer [ 16 ]; // \211 P N G \r \n \032 \n
123 pngbuffer [ 0 ] = 137; pngbuffer [ 1 ] = 80; pngbuffer [ 2 ] = 78; pngbuffer [ 3 ] = 71;
124 pngbuffer [ 4 ] = 13; pngbuffer [ 5 ] = 10; pngbuffer [ 6 ] = 26; pngbuffer [ 7 ] = 10;
125 if ( fread ( pngbuffer + 8, 8, 1, f ) == 1 ) {
126 if ( memcmp ( pngbuffer, pngbuffer + 8, 8 ) == 0 ) {
127 pxml_close_pos = pos;
133 // by now, we have <PXML> .. </PXML>, try to parse..
134 pxmlh = pnd_pxml_fetch_buffer ( (char*) fpath, pxmlbuf );
144 // look for any overrides, if requested
145 pnd_pxml_merge_override ( pxmlh, disco_overrides );
147 // check for validity and add to resultset if it looks executable
148 if ( pnd_is_pxml_valid_app ( pxmlh ) ) {
152 p = pnd_box_allocinsert ( disco_box, (char*) fpath, sizeof(pnd_disco_t) );
155 p -> path_to_object = strdup ( fpath );
156 if ( ( fixpxml = strcasestr ( p -> path_to_object, PXML_FILENAME ) ) ) {
157 *fixpxml = '\0'; // if this is not a .pnd, lop off the PXML.xml at the end
161 p -> pnd_icon_pos = pxml_close_pos;
164 p -> object_type = valid;
167 if ( pnd_pxml_get_app_name_en ( pxmlh ) ) {
168 p -> title_en = strdup ( pnd_pxml_get_app_name_en ( pxmlh ) );
170 if ( pnd_pxml_get_icon ( pxmlh ) ) {
171 p -> icon = strdup ( pnd_pxml_get_icon ( pxmlh ) );
173 if ( pnd_pxml_get_exec ( pxmlh ) ) {
174 p -> exec = strdup ( pnd_pxml_get_exec ( pxmlh ) );
176 if ( pnd_pxml_get_unique_id ( pxmlh ) ) {
177 p -> unique_id = strdup ( pnd_pxml_get_unique_id ( pxmlh ) );
179 if ( pnd_pxml_get_main_category ( pxmlh ) ) {
180 p -> main_category = strdup ( pnd_pxml_get_main_category ( pxmlh ) );
182 if ( pnd_pxml_get_clockspeed ( pxmlh ) ) {
183 p -> clockspeed = strdup ( pnd_pxml_get_clockspeed ( pxmlh ) );
185 if ( pnd_pxml_get_startdir ( pxmlh ) ) {
186 p -> startdir = strdup ( pnd_pxml_get_startdir ( pxmlh ) );
190 //printf ( "Invalid PXML; skipping.\n" );
194 pnd_pxml_delete ( pxmlh );
198 return ( 0 ); // continue the tree walk
201 pnd_box_handle pnd_disco_search ( char *searchpath, char *overridespath ) {
203 //printf ( "Searchpath to discover: '%s'\n", searchpath );
205 // alloc a container for the result set
206 disco_box = pnd_box_new ( "discovery" );
207 disco_overrides = overridespath;
209 /* iterate across the paths within the searchpath, attempting to locate applications
215 // invoke the dir walking function; thankfully Linux includes a pretty good one
216 nftw ( buffer, // path to descend
217 pnd_disco_callback, // callback to do processing
218 10, // no more than X open fd's at once
219 FTW_PHYS ); // do not follow symlinks
224 // return whatever we found, or NULL if nada
225 if ( ! pnd_box_get_head ( disco_box ) ) {
226 pnd_box_delete ( disco_box );
230 return ( disco_box );
233 unsigned char pnd_emit_dotdesktop ( char *targetpath, char *pndrun, pnd_disco_t *p ) {
234 char filename [ FILENAME_MAX ];
235 char buffer [ 1024 ];
239 // http://standards.freedesktop.org/desktop-entry-spec
243 if ( ! p -> unique_id ) {
253 sprintf ( filename, "%s/%s.desktop", targetpath, p -> unique_id );
257 //printf ( "EMIT DOTDESKTOP '%s'\n", filename );
259 f = fopen ( filename, "w" );
265 if ( p -> title_en ) {
266 snprintf ( buffer, 1020, "Name=%s\n", p -> title_en );
267 fprintf ( f, "%s", buffer );
270 fprintf ( f, "Type=Application\n" );
271 fprintf ( f, "Version=1.0\n" );
274 snprintf ( buffer, 1020, "Icon=%s\n", p -> icon );
275 fprintf ( f, "%s", buffer );
279 if ( p -> description_en ) {
280 snprintf ( buffer, 1020, "Comment=%s\n", p -> description_en );
281 fprintf ( f, "%s", buffer );
286 if ( p -> startdir ) {
287 snprintf ( buffer, 1020, "Path=%s\n", p -> startdir );
288 fprintf ( f, "%s", buffer );
290 fprintf ( f, "Path=%s\n", PND_DEFAULT_WORKDIR );
295 snprintf ( buffer, 1020, "Exec=%s -p %s -e %s -u\n", pndrun, p -> path_to_object, p -> exec );
296 fprintf ( f, "%s", buffer );
299 fprintf ( f, "_Source=libpnd\n" ); // should we need to know 'who' created the file during trimming
306 unsigned char pnd_emit_icon ( char *targetpath, pnd_disco_t *p ) {
307 char buffer [ FILENAME_MAX ];
308 char bits [ 8 * 1024 ];
312 sprintf ( buffer, "%s/%u.png", targetpath, p -> unique_id );
314 // first.. are we looking through a pnd file or a dir?
315 if ( p -> object_type == pnd_object_type_directory ) {
316 // if we can find icon, copy it in from directory to destination
318 } else if ( p -> object_type == pnd_object_type_pnd ) {
319 // if we can get it from pnd file, copy it into destination
321 if ( ! p -> pnd_icon_pos ) {
322 return ( 0 ); // discover code didn't find it, so FAIL
328 pnd = fopen ( p -> path_to_object, "r" );
334 target = fopen ( buffer, "wb" );
341 fseek ( pnd, 0, SEEK_END );
343 //fseek ( pnd, 0, SEEK_SET );
345 fseek ( pnd, p -> pnd_icon_pos, SEEK_SET );
347 len -= p -> pnd_icon_pos;
351 if ( len > (8*1024) ) {
357 if ( fread ( bits, bitlen, 1, pnd ) != 1 ) {
364 if ( fwrite ( bits, bitlen, 1, target ) != 1 ) {