3fffa36075249d8d6f8852b62714e695e29f4297
[pandora-libraries.git] / lib / pnd_desktop.c
1
2 #include <stdio.h> /* for FILE etc */
3 #include <string.h>
4 #include <unistd.h> /* for unlink */
5 #include <stdlib.h> /* for free */
6 #include <sys/stat.h> /* for stat */
7
8 #include "pnd_apps.h"
9 #include "pnd_container.h"
10 #include "pnd_pxml.h"
11 #include "pnd_discovery.h"
12 #include "pnd_pndfiles.h"
13 #include "pnd_conf.h"
14 #include "pnd_desktop.h"
15 #include "pnd_logger.h"
16
17 unsigned char pnd_emit_dotdesktop ( char *targetpath, char *pndrun, pnd_disco_t *p ) {
18   char filename [ FILENAME_MAX ];
19   char buffer [ 1024 ];
20   FILE *f;
21
22   // specification
23   // http://standards.freedesktop.org/desktop-entry-spec
24
25   // validation
26
27   if ( ! p -> unique_id ) {
28     pnd_log ( PND_LOG_DEFAULT, "Can't emit dotdesktop for %s, missing unique-id\n", targetpath );
29     return ( 0 );
30   }
31
32   if ( ! p -> exec ) {
33     pnd_log ( PND_LOG_DEFAULT, "Can't emit dotdesktop for %s, missing exec\n", targetpath );
34     return ( 0 );
35   }
36
37   if ( ! targetpath ) {
38     pnd_log ( PND_LOG_DEFAULT, "Can't emit dotdesktop for %s, missing target path\n", targetpath );
39     return ( 0 );
40   }
41
42   if ( ! pndrun ) {
43     pnd_log ( PND_LOG_DEFAULT, "Can't emit dotdesktop for %s, missing pnd_run.sh\n", targetpath );
44     return ( 0 );
45   }
46
47   // set up
48
49   sprintf ( filename, "%s/%s#%u.desktop", targetpath, p -> unique_id, p -> subapp_number );
50
51   // emit
52
53   //printf ( "EMIT DOTDESKTOP '%s'\n", filename );
54
55   f = fopen ( filename, "w" );
56
57   if ( ! f ) {
58     return ( 0 );
59   }
60
61   fprintf ( f, "%s\n", PND_DOTDESKTOP_HEADER );
62
63   if ( p -> title_en ) {
64     snprintf ( buffer, 1020, "Name=%s\n", p -> title_en );
65     fprintf ( f, "%s", buffer );
66   }
67
68   fprintf ( f, "Type=Application\n" );
69   fprintf ( f, "Version=1.0\n" );
70
71   if ( p -> icon ) {
72     snprintf ( buffer, 1020, "Icon=%s\n", p -> icon );
73     fprintf ( f, "%s", buffer );
74   }
75
76   if ( p -> unique_id ) {
77     fprintf ( f, "X-Pandora-UID=%s\n", p -> unique_id );
78   }
79
80 #if 1
81   if ( p -> desc_en && p -> desc_en [ 0 ] ) {
82     snprintf ( buffer, 1020, "Comment=%s\n", p -> desc_en ); // no [en] needed I suppose, yet
83     fprintf ( f, "%s", buffer );
84   }
85 #endif
86
87   if ( p -> preview_pic1 ) {
88     fprintf ( f, "X-Pandora-Preview-Pic-1=%s\n", p -> preview_pic1 );
89   }
90
91   if ( p -> clockspeed ) {
92     fprintf ( f, "X-Pandora-Clockspeed=%s\n", p -> clockspeed );
93   }
94
95   if ( p -> startdir ) {
96     fprintf ( f, "X-Pandora-Startdir=%s\n", p -> startdir );
97   }
98
99   if ( p -> main_category ) {
100     fprintf ( f, "X-Pandora-MainCategory=%s\n", p -> main_category );
101   }
102   if ( p -> main_category1 ) {
103     fprintf ( f, "X-Pandora-MainCategory1=%s\n", p -> main_category1 );
104   }
105   if ( p -> main_category2 ) {
106     fprintf ( f, "X-Pandora-MainCategory2=%s\n", p -> main_category2 );
107   }
108
109   if ( p -> alt_category ) {
110     fprintf ( f, "X-Pandora-AltCategory=%s\n", p -> alt_category );
111   }
112   if ( p -> alt_category1 ) {
113     fprintf ( f, "X-Pandora-AltCategory1=%s\n", p -> alt_category1 );
114   }
115   if ( p -> alt_category2 ) {
116     fprintf ( f, "X-Pandora-AltCategory2=%s\n", p -> alt_category2 );
117   }
118   if ( p -> info_filename ) {
119     fprintf ( f, "X-Pandora-Info-Filename=%s\n", p -> info_filename );
120   }
121   if ( p -> info_name ) {
122     fprintf ( f, "X-Pandora-Info-Name=%s\n", p -> info_name );
123   }
124
125 #if 0 // we let pnd_run.sh command line handle this instead of in .desktop
126   if ( p -> startdir ) {
127     snprintf ( buffer, 1020, "Path=%s\n", p -> startdir );
128     fprintf ( f, "%s", buffer );
129   } else {
130     fprintf ( f, "Path=%s\n", PND_DEFAULT_WORKDIR );
131   }
132 #endif
133
134   if ( p -> exec ) {
135     char *nohup;
136
137     if ( p -> option_no_x11 ) {
138       nohup = "/usr/bin/nohup ";
139     } else {
140       nohup = "";
141     }
142
143     // basics
144     if ( p -> object_type == pnd_object_type_directory ) {
145       snprintf ( buffer, 1020, "Exec=%s%s -p \"%s\" -e \"%s\" -b \"%s\"",
146                  nohup, pndrun, p -> object_path, p -> exec,
147                  p -> appdata_dirname ? p -> appdata_dirname : p -> unique_id );
148     } else if ( p -> object_type == pnd_object_type_pnd ) {
149       snprintf ( buffer, 1020, "Exec=%s%s -p \"%s/%s\" -e \"%s\" -b \"%s\"",
150                  nohup, pndrun, p -> object_path, p -> object_filename, p -> exec,
151                  p -> appdata_dirname ? p -> appdata_dirname : p -> unique_id );
152     }
153
154     // start dir
155     if ( p -> startdir ) {
156       strncat ( buffer, " -s ", 1020 );
157       strncat ( buffer, p -> startdir, 1020 );
158     }
159
160     // args
161     if ( p -> execargs ) {
162       char argbuf [ 1024 ];
163       snprintf ( argbuf, 1000, " -a \"%s\"", p -> execargs );
164       strncat ( buffer, argbuf, 1020 );
165     }
166
167     // clockspeed
168     if ( p -> clockspeed && atoi ( p -> clockspeed ) != 0 ) {
169       strncat ( buffer, " -c ", 1020 );
170       strncat ( buffer, p -> clockspeed, 1020 );
171     }
172
173     // exec options
174     if ( pnd_pxml_get_x11 ( p -> option_no_x11 ) == pnd_pxml_x11_stop ) {
175       strncat ( buffer, " -x ", 1020 );
176     }
177
178     // newline
179     strncat ( buffer, "\n", 1020 );
180
181     // emit
182     fprintf ( f, "%s", buffer );
183
184     // and lets copy in some stuff in case it makes .desktop consumers life easier
185     if ( p -> exec ) { fprintf ( f, "X-Pandora-Exec=%s\n", p -> exec ); }
186     if ( p -> appdata_dirname ) { fprintf ( f, "X-Pandora-Appdata-Dirname=%s\n", p -> appdata_dirname ); }
187     if ( p -> execargs ) { fprintf ( f, "X-Pandora-ExecArgs=%s\n", p -> execargs ); }
188     if ( p -> object_flags & PND_DISCO_FLAG_OVR ) { fprintf ( f, "X-Pandora-Object-Flag-OVR=%s\n", "Yes" ); }
189     if ( p -> object_type == pnd_object_type_pnd ) {
190       fprintf ( f, "X-Pandora-Object-Path=%s\n", p -> object_path );
191       fprintf ( f, "X-Pandora-Object-Filename=%s\n", p -> object_filename );
192     }
193
194   }
195
196   // categories
197   {
198     char cats [ 512 ] = "";
199     int n;
200     pnd_conf_handle c;
201     char *confpath;
202
203     // uuuuh, defaults?
204     // "Application" used to be in the standard and is commonly supported still
205     // Utility and Network should ensure the app is visible 'somewhere' :/
206     char *defaults = PND_DOTDESKTOP_DEFAULT_CATEGORY;
207
208     // determine searchpath (for conf, not for apps)
209     confpath = pnd_conf_query_searchpath();
210
211     // inhale the conf file
212     c = pnd_conf_fetch_by_id ( pnd_conf_categories, confpath );
213
214     // if we can find a default category set, pull it in; otherwise assume
215     // the hardcoded one
216     if ( pnd_conf_get_as_char ( c, "default" ) ) {
217       defaults = pnd_conf_get_as_char ( c, "default" );
218     }
219
220     // ditch the confpath
221     free ( confpath );
222
223     // attempt mapping
224     if ( c ) {
225
226       n = pnd_map_dotdesktop_categories ( c, cats, 511, p );
227
228       if ( n ) {
229         fprintf ( f, "Categories=%s\n", cats );
230       } else {
231         fprintf ( f, "Categories=%s\n", defaults );
232       }
233
234     } else {
235       fprintf ( f, "Categories=%s\n", defaults );
236     }
237
238   }
239
240   fprintf ( f, "%s\n", PND_DOTDESKTOP_SOURCE ); // should we need to know 'who' created the file during trimming
241
242   fclose ( f );
243
244   return ( 1 );
245 }
246
247 unsigned char pnd_emit_dotinfo ( char *targetpath, char *pndrun, pnd_disco_t *p ) {
248   char filename [ FILENAME_MAX ];
249   char buffer [ 1024 ];
250   FILE *f;
251   char *viewer, *searchpath;
252   pnd_conf_handle desktoph;
253
254   // specification
255   // http://standards.freedesktop.org/desktop-entry-spec
256
257   // validation
258   //
259
260   // viewer
261   searchpath = pnd_conf_query_searchpath();
262
263   desktoph = pnd_conf_fetch_by_id ( pnd_conf_desktop, searchpath );
264
265   if ( ! desktoph ) {
266     return ( 0 );
267   }
268
269   viewer = pnd_conf_get_as_char ( desktoph, "info.viewer" );
270
271   if ( ! viewer ) {
272     return ( 0 ); // no way to view the file
273   }
274
275   // etc
276   if ( ! p -> unique_id ) {
277     pnd_log ( PND_LOG_DEFAULT, "Can't emit dotdesktop for %s, missing unique-id\n", targetpath );
278     return ( 0 );
279   }
280
281   if ( ! p -> info_filename ) {
282     pnd_log ( PND_LOG_DEFAULT, "Can't emit dotdesktop for %s, missing info_filename\n", targetpath );
283     return ( 0 );
284   }
285
286   if ( ! p -> info_name ) {
287     pnd_log ( PND_LOG_DEFAULT, "Can't emit dotdesktop for %s, missing info_name\n", targetpath );
288     return ( 0 );
289   }
290
291   if ( ! targetpath ) {
292     pnd_log ( PND_LOG_DEFAULT, "Can't emit dotdesktop for %s, missing target path\n", targetpath );
293     return ( 0 );
294   }
295
296   if ( ! pndrun ) {
297     pnd_log ( PND_LOG_DEFAULT, "Can't emit dotdesktop for %s, missing pnd_run.sh\n", targetpath );
298     return ( 0 );
299   }
300
301   // set up
302
303   sprintf ( filename, "%s/%s#%uinfo.desktop", targetpath, p -> unique_id, p -> subapp_number );
304
305   // emit
306
307   f = fopen ( filename, "w" );
308
309   if ( ! f ) {
310     return ( 0 );
311   }
312
313   fprintf ( f, "%s\n", PND_DOTDESKTOP_HEADER );
314
315   if ( p -> info_name ) {
316     snprintf ( buffer, 1020, "Name=%s\n", p -> info_name );
317     fprintf ( f, "%s", buffer );
318   }
319
320   fprintf ( f, "Type=Application\n" );
321   fprintf ( f, "Version=1.0\n" );
322
323 #if 0
324   if ( p -> icon ) {
325     snprintf ( buffer, 1020, "Icon=%s\n", p -> icon );
326     fprintf ( f, "%s", buffer );
327   }
328 #endif
329
330   if ( p -> unique_id ) {
331     fprintf ( f, "X-Pandora-UID=%s\n", p -> unique_id );
332   }
333
334   if ( p -> title_en && p -> title_en [ 0 ] ) {
335     snprintf ( buffer, 1020, "Comment=Automatic menu info entry for %s\n", p -> title_en );
336     fprintf ( f, "%s", buffer );
337   }
338
339 #if 0 // we let pnd_run.sh command line handle this instead of in .desktop
340   if ( p -> startdir ) {
341     snprintf ( buffer, 1020, "Path=%s\n", p -> startdir );
342     fprintf ( f, "%s", buffer );
343   } else {
344     fprintf ( f, "Path=%s\n", PND_DEFAULT_WORKDIR );
345   }
346 #endif
347
348   // exec line
349   char args [ 1001 ];
350   char *pargs = args;
351   char *viewerargs = pnd_conf_get_as_char ( desktoph, "info.viewer_args" );
352   if ( viewerargs && viewerargs [ 0 ] ) {
353     snprintf ( pargs, 1001, "%s %s",
354                pnd_conf_get_as_char ( desktoph, "info.viewer_args" ), p -> info_filename );
355   } else {
356     pargs = NULL;
357     // WARNING: This might not be quite right; if no viewer-args, shouldn't we still append the info-filename? likewise,
358     //          even if we do have view-args, shouldn't we check if filename is present?
359   }
360
361   char pndfile [ 1024 ];
362   if ( p -> object_type == pnd_object_type_directory ) {
363     // for PXML-app-dir, pnd_run.sh doesn't want the PXML.xml.. it just wants the dir-name
364     strncpy ( pndfile, p -> object_path, 1000 );
365   } else if ( p -> object_type == pnd_object_type_pnd ) {
366     // pnd_run.sh wants the full path and filename for the .pnd file
367     snprintf ( pndfile, 1020, "%s/%s", p -> object_path, p -> object_filename );
368   }
369
370   pnd_apps_exec_info_t info;
371   info.viewer = viewer;
372   info.args = pargs;
373
374   if ( ! pnd_apps_exec_disco ( pndrun, p, PND_EXEC_OPTION_NORUN | PND_EXEC_OPTION_INFO, &info ) ) {
375     return ( 0 );
376   }
377
378   fprintf ( f, "Exec=%s\n", pnd_apps_exec_runline() );
379
380   if ( pnd_conf_get_as_char ( desktoph, "info.category" ) ) {
381     fprintf ( f, "Categories=%s\n", pnd_conf_get_as_char ( desktoph, "info.category" ) );
382   } else {
383     fprintf ( f, "Categories=Documentation\n" );
384   }
385
386   fprintf ( f, "%s\n", PND_DOTDESKTOP_SOURCE ); // should we need to know 'who' created the file during trimming
387
388   fclose ( f );
389
390   return ( 1 );
391 }
392
393 unsigned char pnd_emit_icon ( char *targetpath, pnd_disco_t *p ) {
394   //#define BITLEN (8*1024)
395 #define BITLEN (64*1024)
396   char buffer [ FILENAME_MAX ]; // target filename
397   char from [ FILENAME_MAX ];   // source filename
398   unsigned char bits [ BITLEN ];
399   unsigned int bitlen;
400   FILE *pnd, *target;
401
402   // prelim .. if a pnd file, and no offset found, discovery code didn't locate icon.. so bail.
403   if ( ( p -> object_type == pnd_object_type_pnd ) &&
404        ( ! p -> pnd_icon_pos ) )
405   {
406     return ( 0 ); // discover code didn't find it, so FAIL
407   }
408
409   // determine filename for target
410   sprintf ( buffer, "%s/%s.png", targetpath, p -> unique_id /*, p -> subapp_number*/ ); // target
411
412   /* first.. open the source file, by type of application:
413    * are we looking through a pnd file or a dir?
414    */
415   if ( p -> object_type == pnd_object_type_directory ) {
416     sprintf ( from, "%s/%s", p -> object_path, p -> icon );
417   } else if ( p -> object_type == pnd_object_type_pnd ) {
418     sprintf ( from, "%s/%s", p -> object_path, p -> object_filename );
419   }
420
421   pnd = fopen ( from, "rb" );
422
423   if ( ! pnd ) {
424     pnd_log ( PND_LOG_DEFAULT, "    Emit icon, couldn't open source\n" );
425     return ( 0 );
426   }
427
428   unsigned int len;
429
430   target = fopen ( buffer, "wb" );
431
432   if ( ! target ) {
433     fclose ( pnd );
434     pnd_log ( PND_LOG_DEFAULT, "    Emit icon, couldn't open target\n" );
435     return ( 0 );
436   }
437
438   fseek ( pnd, 0, SEEK_END );
439   len = ftell ( pnd );
440   //fseek ( pnd, 0, SEEK_SET );
441
442   fseek ( pnd, p -> pnd_icon_pos, SEEK_SET );
443
444   len -= p -> pnd_icon_pos;
445
446   pnd_log ( PND_LOG_DEFAULT, "    Emit icon, length: %u\n", len );
447
448   while ( len ) {
449
450     if ( len > (BITLEN) ) {
451       bitlen = (BITLEN);
452     } else {
453       bitlen = len;
454     }
455
456     if ( fread ( bits, bitlen, 1, pnd ) != 1 ) {
457       fclose ( pnd );
458       fclose ( target );
459       unlink ( buffer );
460       pnd_log ( PND_LOG_DEFAULT, "    Emit icon, bad read\n" );
461       return ( 0 );
462     }
463
464 #if 0
465     {
466       unsigned int i = 0;
467       char bigbuffer [ 200 * 1024 ] = "\0";
468       char b [ 10 ];
469       pnd_log ( PND_LOG_DEFAULT, "    Read hexdump\n" );
470       while ( i < bitlen ) {
471         sprintf ( b, "%x,", bits [ i ] );
472         strcat ( bigbuffer, b );
473         i++;
474       }
475       pnd_log ( PND_LOG_DEFAULT, bigbuffer );
476     }
477 #endif
478
479     if ( fwrite ( bits, bitlen, 1, target ) != 1 ) {
480       fclose ( pnd );
481       fclose ( target );
482       unlink ( buffer );
483       pnd_log ( PND_LOG_DEFAULT, "    Emit icon, bad write\n" );
484       return ( 0 );
485     }
486
487     len -= bitlen;
488     //pnd_log ( PND_LOG_DEFAULT, "    Emit icon, next block, length: %u\n", len );
489   } // while
490
491   fclose ( pnd );
492   fclose ( target );
493
494   //pnd_log ( PND_LOG_DEFAULT, "    Emit icon, done.\n" );
495
496   return ( 1 );
497 }
498
499 #if 1 // we switched direction to freedesktop standard categories
500 // if no categories herein, return 0; otherwise, if some category-like-text, return 1
501 int pnd_map_dotdesktop_categories ( pnd_conf_handle c, char *target_buffer, unsigned short int len, pnd_disco_t *d ) {
502   char *t;
503   char *match;
504
505   // clear target so we can easily append
506   memset ( target_buffer, '\0', len );
507
508   // for each main-cat and sub-cat, including alternates, just append them all together
509   // we'll try mapping them, since the categories file is there, but we'll default to
510   // copying over; this lets the conf file do merging or renaming of cagtegories, which
511   // could still be useful, but we can leave the conf file empty to effect a pure
512   // trusted-PXML-copying
513
514   // it would be sort of cumbersome to copy all the freedesktop.org defined categories (as
515   // there are hundreds), and would also mean new ones and peoples custom ones would
516   // flop
517
518   /* attempt primary category chain
519    */
520   #define MAPCAT(field)                                     \
521     if ( ( t = d -> field ) ) {                             \
522       match = pnd_map_dotdesktop_category ( c, t );         \
523       strncat ( target_buffer, match ? match : t, len );    \
524       strncat ( target_buffer, ";", len );                  \
525     }
526
527   MAPCAT(main_category);
528   MAPCAT(main_category1);
529   MAPCAT(main_category2);
530   MAPCAT(alt_category);
531   MAPCAT(alt_category1);
532   MAPCAT(alt_category2);
533
534   if ( target_buffer [ 0 ] ) {
535     return ( 1 ); // I guess its 'good'?
536   }
537
538 #if 0
539   if ( ( t = d -> main_category ) ) {
540     match = pnd_map_dotdesktop_category ( c, t );
541     strncat ( target_buffer, match ? match : t, len );
542     strncat ( target_buffer, ";", len );
543   }
544 #endif
545
546   return ( 0 );
547 }
548 #endif
549
550 #if 0 // we switched direction
551 //int pnd_map_dotdesktop_categories ( pnd_conf_handle c, char *target_buffer, unsigned short int len, pnd_pxml_handle h ) {
552 int pnd_map_dotdesktop_categories ( pnd_conf_handle c, char *target_buffer, unsigned short int len, pnd_disco_t *d ) {
553   unsigned short int n = 0; // no. matches
554   char *t;
555   char *match;
556
557   // clear target so we can easily append
558   memset ( target_buffer, '\0', len );
559
560   /* attempt primary category chain
561    */
562   match = NULL;
563
564   if ( ( t = d -> main_category ) ) {
565     match = pnd_map_dotdesktop_category ( c, t );
566   }
567
568   if ( ( ! match ) &&
569        ( t = d -> main_category1 ) )
570   {
571     match = pnd_map_dotdesktop_category ( c, t );
572   }
573
574   if ( ( ! match ) &&
575        ( t = d -> main_category2 ) )
576   {
577     match = pnd_map_dotdesktop_category ( c, t );
578   }
579
580   if ( match ) {
581     strncat ( target_buffer, match, len );
582     len -= strlen ( target_buffer );
583     n += 1;
584   }
585
586   /* attempt secondary category chain
587    */
588   match = NULL;
589
590   if ( ( t = d -> alt_category ) ) {
591     match = pnd_map_dotdesktop_category ( c, t );
592   }
593
594   if ( ( ! match ) &&
595        ( t = d -> alt_category1 ) )
596   {
597     match = pnd_map_dotdesktop_category ( c, t );
598   }
599
600   if ( ( ! match ) &&
601        ( t = d -> alt_category2 ) )
602   {
603     match = pnd_map_dotdesktop_category ( c, t );
604   }
605
606   if ( match ) {
607     if ( target_buffer [ 0 ] != '\0' && len > 0 ) {
608       strcat ( target_buffer, ";" );
609       len -= 1;
610     }
611     strncat ( target_buffer, match, len );
612     len -= strlen ( target_buffer );
613     n += 1;
614   }
615
616 #if 0 // doh, originally I was using pxml-t till I realized I pre-boned myself on that one
617   match = NULL;
618
619   if ( ( t = pnd_pxml_get_main_category ( h ) ) ) {
620     match = pnd_map_dotdesktop_category ( c, t );
621   }
622
623   if ( ( ! match ) &&
624        ( t = pnd_pxml_get_subcategory1 ( h ) ) )
625   {
626     match = pnd_map_dotdesktop_category ( c, t );
627   }
628
629   if ( ( ! match ) &&
630        ( t = pnd_pxml_get_subcategory2 ( h ) ) )
631   {
632     match = pnd_map_dotdesktop_category ( c, t );
633   }
634
635   if ( match ) {
636     strncat ( target_buffer, match, len );
637     len -= strlen ( target_buffer );
638     n += 1;
639   }
640
641   /* attempt secondary category chain
642    */
643   match = NULL;
644
645   if ( ( t = pnd_pxml_get_altcategory ( h ) ) ) {
646     match = pnd_map_dotdesktop_category ( c, t );
647   }
648
649   if ( ( ! match ) &&
650        ( t = pnd_pxml_get_altsubcategory1 ( h ) ) )
651   {
652     match = pnd_map_dotdesktop_category ( c, t );
653   }
654
655   if ( ( ! match ) &&
656        ( t = pnd_pxml_get_altsubcategory2 ( h ) ) )
657   {
658     match = pnd_map_dotdesktop_category ( c, t );
659   }
660
661   if ( match ) {
662     if ( target_buffer [ 0 ] != '\0' && len > 0 ) {
663       strcat ( target_buffer, ";" );
664       len -= 1;
665     }
666     strncat ( target_buffer, match, len );
667     len -= strlen ( target_buffer );
668     n += 1;
669   }
670 #endif
671
672   if ( n && len ) {
673     strcat ( target_buffer, ";" );
674   }
675
676   return ( n );
677 }
678 #endif
679
680 // given category 'foo', look it up in the provided config map. return the char* reference, or NULL
681 char *pnd_map_dotdesktop_category ( pnd_conf_handle c, char *single_category ) {
682   char *key;
683   char *ret;
684
685   key = malloc ( strlen ( single_category ) + 4 + 1 );
686
687   sprintf ( key, "map.%s", single_category );
688
689   ret = pnd_conf_get_as_char ( c, key );
690
691   free ( key );
692
693   return ( ret );
694 }
695
696 unsigned char *pnd_emit_icon_to_buffer ( pnd_disco_t *p, unsigned int *r_buflen ) {
697   // this is shamefully mostly a copy of emit_icon() above; really, need to refactor that to use this routine
698   // with a fwrite at the end...
699   char from [ FILENAME_MAX ];   // source filename
700   char bits [ 8 * 1024 ];
701   unsigned int bitlen;
702   FILE *pnd = NULL;
703   unsigned char *target = NULL, *targiter = NULL;
704
705   // prelim .. if a pnd file, and no offset found, discovery code didn't locate icon.. so bail.
706   if ( ( p -> object_type == pnd_object_type_pnd ) &&
707        ( ! p -> pnd_icon_pos ) )
708   {
709     return ( NULL ); // discover code didn't find it, so FAIL
710   }
711
712   /* first.. open the source file, by type of application:
713    * are we looking through a pnd file or a dir?
714    */
715   if ( p -> object_type == pnd_object_type_directory ) {
716     sprintf ( from, "%s/%s", p -> object_path, p -> icon );
717   } else if ( p -> object_type == pnd_object_type_pnd ) {
718     sprintf ( from, "%s/%s", p -> object_path, p -> object_filename );
719   }
720
721   pnd = fopen ( from, "r" );
722
723   if ( ! pnd ) {
724     return ( NULL );
725   }
726
727   // determine length of file, then adjust by icon position to find begin of icon
728   unsigned int len;
729
730   fseek ( pnd, 0, SEEK_END );
731   len = ftell ( pnd );
732   //fseek ( pnd, 0, SEEK_SET );
733
734   fseek ( pnd, p -> pnd_icon_pos, SEEK_SET );
735
736   len -= p -> pnd_icon_pos;
737
738   // create target buffer
739   target = malloc ( len );
740
741   if ( ! target ) {
742     fclose ( pnd );
743     return ( 0 );
744   }
745
746   targiter = target;
747
748   if ( r_buflen ) {
749     *r_buflen = len;
750   }
751
752   // copy over icon to target
753   while ( len ) {
754
755     if ( len > (8*1024) ) {
756       bitlen = (8*1024);
757     } else {
758       bitlen = len;
759     }
760
761     if ( fread ( bits, bitlen, 1, pnd ) != 1 ) {
762       fclose ( pnd );
763       free ( target );
764       return ( NULL );
765     }
766
767     memmove ( targiter, bits, bitlen );
768     targiter += bitlen;
769
770     len -= bitlen;
771   } // while
772
773   fclose ( pnd );
774
775   return ( target );
776 }
777
778 // parse_dotdesktop() can be used to read a libpnd generated .desktop and return a limited
779 // but useful disco-t structure back; possibly useful for scanning .desktops rather than
780 // scanning pnd-files?
781 pnd_disco_t *pnd_parse_dotdesktop ( char *ddpath, unsigned int flags ) {
782
783   // will verify the .desktop has the libpnd-marking on it (X-Pandora-Source): PND_DOTDESKTOP_SOURCE
784
785   // attempt to extract..
786   // - unique-id (from filename or field)
787   // - subapp number (from filename)
788   // - exec required info
789   // - icon path
790   // - name (title-en)
791   // - comment (desc-en)
792   // - option_no_x11
793   // - object path
794   // - appdata name (or unique-id if not present)
795   // - start dir
796   // - args
797   // - clockspeed
798   // - categories
799
800   char pndpath [ 1024 ];
801   bzero ( pndpath, 1024 );
802
803   // filter on filename?
804   if ( flags & PND_DOTDESKTOP_LIBPND_ONLY ) {
805     // too bad we didn't put some libpnd token at the front of the filename or something
806     // hell, we should cleanse unique-id to ensure its not full of special chars like '*' and '..'.. eep!
807     if ( strrchr ( ddpath, '#' ) == NULL ) { // but if requiring libpnd, we can at least check for #subapp-number
808       return ( NULL );
809     }
810   }
811
812   if ( strstr ( ddpath, ".desktop" ) == NULL ) {
813     return ( NULL ); // no .desktop in filename, must be something else... skip!
814   }
815
816   if ( strstr ( ddpath, "info.desktop" ) != NULL ) {
817     // ".....info.desktop" is the 'document help' (README) emitted from a pnd, not an actual app; minimenu rather
818     // expects the doc-info as part of the main app, not a separate app.. so lets drop it here, to avoid doubling up
819     // the number of applications, needlessly..
820     return ( NULL );
821   }
822
823   // determine file length
824   struct stat statbuf;
825
826   if ( stat ( ddpath, &statbuf) < 0 ) {
827     return ( NULL ); // couldn't open
828   }
829
830   // buffers..
831   char dd [ 1024 ];
832   unsigned char libpnd_origin = 0;
833
834   // disco
835   pnd_disco_t *p = malloc ( sizeof(pnd_disco_t) );
836   if ( ! p ) {
837     return ( NULL );
838   }
839   bzero ( p, sizeof(pnd_disco_t) );
840
841   // inhale file
842   FILE *f = fopen ( ddpath, "r" );
843
844   if ( ! f ) {
845     return ( NULL ); // not up or shut up!
846   }
847
848   while ( fgets ( dd, 1024, f ) ) {
849     char *nl = strchr ( dd, '\n' );
850     if ( nl ) {
851       *nl = '\0';
852     }
853
854     // grep
855     //
856
857     if ( strncmp ( dd, "Name=", 5 ) == 0 ) {
858       p -> title_en = strdup ( dd + 5 );
859     } else if ( strncmp ( dd, "Name[en]=", 9 ) == 0 ) {
860       p -> title_en = strdup ( dd + 9 );
861     } else if ( strncmp ( dd, "Icon=", 5 ) == 0 ) {
862       p -> icon = strdup ( dd + 5 );
863     } else if ( strcmp ( dd, PND_DOTDESKTOP_SOURCE ) == 0 ) {
864       libpnd_origin = 1;
865     } else if ( strncmp ( dd, "X-Pandora-UID=", 14 ) == 0 ) {
866       p -> unique_id = strdup ( dd + 14 );
867     } else if ( strncmp ( dd, "X-Pandora-Preview-Pic-1=", 24 ) == 0 ) {
868       p -> preview_pic1 = strdup ( dd + 24 );
869     } else if ( strncmp ( dd, "X-Pandora-Clockspeed=", 21 ) == 0 ) {
870       p -> clockspeed = strdup ( dd + 21 );
871     } else if ( strncmp ( dd, "X-Pandora-Startdir=", 19 ) == 0 ) {
872       p -> startdir = strdup ( dd + 19 );
873     } else if ( strncmp ( dd, "X-Pandora-Appdata-Dirname=", 26 ) == 0 ) {
874       p -> appdata_dirname = strdup ( dd + 26 );
875     } else if ( strncmp ( dd, "X-Pandora-ExecArgs=", 19 ) == 0 ) {
876       p -> execargs = strdup ( dd + 19 );
877     } else if ( strncmp ( dd, "X-Pandora-Exec=", 15 ) == 0 ) {
878       p -> exec = strdup ( dd + 15 );
879     } else if ( strncmp ( dd, "X-Pandora-Object-Path=", 22 ) == 0 ) {
880       p -> object_path = strdup ( dd + 22 );
881     } else if ( strncmp ( dd, "X-Pandora-Object-Filename=", 26 ) == 0 ) {
882       p -> object_filename = strdup ( dd + 26 );
883     } else if ( strncmp ( dd, "X-Pandora-Object-Flag-OVR=", 26 ) == 0 ) {
884       p -> object_flags |= PND_DISCO_FLAG_OVR;
885
886     } else if ( strncmp ( dd, "X-Pandora-MainCategory=", 23 ) == 0 ) {
887       p -> main_category = strdup ( dd + 23 );
888     } else if ( strncmp ( dd, "X-Pandora-MainCategory1=", 24 ) == 0 ) {
889       p -> main_category1 = strdup ( dd + 24 );
890     } else if ( strncmp ( dd, "X-Pandora-MainCategory2=", 24 ) == 0 ) {
891       p -> main_category2 = strdup ( dd + 24 );
892
893     } else if ( strncmp ( dd, "X-Pandora-AltCategory=", 22 ) == 0 ) {
894       p -> alt_category = strdup ( dd + 22 );
895     } else if ( strncmp ( dd, "X-Pandora-AltCategory1=", 23 ) == 0 ) {
896       p -> alt_category1 = strdup ( dd + 23 );
897     } else if ( strncmp ( dd, "X-Pandora-AltCategory2=", 23 ) == 0 ) {
898       p -> alt_category2 = strdup ( dd + 23 );
899
900     } else if ( strncmp ( dd, "X-Pandora-Info-Filename=", 24 ) == 0 ) {
901       p -> info_filename = strdup ( dd + 24 );
902     } else if ( strncmp ( dd, "X-Pandora-Info-Name=", 20 ) == 0 ) {
903       p -> info_name = strdup ( dd + 20 );
904
905     } else if ( strncmp ( dd, "Comment=", 8 ) == 0 ) {
906       p -> desc_en = strdup ( dd + 8 );
907     } else if ( strncmp ( dd, "Comment[en]=", 12 ) == 0 ) {
908       p -> desc_en = strdup ( dd + 12 );
909     } else if ( strncmp ( dd, "Exec=", 5 ) == 0 ) {
910
911       char *e = strstr ( dd, " -e " );
912
913       if ( e ) {
914         // probably libpnd app
915 #if 0 // no needed due to above X-Pandora attributes
916
917         if ( e ) {
918           e += 5;
919
920           char *space = strchr ( e, ' ' );
921           p -> exec = strndup ( e, space - e - 1 );
922         }
923
924         char *b = strstr ( dd, " -b " );
925         if ( b ) {
926           b += 5;
927           char *space = strchr ( b, '\0' );
928           p -> appdata_dirname = strndup ( b, space - b - 1 );
929         }
930
931         char *p = strstr ( dd, " -p " );
932         if ( p ) {
933           p += 5;
934           char *space = strchr ( p, ' ' );
935           strncpy ( pndpath, p, space - p - 1 );
936         }
937 #endif
938
939       } else {
940         // probably not libpnd app
941         p -> exec = strdup ( dd + 5 );
942       }
943
944 #if 0 // ignore; using X- categories now
945     } else if ( strncmp ( dd, "Categories=", 11 ) == 0 ) {
946       // HACK; only honours first category
947       char *semi = strchr ( dd, ';' );
948       if ( semi ) {
949         p -> main_category = strndup ( dd + 11, semi - dd + 11 );
950       } else {
951         p -> main_category = strdup ( dd + 11 );
952       }
953       semi = strchr ( p -> main_category, ';' );
954       if ( semi ) {
955         *semi = '\0';
956       }
957 #endif
958
959     }
960
961     //
962     // /grep
963
964   } // while
965
966   fclose ( f );
967
968   // filter
969   if ( ! libpnd_origin ) {
970
971     // convenience flag
972     if ( flags & PND_DOTDESKTOP_LIBPND_ONLY ) {
973       pnd_disco_destroy ( p );
974       free ( p );
975       return ( NULL );
976     }
977
978   } else {
979     p -> object_flags |= PND_DISCO_LIBPND_DD; // so caller can do something if it wishes
980   }
981
982   // filter on content
983   if ( ( ! p -> title_en ) ||
984        ( ! p -> exec )
985      )
986   {
987     pnd_disco_destroy ( p );
988     free ( p );
989     return ( NULL );
990   }
991
992   if ( ! p -> unique_id ) {
993     if ( flags & PND_DOTDESKTOP_LIBPND_ONLY ) {
994       pnd_disco_destroy ( p );
995       free ( p );
996       return ( NULL );
997     } else {
998       char hack [ 100 ];
999       snprintf ( hack, 100, "inode-%lu", statbuf.st_ino );
1000       p -> unique_id = strdup ( hack );
1001     }
1002   }
1003
1004   // additional
1005   p -> object_type = pnd_object_type_pnd;
1006
1007 #if 0 // nolonger needed due to above X-Pandora attributes
1008   char *source;
1009   if ( pndpath [ 0 ] ) {
1010     source = pndpath;
1011   } else {
1012     source = ddpath;
1013   }
1014
1015   char *slash = strrchr ( source, '/' );
1016   if ( slash ) {
1017     p -> object_path = strndup ( source, slash - source );
1018     p -> object_filename = strdup ( slash + 1 );
1019   } else {
1020     p -> object_path = "./";
1021     p -> object_filename = strdup ( source );
1022   }
1023 #endif
1024
1025   // lame guards, in case of lazy consumers and broken .desktop files
1026   if ( p -> object_path == NULL ) {
1027     p -> object_path = strdup ( "/tmp" );
1028   }
1029   if ( p -> object_filename == NULL ) {
1030     p -> object_filename = strdup ( "" ); // force bad filename
1031   }
1032
1033   // return disco-t
1034   return ( p );
1035 }