merge some cpas changes in for the PXML.[ch] fields
[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       char b [ 1024 ]; // TBD: also lame
131       pnd_disco_t *p;
132
133       p = pnd_box_allocinsert ( disco_box, (char*) fpath, sizeof(pnd_disco_t) );
134       if ( pnd_pxml_get_app_name_en ( pxmlh ) ) {
135         p -> title_en = strdup ( pnd_pxml_get_app_name_en ( pxmlh ) );
136       }
137       if ( pnd_pxml_get_icon ( pxmlh ) ) {
138         p -> icon = strdup ( pnd_pxml_get_icon ( pxmlh ) );
139       }
140       if ( pnd_pxml_get_exec ( pxmlh ) ) {
141         snprintf ( b, 1024, "pnd_run_magic %s", pnd_pxml_get_exec ( pxmlh ) );
142         p -> exec = strdup ( b );
143       }
144       if ( pnd_pxml_get_unique_id ( pxmlh ) ) {
145         p -> unique_id = strdup ( pnd_pxml_get_unique_id ( pxmlh ) );
146       }
147       if ( pnd_pxml_get_main_category ( pxmlh ) ) {
148         p -> main_category = strdup ( pnd_pxml_get_main_category ( pxmlh ) );
149       }
150       if ( pnd_pxml_get_clockspeed ( pxmlh ) ) {
151         p -> clockspeed = strdup ( pnd_pxml_get_clockspeed ( pxmlh ) ); 
152       }
153
154     } else {
155       //printf ( "Invalid PXML; skipping.\n" );
156     }
157
158     // ditch pxml
159     pnd_pxml_delete ( pxmlh );
160
161   } // got a pxmlh
162
163   return ( 0 ); // continue the tree walk
164 }
165
166 pnd_box_handle pnd_disco_search ( char *searchpath, char *overridespath ) {
167
168   //printf ( "Searchpath to discover: '%s'\n", searchpath );
169
170   // alloc a container for the result set
171   disco_box = pnd_box_new ( "discovery" );
172   disco_overrides = overridespath;
173
174   /* iterate across the paths within the searchpath, attempting to locate applications
175    */
176
177   SEARCHPATH_PRE
178   {
179
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
185
186   }
187   SEARCHPATH_POST
188
189   // return whatever we found, or NULL if nada
190   if ( ! pnd_box_get_head ( disco_box ) ) {
191     pnd_box_delete ( disco_box );
192     disco_box = NULL;
193   }
194
195   return ( disco_box );
196 }
197
198 unsigned char pnd_emit_dotdesktop ( char *targetpath, pnd_disco_t *p ) {
199   char filename [ FILENAME_MAX ];
200   char buffer [ 1024 ];
201   FILE *f;
202
203   // specification
204   // http://standards.freedesktop.org/desktop-entry-spec
205
206   // validation
207
208   if ( ! p -> unique_id ) {
209     return ( 0 );
210   }
211
212   if ( ! p -> exec ) {
213     return ( 0 );
214   }
215
216   // set up
217
218   sprintf ( filename, "%s/%s.desktop", targetpath, p -> unique_id );
219
220   // emit
221
222   //printf ( "EMIT DOTDESKTOP '%s'\n", filename );
223
224   f = fopen ( filename, "w" );
225
226   if ( ! f ) {
227     return ( 0 );
228   }
229
230   if ( p -> title_en ) {
231     snprintf ( buffer, 1020, "Name=%s\n", p -> title_en );
232     fprintf ( f, "%s", buffer );
233   }
234
235   fprintf ( f, "Type=Application\n" );
236   fprintf ( f, "Version=1.0\n" );
237
238   if ( p -> icon ) {
239     snprintf ( buffer, 1020, "Icon=%s\n", p -> icon );
240     fprintf ( f, "%s", buffer );
241   }
242
243 #if 0
244   if ( p -> description_en ) {
245     snprintf ( buffer, 1020, "Comment=%s\n", p -> description_en );
246     fprintf ( f, "%s", buffer );
247   }
248 #endif
249
250 #if 0
251   if ( p -> startdir ) {
252     snprintf ( buffer, 1020, "Path=%s\n", p -> startdir );
253     fprintf ( f, "%s", buffer );
254   } else {
255     fprintf ( f, "Path=%s\n", PND_DEFAULT_WORKDIR );
256   }
257 #endif
258
259   if ( p -> exec ) {
260     snprintf ( buffer, 1020, "Exec=%s\n", p -> exec );
261     fprintf ( f, "%s", buffer );
262   }
263
264   fprintf ( f, "_Source=libpnd\n" ); // should we need to know 'who' created the file during trimming
265
266   fclose ( f );
267
268   return ( 1 );
269 }