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 = 0; // 1 for plaintext PXML, 2 for PND...
56 pnd_pxml_handle pxmlh = 0;
58 //printf ( "disco root callback encountered '%s'\n", fpath );
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
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 ) {
70 } else if ( strcasestr ( fpath + ftwbuf -> base, PND_PACKAGE_FILEEXT "\0" ) ) {
74 // if not a file of interest, just keep looking until we run out
76 //printf ( " .. bad filename, skipping\n" );
80 // potentially a valid application
82 // Plaintext PXML file
83 //printf ( "PXML: disco callback encountered '%s'\n", fpath );
85 // pick up the PXML if we can
86 pxmlh = pnd_pxml_fetch ( (char*) fpath );
88 } else if ( valid == 2 ) {
91 char pxmlbuf [ 32 * 1024 ]; // TBD: assuming 32k pxml accrual buffer is a little lame
93 //printf ( "PND: disco callback encountered '%s'\n", fpath );
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?
100 f = fopen ( fpath, "r" );
102 // try to locate the PXML portion
103 if ( ! pnd_pnd_seek_pxml ( f ) ) {
105 return ( 0 ); // pnd or not, but not to spec. Pwn'd the pnd?
108 // accrue it into a buffer
109 if ( ! pnd_pnd_accrue_pxml ( f, pxmlbuf, 32 * 1024 ) ) {
114 //printf ( "buffer is %s\n", pxmlbuf );
117 // by now, we have <PXML> .. </PXML>, try to parse..
118 pxmlh = pnd_pxml_fetch_buffer ( (char*) fpath, pxmlbuf );
125 // look for any overrides, if requested
126 pnd_pxml_merge_override ( pxmlh, disco_overrides );
128 // check for validity and add to resultset if it looks executable
129 if ( pnd_is_pxml_valid_app ( pxmlh ) ) {
130 char b [ 1024 ]; // TBD: also lame
133 p = pnd_box_allocinsert ( disco_box, (char*) fpath, sizeof(pnd_disco_t) );
134 if ( pnd_pxml_get_app_name ( pxmlh ) ) {
135 p -> title_en = strdup ( pnd_pxml_get_app_name ( pxmlh ) );
137 if ( pnd_pxml_get_icon_path ( pxmlh ) ) {
138 p -> icon = strdup ( pnd_pxml_get_icon_path ( pxmlh ) );
140 if ( pnd_pxml_get_exec_path ( pxmlh ) ) {
141 snprintf ( b, 1024, "pnd_run_magic %s", pnd_pxml_get_exec_path ( pxmlh ) );
142 p -> exec = strdup ( b );
144 if ( pnd_pxml_get_unique_id ( pxmlh ) ) {
145 p -> unique_id = strdup ( pnd_pxml_get_unique_id ( pxmlh ) );
147 if ( pnd_pxml_get_primary_category ( pxmlh ) ) {
148 p -> main_category = strdup ( pnd_pxml_get_primary_category ( pxmlh ) );
150 if ( pnd_pxml_get_clockspeed ( pxmlh ) ) {
151 p -> clockspeed = strdup ( pnd_pxml_get_clockspeed ( pxmlh ) );
155 //printf ( "Invalid PXML; skipping.\n" );
159 pnd_pxml_delete ( pxmlh );
163 return ( 0 ); // continue the tree walk
166 pnd_box_handle pnd_disco_search ( char *searchpath, char *overridespath ) {
168 //printf ( "Searchpath to discover: '%s'\n", searchpath );
170 // alloc a container for the result set
171 disco_box = pnd_box_new ( "discovery" );
172 disco_overrides = overridespath;
174 /* iterate across the paths within the searchpath, attempting to locate applications
180 // invoke the dir walking function; thankfully Linux includes a pretty good one
181 nftw ( buffer, // path to descend
182 pnd_disco_callback, // callback to do processing
183 10, // no more than X open fd's at once
184 FTW_PHYS ); // do not follow symlinks
189 // return whatever we found, or NULL if nada
190 if ( ! pnd_box_get_head ( disco_box ) ) {
191 pnd_box_delete ( disco_box );
195 return ( disco_box );
198 unsigned char pnd_emit_dotdesktop ( char *targetpath, pnd_disco_t *p ) {
199 char filename [ FILENAME_MAX ];
200 char buffer [ 1024 ];
204 // http://standards.freedesktop.org/desktop-entry-spec
208 if ( ! p -> unique_id ) {
218 sprintf ( filename, "%s/%s.desktop", targetpath, p -> unique_id );
222 //printf ( "EMIT DOTDESKTOP '%s'\n", filename );
224 f = fopen ( filename, "w" );
230 if ( p -> title_en ) {
231 snprintf ( buffer, 1020, "Name=%s\n", p -> title_en );
232 fprintf ( f, "%s", buffer );
235 fprintf ( f, "Type=Application\n" );
236 fprintf ( f, "Version=1.0\n" );
239 snprintf ( buffer, 1020, "Icon=%s\n", p -> icon );
240 fprintf ( f, "%s", buffer );
244 if ( p -> description_en ) {
245 snprintf ( buffer, 1020, "Comment=%s\n", p -> description_en );
246 fprintf ( f, "%s", buffer );
251 if ( p -> startdir ) {
252 snprintf ( buffer, 1020, "Path=%s\n", p -> startdir );
253 fprintf ( f, "%s", buffer );
255 fprintf ( f, "Path=%s\n", PND_DEFAULT_WORKDIR );
260 snprintf ( buffer, 1020, "Exec=%s\n", p -> exec );
261 fprintf ( f, "%s", buffer );
264 fprintf ( f, "_Source=libpnd\n" ); // should we need to know 'who' created the file during trimming