12 #include "pnd_logger.h"
14 #include "pnd_container.h"
15 #include "pnd_discovery.h"
16 #include "../lib/pnd_pathiter.h"
22 // all categories known -- visible, hidden, subcats, whatever.
23 pnd_box_handle m_categories = NULL;
24 unsigned int m_categorycount = 0;
26 // all categories being published right now -- a subset copied from m_categories
27 mm_category_t *g_categories [ MAX_CATS ];
28 unsigned char g_categorycount;
31 mm_catmap_t g_catmaps [ MAX_CATS ];
32 unsigned char g_catmapcount = 0;
34 extern pnd_conf_handle g_conf;
36 void category_init ( void ) {
37 m_categories = pnd_box_new ( "Schrodinger's cat" );
41 unsigned char category_push ( char *catname, pnd_disco_t *app, pnd_conf_handle ovrh, char *fspath, unsigned char visiblep ) {
44 // check category list; if found, append app to the end of it.
45 // if not found, add it to the category list and plop the app in there.
46 // app's are just app-refs, which contain links to the disco-t list -- thus removal is only in one place, and
47 // an app can be in multiple categories if we like..
50 // find or create category
53 if ( ( c = pnd_box_find_by_key ( m_categories, catname ) ) ) {
54 // category was found..
56 // category wasn't found..
57 //pnd_log ( PND_LOG_DEFAULT, "New category '%s'\n", catname );
58 c = pnd_box_allocinsert ( m_categories, catname, sizeof(mm_category_t) );
59 c -> catname = strdup ( catname );
61 c -> catflags = CFNORMAL;
63 c -> catflags = CFHIDDEN;
68 c -> fspath = strdup ( fspath );
75 return ( 1 ); // create cat, but skip app
78 // alloc and populate appref
80 mm_appref_t *ar = malloc ( sizeof(mm_appref_t) );
85 bzero ( ar, sizeof(mm_appref_t) );
90 // plug it into category
91 // and sort it on insert!
93 ar -> next = c -> refs;
96 // if no refs at all, or new guy has no title, just stick it in at head
97 if ( c -> refs && ar -> ref -> title_en ) {
98 mm_appref_t *iter = c -> refs;
99 mm_appref_t *last = NULL;
103 if ( iter -> ref -> title_en ) {
104 if ( cat_sort_score ( c, ar, iter ) < 0 ) {
105 // new guy is smaller than the current guy!
109 // since new guy must have a name by here, we're bigger than any guy who does not have a name
118 // smaller than the current guy, so stitch in
123 ar -> next = c -> refs;
127 // we're the biggest, just append to last
132 ar -> next = c -> refs;
141 int cat_sort_score ( mm_category_t *cat, mm_appref_t *s1, mm_appref_t *s2 ) {
143 // are we in a directory browser, or looking at pnd-files?
144 if ( cat -> fspath ) {
147 return ( 0 ); // equal
149 } else if ( s1 -> ref -> object_type == pnd_object_type_directory &&
150 s2 -> ref -> object_type == pnd_object_type_directory )
152 // both are directories, be nice
153 return ( strcmp ( s1 -> ref -> title_en, s2 -> ref -> title_en ) );
154 } else if ( s1 -> ref -> object_type == pnd_object_type_directory &&
155 s2 -> ref -> object_type != pnd_object_type_directory )
157 return ( -1 ); // dir on the left is earlier than file on the right
158 } else if ( s1 -> ref -> object_type != pnd_object_type_directory &&
159 s2 -> ref -> object_type == pnd_object_type_directory )
161 return ( 1 ); // dir on the right is earlier than file on the left
164 return ( strcmp ( s1 -> ref -> title_en, s2 -> ref -> title_en ) );
169 return ( strcasecmp ( s1 -> ref -> title_en, s2 -> ref -> title_en ) );
172 void category_dump ( void ) {
174 // WHY AREN'T I SORTING ON INSERT?
177 mm_category_t *iter = pnd_box_get_head ( m_categories );
178 unsigned int counter = 0;
182 pnd_log ( PND_LOG_DEFAULT, "Category %u: '%s' * %u\n", counter, iter -> catname, iter -> refcount );
183 mm_appref_t *ar = iter -> refs;
186 pnd_log ( PND_LOG_DEFAULT, " Appref %s\n", IFNULL(ar -> ref -> title_en,"No Name") );
190 iter = pnd_box_get_next ( iter );
197 void category_freeall ( void ) {
198 mm_category_t *c, *cnext;
199 mm_appref_t *iter, *next;
201 c = pnd_box_get_head ( m_categories );
204 cnext = pnd_box_get_next ( c );
217 if ( c -> catname ) {
218 free ( c -> catname );
223 free ( c -> fspath );
227 pnd_box_delete_node ( m_categories, c );
236 unsigned char category_map_setup ( void ) {
238 char *searchpath = pnd_box_get_head ( g_conf );
240 if ( ! searchpath ) {
244 // look through conf for useful keys
245 while ( searchpath ) {
246 char *k = pnd_box_get_key ( searchpath );
248 // does this key look like a category mapping key?
249 if ( strncasecmp ( k, "categories.@", 12 ) == 0 ) {
252 // iterate across 'words' in v, assigning catmaps to them
255 //pnd_log ( pndn_debug, "target(%s) from(%s)\n", k, buffer );
257 category_push ( k, NULL, 0, NULL /* fspath */, 1 );
258 g_catmaps [ g_catmapcount ].target = pnd_box_find_by_key ( m_categories, k );
259 g_catmaps [ g_catmapcount ].from = strdup ( buffer );
265 } // if key looks like catmap
267 searchpath = pnd_box_get_next ( searchpath );
268 } // while each conf key
273 mm_category_t *category_map_query ( char *cat ) {
276 for ( i = 0; i < g_catmapcount; i++ ) {
277 if ( strcasecmp ( g_catmaps [ i ].from, cat ) == 0 ) {
278 return ( g_catmaps [ i ].target );
285 unsigned char category_meta_push ( char *catname, char *parentcatname, pnd_disco_t *app, pnd_conf_handle ovrh, unsigned char visiblep ) {
287 char catnamebuffer [ 512 ] = "";
290 return ( 1 ); // fine, just nada
293 //fprintf ( stderr, "meta push: '%s'\n", catname );
295 // if invisible, and a parent category name is known, prepend it for ease of use
296 if ( ! visiblep && parentcatname ) {
297 snprintf ( catnamebuffer, 500, "%s.%s", parentcatname, catname );
298 catname = catnamebuffer;
301 // do we honour cat mapping at all?
302 if ( pnd_conf_get_as_int_d ( g_conf, "categories.map_on", 0 ) ) {
304 // is this guy mapped?
305 cat = category_map_query ( catname );
308 category_push ( cat -> catname, app, ovrh, NULL /* fspath */, visiblep );
312 // not mapped.. but default?
313 if ( pnd_conf_get_as_int_d ( g_conf, "categories.map_default_on", 0 ) ) {
314 char *def = pnd_conf_get_as_char ( g_conf, "categories.map_default_cat" );
316 category_push ( def, app, ovrh, NULL /* fspath */, visiblep );
321 } // cat map is desired?
323 // not default, just do it
324 category_push ( catname, app, ovrh, NULL /* fspath */, visiblep );
329 //fprintf ( stderr, "cat meta-push : vis[%30s,%d b] : tally; vis %d invis %d\n", catname, visiblep, g_categorycount, _categories_inviscount );
334 unsigned char category_fs_restock ( mm_category_t *cat ) {
336 if ( ! cat -> fspath ) {
337 return ( 1 ); // not a filesystem browser tab
340 // clear any existing baggage
344 mm_appref_t *iter = cat -> refs, *next;
353 if ( cat -> disco ) {
354 pnd_disco_t *p = pnd_box_get_head ( cat -> disco );
357 n = pnd_box_get_next ( p );
358 pnd_disco_destroy ( p );
361 pnd_box_delete ( cat -> disco );
364 // rescan the filesystem
367 //pnd_log ( pndn_debug, "Restocking cat %s with path %s\n", cat -> catname, cat -> fspath );
370 if ( ( d = opendir ( cat -> fspath ) ) ) {
371 struct dirent *de = readdir ( d );
376 cat -> disco = pnd_box_new ( cat -> catname );
381 char fullpath [ PATH_MAX ];
382 sprintf ( fullpath, "%s/%s", cat -> fspath, de -> d_name );
383 int statret = stat ( fullpath, &buffy );
385 // if file is executable somehow or another
387 buffy.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)
390 // determine unique-id
391 sprintf ( uid, "%d", (int) de -> d_ino );
394 switch ( de -> d_type ) {
397 if ( strcmp ( de -> d_name, "." ) == 0 ) {
398 // ignore ".", but ".." is fine
400 disco = pnd_box_allocinsert ( cat -> disco, uid, sizeof(pnd_disco_t) );
401 disco -> object_type = pnd_object_type_directory; // suggest to Grid that its a dir
406 disco = pnd_box_allocinsert ( cat -> disco, uid, sizeof(pnd_disco_t) );
407 disco -> object_type = pnd_object_type_unknown; // suggest to Grid that its a file
412 // found a directory or executable?
414 // register with current category
415 disco -> unique_id = strdup ( uid );
416 disco -> title_en = strdup ( de -> d_name );
417 disco -> object_flags = PND_DISCO_GENERATED;
418 disco -> object_path = strdup ( cat -> fspath );
419 disco -> object_filename = strdup ( de -> d_name );
420 category_push ( cat -> catname, disco, 0 /* no ovr */, NULL /* fspath already set */, 1 /* visible */ );
421 // if a override icon exists, cache it up
422 cache_icon ( disco, pnd_conf_get_as_int_d ( g_conf, "grid.icon_max_width", 50 ),
423 pnd_conf_get_as_int_d ( g_conf, "grid.icon_max_height", 50 ) );
438 static int catname_cmp ( const void *p1, const void *p2 ) {
439 //mm_category_t *c1 = (mm_category_t*) p1;
440 //mm_category_t *c2 = (mm_category_t*) p2;
441 mm_category_t *c1 = *( (mm_category_t**) p1 );
442 mm_category_t *c2 = *( (mm_category_t**) p2 );
444 if ( ( isalnum ( c1 -> catname [ 0 ] ) ) && ( ! isalnum ( c1 -> catname [ 1 ] ) ) ) {
446 } else if ( ( ! isalnum ( c1 -> catname [ 0 ] ) ) && ( isalnum ( c1 -> catname [ 1 ] ) ) ) {
448 } else if ( ( ! isalnum ( c1 -> catname [ 0 ] ) ) && ( ! isalnum ( c1 -> catname [ 1 ] ) ) ) {
452 int i = strcasecmp ( c1 -> catname, c2 -> catname );
453 //printf ( "cat name compare %p %s to %p %s = %d\n", p1, c1 -> catname, p2, c2 -> catname, i );
458 void category_sort ( void ) {
459 // we probably don't want to sort tab categories, since the user may have specified an ordering
460 // But we can sort invisi-cats, to make them easier to find, and ordered by parent category
463 qsort ( _categories_invis, _categories_inviscount, sizeof(mm_category_t), catname_cmp );
467 qsort ( g_categories, g_categorycount, sizeof(mm_category_t*), catname_cmp );
473 void category_publish ( unsigned int filter_mask, char *param ) {
474 unsigned char interested;
476 // clear published categories
477 memset ( g_categories, '\0', sizeof(mm_category_t*) * MAX_CATS );
480 // figure out the start
481 mm_category_t *iter = pnd_box_get_head ( m_categories );
483 // for each category we know...
488 // is this category desired?
489 if ( filter_mask == CFALL ) {
491 } else if ( iter -> catflags == filter_mask ) {
496 // set us up the bomb; notice that we're just duplicating the pointers, not making
497 // any new data here; none of this should ever be free'd!
498 g_categories [ g_categorycount ] = iter;
503 iter = pnd_box_get_next ( iter );
509 for ( i = 0; i < g_categorycount; i++ ) {
510 printf ( "Unsorted cat %d %p: %s\n", i, &(g_categories [ i ]), g_categories [ i ] -> catname );
514 // sort published categories
520 unsigned int category_count ( unsigned int filter_mask ) {
521 mm_category_t *iter = pnd_box_get_head ( m_categories );
522 unsigned int count = 0;
524 // for each category we know...
527 // is this category desired?
528 if ( iter -> catflags == filter_mask ) {
533 iter = pnd_box_get_next ( iter );
539 int category_index ( char *catname ) {
542 for ( i = 0; i < g_categorycount; i++ ) {
544 if ( strcasecmp ( g_categories [ i ] -> catname, catname ) == 0 ) {