11 #include "pnd_logger.h"
13 #include "pnd_container.h"
14 #include "pnd_discovery.h"
15 #include "../lib/pnd_pathiter.h"
21 mm_category_t _categories [ MAX_CATS ];
22 mm_category_t *g_categories = _categories;
23 unsigned char g_categorycount = 0;
25 mm_category_t _categories_invis [ MAX_CATS ];
26 unsigned char _categories_inviscount = 0;
28 mm_catmap_t g_catmaps [ MAX_CATS ];
29 unsigned char g_catmapcount = 0;
31 extern pnd_conf_handle g_conf;
33 unsigned char category_push ( char *catname, pnd_disco_t *app, pnd_conf_handle ovrh, char *fspath ) {
36 // check category list; if found, append app to the end of it.
37 // if not found, add it to the category list and plop the app in there.
38 // app's are just app-refs, which contain links to the disco-t list -- thus removal is only in one place, and
39 // an app can be in multiple categories if we like..
42 // find or create category
45 if ( ( c = category_query ( catname ) ) ) {
46 // category was found..
48 // category wasn't found..
49 //pnd_log ( PND_LOG_DEFAULT, "New category '%s'\n", catname );
50 g_categories [ g_categorycount ].catname = strdup ( catname );
51 g_categories [ g_categorycount ].refs = NULL;
52 c = &(g_categories [ g_categorycount ]);
55 g_categories [ g_categorycount ].fspath = strdup ( fspath );;
62 return ( 1 ); // create cat, but skip app
65 // alloc and populate appref
67 mm_appref_t *ar = malloc ( sizeof(mm_appref_t) );
72 bzero ( ar, sizeof(mm_appref_t) );
77 // plug it into category
78 // and sort it on insert!
80 ar -> next = c -> refs;
83 // if no refs at all, or new guy has no title, just stick it in at head
84 if ( c -> refs && ar -> ref -> title_en ) {
85 mm_appref_t *iter = c -> refs;
86 mm_appref_t *last = NULL;
90 if ( iter -> ref -> title_en ) {
91 if ( cat_sort_score ( ar, iter ) < 0 ) {
92 // new guy is smaller than the current guy!
96 // since new guy must have a name by here, we're bigger than any guy who does not have a name
105 // smaller than the current guy, so stitch in
110 ar -> next = c -> refs;
114 // we're the biggest, just append to last
119 ar -> next = c -> refs;
128 mm_category_t *category_query ( char *catname ) {
131 for ( i = 0; i < g_categorycount; i++ ) {
133 if ( strcasecmp ( g_categories [ i ].catname, catname ) == 0 ) {
134 return ( &(g_categories [ i ]) );
142 int cat_sort_score ( mm_appref_t *s1, mm_appref_t *s2 ) {
144 extern unsigned char ui_category;
146 // are we in a directory browser, or looking at pnd-files?
147 if ( g_categories [ ui_category ].fspath ) {
150 return ( 0 ); // equal
152 } else if ( s1 -> ref -> object_type == pnd_object_type_directory &&
153 s2 -> ref -> object_type == pnd_object_type_directory )
155 // both are directories, be nice
156 return ( strcmp ( s1 -> ref -> title_en, s2 -> ref -> title_en ) );
157 } else if ( s1 -> ref -> object_type == pnd_object_type_directory &&
158 s2 -> ref -> object_type != pnd_object_type_directory )
160 return ( -1 ); // dir on the left is earlier than file on the right
161 } else if ( s1 -> ref -> object_type != pnd_object_type_directory &&
162 s2 -> ref -> object_type == pnd_object_type_directory )
164 return ( 1 ); // dir on the right is earlier than file on the left
167 return ( strcmp ( s1 -> ref -> title_en, s2 -> ref -> title_en ) );
172 return ( strcasecmp ( s1 -> ref -> title_en, s2 -> ref -> title_en ) );
175 void category_dump ( void ) {
178 // WHY AREN'T I SORTING ON INSERT?
181 for ( i = 0; i < g_categorycount; i++ ) {
182 pnd_log ( PND_LOG_DEFAULT, "Category %u: '%s' * %u\n", i, g_categories [ i ].catname, g_categories [ i ].refcount );
183 mm_appref_t *ar = g_categories [ i ].refs;
186 pnd_log ( PND_LOG_DEFAULT, " Appref %s\n", IFNULL(ar -> ref -> title_en,"No Name") );
195 static void _category_freeall ( mm_category_t *p, unsigned int c ) {
197 mm_appref_t *iter, *next;
199 for ( i = 0; i < c; i++ ) {
211 if ( p [ i ].catname ) {
212 free ( p [ i ].catname );
213 p [ i ].catname = NULL;
216 if ( p [ i ].fspath ) {
217 free ( p [ i ].fspath );
218 p [ i ].fspath = NULL;
226 void category_freeall ( void ) {
228 _category_freeall ( g_categories, g_categorycount );
229 _category_freeall ( _categories_invis, _categories_inviscount );
232 _categories_inviscount = 0;
237 unsigned char category_map_setup ( void ) {
239 char *searchpath = pnd_box_get_head ( g_conf );
241 if ( ! searchpath ) {
245 // look through conf for useful keys
246 while ( searchpath ) {
247 char *k = pnd_box_get_key ( searchpath );
249 // does this key look like a category mapping key?
250 if ( strncasecmp ( k, "categories.@", 12 ) == 0 ) {
253 // iterate across 'words' in v, assigning catmaps to them
256 //pnd_log ( pndn_debug, "target(%s) from(%s)\n", k, buffer );
258 category_push ( k, NULL, 0, NULL /* fspath */ );
259 g_catmaps [ g_catmapcount ].target = category_query ( k );
260 g_catmaps [ g_catmapcount ].from = strdup ( buffer );
266 } // if key looks like catmap
268 searchpath = pnd_box_get_next ( searchpath );
269 } // while each conf key
274 mm_category_t *category_map_query ( char *cat ) {
277 for ( i = 0; i < g_catmapcount; i++ ) {
278 if ( strcasecmp ( g_catmaps [ i ].from, cat ) == 0 ) {
279 return ( g_catmaps [ i ].target );
286 unsigned char category_meta_push ( char *catname, char *parentcatname, pnd_disco_t *app, pnd_conf_handle ovrh, unsigned char visiblep ) {
288 unsigned char catcount = g_categorycount;
289 char catnamebuffer [ 512 ] = "";
292 return ( 1 ); // fine, just nada
295 //fprintf ( stderr, "meta push: '%s'\n", catname );
298 //return ( 1 ); // fine, suppress it
300 // serious evidence this was a rushed program
301 g_categories = _categories_invis;
302 g_categorycount = _categories_inviscount;
304 // if invisible, and a parent category name is known, prepend it for ease of use
305 if ( parentcatname ) {
306 snprintf ( catnamebuffer, 500, "%s.%s", parentcatname, catname );
307 catname = catnamebuffer;
312 // do we honour cat mapping at all?
313 if ( pnd_conf_get_as_int_d ( g_conf, "categories.map_on", 0 ) ) {
315 // is this guy mapped?
316 cat = category_map_query ( catname );
319 category_push ( cat -> catname, app, ovrh, NULL /* fspath */ );
320 goto visibility_hack_cleanup;
323 // not mapped.. but default?
324 if ( pnd_conf_get_as_int_d ( g_conf, "categories.map_default_on", 0 ) ) {
325 char *def = pnd_conf_get_as_char ( g_conf, "categories.map_default_cat" );
327 category_push ( def, app, ovrh, NULL /* fspath */ );
328 goto visibility_hack_cleanup;
332 } // cat map is desired?
334 // not default, just do it
335 category_push ( catname, app, ovrh, NULL /* fspath */ );
338 visibility_hack_cleanup:
340 _categories_inviscount = g_categorycount;
341 g_categories = _categories;
342 g_categorycount = catcount;
345 //fprintf ( stderr, "cat meta-push : vis[%30s,%d b] : tally; vis %d invis %d\n", catname, visiblep, g_categorycount, _categories_inviscount );
350 unsigned char category_fs_restock ( mm_category_t *cat ) {
352 if ( ! cat -> fspath ) {
353 return ( 1 ); // not a filesystem browser tab
356 // clear any existing baggage
360 mm_appref_t *iter = cat -> refs, *next;
369 if ( cat -> disco ) {
370 pnd_disco_t *p = pnd_box_get_head ( cat -> disco );
373 n = pnd_box_get_next ( p );
374 pnd_disco_destroy ( p );
377 pnd_box_delete ( cat -> disco );
380 // rescan the filesystem
383 //pnd_log ( pndn_debug, "Restocking cat %s with path %s\n", cat -> catname, cat -> fspath );
386 if ( ( d = opendir ( cat -> fspath ) ) ) {
387 struct dirent *de = readdir ( d );
392 cat -> disco = pnd_box_new ( cat -> catname );
397 char fullpath [ PATH_MAX ];
398 sprintf ( fullpath, "%s/%s", cat -> fspath, de -> d_name );
399 int statret = stat ( fullpath, &buffy );
401 // if file is executable somehow or another
403 buffy.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)
406 // determine unique-id
407 sprintf ( uid, "%d", (int) de -> d_ino );
410 switch ( de -> d_type ) {
413 if ( strcmp ( de -> d_name, "." ) == 0 ) {
414 // ignore ".", but ".." is fine
416 disco = pnd_box_allocinsert ( cat -> disco, uid, sizeof(pnd_disco_t) );
417 disco -> object_type = pnd_object_type_directory; // suggest to Grid that its a dir
422 disco = pnd_box_allocinsert ( cat -> disco, uid, sizeof(pnd_disco_t) );
423 disco -> object_type = pnd_object_type_unknown; // suggest to Grid that its a file
428 // found a directory or executable?
430 // register with this category
431 disco -> unique_id = strdup ( uid );
432 disco -> title_en = strdup ( de -> d_name );
433 disco -> object_flags = PND_DISCO_GENERATED;
434 disco -> object_path = strdup ( cat -> fspath );
435 disco -> object_filename = strdup ( de -> d_name );
436 category_push ( cat -> catname, disco, 0, NULL /* fspath already set */ );
437 // if a override icon exists, cache it up
438 cache_icon ( disco, pnd_conf_get_as_int_d ( g_conf, "grid.icon_max_width", 50 ),
439 pnd_conf_get_as_int_d ( g_conf, "grid.icon_max_height", 50 ) );
454 static int catname_cmp ( const void *p1, const void *p2 ) {
455 mm_category_t *c1 = (mm_category_t*) p1;
456 mm_category_t *c2 = (mm_category_t*) p2;
457 return ( strcasecmp ( c1 -> catname, c2 -> catname ) );
460 void category_sort ( void ) {
461 // we probably don't want to sort tab categories, since the user may have specified an ordering
462 // But we can sort invisi-cats, to make them easier to find, and ordered by parent category
464 qsort ( _categories_invis, _categories_inviscount, sizeof(mm_category_t), catname_cmp );