Added first stab of pnd exec support
[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       char *fixpxml;
132
133       p = pnd_box_allocinsert ( disco_box, (char*) fpath, sizeof(pnd_disco_t) );
134
135       // base path
136       p -> path_to_object = strdup ( fpath );
137
138       if ( ( fixpxml = strcasestr ( p -> path_to_object, PXML_FILENAME ) ) ) {
139         *fixpxml = '\0'; // if this is not a .pnd, lop off the PXML.xml at the end
140       }
141
142       // PXML fields
143       if ( pnd_pxml_get_app_name_en ( pxmlh ) ) {
144         p -> title_en = strdup ( pnd_pxml_get_app_name_en ( pxmlh ) );
145       }
146       if ( pnd_pxml_get_icon ( pxmlh ) ) {
147         p -> icon = strdup ( pnd_pxml_get_icon ( pxmlh ) );
148       }
149       if ( pnd_pxml_get_exec ( pxmlh ) ) {
150         p -> exec = strdup ( pnd_pxml_get_exec ( pxmlh ) );
151       }
152       if ( pnd_pxml_get_unique_id ( pxmlh ) ) {
153         p -> unique_id = strdup ( pnd_pxml_get_unique_id ( pxmlh ) );
154       }
155       if ( pnd_pxml_get_main_category ( pxmlh ) ) {
156         p -> main_category = strdup ( pnd_pxml_get_main_category ( pxmlh ) );
157       }
158       if ( pnd_pxml_get_clockspeed ( pxmlh ) ) {
159         p -> clockspeed = strdup ( pnd_pxml_get_clockspeed ( pxmlh ) ); 
160       }
161       if ( pnd_pxml_get_startdir ( pxmlh ) ) {
162         p -> startdir = strdup ( pnd_pxml_get_startdir ( pxmlh ) ); 
163       }
164
165     } else {
166       //printf ( "Invalid PXML; skipping.\n" );
167     }
168
169     // ditch pxml
170     pnd_pxml_delete ( pxmlh );
171
172   } // got a pxmlh
173
174   return ( 0 ); // continue the tree walk
175 }
176
177 pnd_box_handle pnd_disco_search ( char *searchpath, char *overridespath ) {
178
179   //printf ( "Searchpath to discover: '%s'\n", searchpath );
180
181   // alloc a container for the result set
182   disco_box = pnd_box_new ( "discovery" );
183   disco_overrides = overridespath;
184
185   /* iterate across the paths within the searchpath, attempting to locate applications
186    */
187
188   SEARCHPATH_PRE
189   {
190
191     // invoke the dir walking function; thankfully Linux includes a pretty good one
192     nftw ( buffer,               // path to descend
193            pnd_disco_callback,   // callback to do processing
194            10,                   // no more than X open fd's at once
195            FTW_PHYS );           // do not follow symlinks
196
197   }
198   SEARCHPATH_POST
199
200   // return whatever we found, or NULL if nada
201   if ( ! pnd_box_get_head ( disco_box ) ) {
202     pnd_box_delete ( disco_box );
203     disco_box = NULL;
204   }
205
206   return ( disco_box );
207 }
208
209 unsigned char pnd_emit_dotdesktop ( char *targetpath, char *pndrun, pnd_disco_t *p ) {
210   char filename [ FILENAME_MAX ];
211   char buffer [ 1024 ];
212   FILE *f;
213
214   // specification
215   // http://standards.freedesktop.org/desktop-entry-spec
216
217   // validation
218
219   if ( ! p -> unique_id ) {
220     return ( 0 );
221   }
222
223   if ( ! p -> exec ) {
224     return ( 0 );
225   }
226
227   // set up
228
229   sprintf ( filename, "%s/%s.desktop", targetpath, p -> unique_id );
230
231   // emit
232
233   //printf ( "EMIT DOTDESKTOP '%s'\n", filename );
234
235   f = fopen ( filename, "w" );
236
237   if ( ! f ) {
238     return ( 0 );
239   }
240
241   if ( p -> title_en ) {
242     snprintf ( buffer, 1020, "Name=%s\n", p -> title_en );
243     fprintf ( f, "%s", buffer );
244   }
245
246   fprintf ( f, "Type=Application\n" );
247   fprintf ( f, "Version=1.0\n" );
248
249   if ( p -> icon ) {
250     snprintf ( buffer, 1020, "Icon=%s\n", p -> icon );
251     fprintf ( f, "%s", buffer );
252   }
253
254 #if 0
255   if ( p -> description_en ) {
256     snprintf ( buffer, 1020, "Comment=%s\n", p -> description_en );
257     fprintf ( f, "%s", buffer );
258   }
259 #endif
260
261 #if 0
262   if ( p -> startdir ) {
263     snprintf ( buffer, 1020, "Path=%s\n", p -> startdir );
264     fprintf ( f, "%s", buffer );
265   } else {
266     fprintf ( f, "Path=%s\n", PND_DEFAULT_WORKDIR );
267   }
268 #endif
269
270   if ( p -> exec ) {
271     snprintf ( buffer, 1020, "Exec=%s -p %s -e %s -u\n", pndrun, p -> path_to_object, p -> exec );
272     fprintf ( f, "%s", buffer );
273   }
274
275   fprintf ( f, "_Source=libpnd\n" ); // should we need to know 'who' created the file during trimming
276
277   fclose ( f );
278
279   return ( 1 );
280 }