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 g_categories [ MAX_CATS ];
22 unsigned char g_categorycount = 0;
24 mm_catmap_t g_catmaps [ MAX_CATS ];
25 unsigned char g_catmapcount = 0;
27 extern pnd_conf_handle g_conf;
29 unsigned char category_push ( char *catname, pnd_disco_t *app, pnd_conf_handle ovrh, char *fspath ) {
32 // check category list; if found, append app to the end of it.
33 // if not found, add it to the category list and plop the app in there.
34 // app's are just app-refs, which contain links to the disco-t list -- thus removal is only in one place, and
35 // an app can be in multiple categories if we like..
38 // find or create category
41 if ( ( c = category_query ( catname ) ) ) {
42 // category was found..
44 // category wasn't found..
45 pnd_log ( PND_LOG_DEFAULT, "New category '%s'\n", catname );
46 g_categories [ g_categorycount ].catname = strdup ( catname );
47 g_categories [ g_categorycount ].refs = NULL;
48 c = &(g_categories [ g_categorycount ]);
51 g_categories [ g_categorycount ].fspath = strdup ( fspath );;
58 return ( 1 ); // create cat, but skip app
61 // alloc and populate appref
63 mm_appref_t *ar = malloc ( sizeof(mm_appref_t) );
68 bzero ( ar, sizeof(mm_appref_t) );
73 // plug it into category
74 // and sort it on insert!
76 ar -> next = c -> refs;
79 // if no refs at all, or new guy has no title, just stick it in at head
80 if ( c -> refs && ar -> ref -> title_en ) {
81 mm_appref_t *iter = c -> refs;
82 mm_appref_t *last = NULL;
86 if ( iter -> ref -> title_en ) {
87 if ( cat_sort_score ( ar, iter ) < 0 ) {
88 // new guy is smaller than the current guy!
92 // since new guy must have a name by here, we're bigger than any guy who does not have a name
101 // smaller than the current guy, so stitch in
106 ar -> next = c -> refs;
110 // we're the biggest, just append to last
115 ar -> next = c -> refs;
124 mm_category_t *category_query ( char *catname ) {
127 for ( i = 0; i < g_categorycount; i++ ) {
129 if ( strcasecmp ( g_categories [ i ].catname, catname ) == 0 ) {
130 return ( &(g_categories [ i ]) );
138 int cat_sort_score ( mm_appref_t *s1, mm_appref_t *s2 ) {
140 extern mm_category_t g_categories [ MAX_CATS ];
141 extern unsigned char g_categorycount;
142 extern unsigned char ui_category;
144 // are we in a directory browser, or looking at pnd-files?
145 if ( g_categories [ ui_category ].fspath ) {
148 return ( 0 ); // equal
150 } else if ( s1 -> ref -> object_type == pnd_object_type_directory &&
151 s2 -> ref -> object_type == pnd_object_type_directory )
153 // both are directories, be nice
154 return ( strcmp ( s1 -> ref -> title_en, s2 -> ref -> title_en ) );
155 } else if ( s1 -> ref -> object_type == pnd_object_type_directory &&
156 s2 -> ref -> object_type != pnd_object_type_directory )
158 return ( -1 ); // dir on the left is earlier than file on the right
159 } else if ( s1 -> ref -> object_type != pnd_object_type_directory &&
160 s2 -> ref -> object_type == pnd_object_type_directory )
162 return ( 1 ); // dir on the right is earlier than file on the left
165 return ( strcmp ( s1 -> ref -> title_en, s2 -> ref -> title_en ) );
170 return ( strcmp ( s1 -> ref -> title_en, s2 -> ref -> title_en ) );
173 void category_dump ( void ) {
176 // WHY AREN'T I SORTING ON INSERT?
179 for ( i = 0; i < g_categorycount; i++ ) {
180 pnd_log ( PND_LOG_DEFAULT, "Category %u: '%s' * %u\n", i, g_categories [ i ].catname, g_categories [ i ].refcount );
181 mm_appref_t *ar = g_categories [ i ].refs;
184 pnd_log ( PND_LOG_DEFAULT, " Appref %s\n", IFNULL(ar -> ref -> title_en,"No Name") );
193 void category_freeall ( void ) {
195 mm_appref_t *iter, *next;
197 for ( i = 0; i < g_categorycount; i++ ) {
199 iter = g_categories [ i ].refs;
207 g_categories [ i ].refs = NULL;
216 unsigned char category_map_setup ( void ) {
218 char *searchpath = pnd_box_get_head ( g_conf );
220 if ( ! searchpath ) {
224 // look through conf for useful keys
225 while ( searchpath ) {
226 char *k = pnd_box_get_key ( searchpath );
228 // does this key look like a category mapping key?
229 if ( strncasecmp ( k, "categories.@", 12 ) == 0 ) {
232 // iterate across 'words' in v, assigning catmaps to them
235 //pnd_log ( pndn_debug, "target(%s) from(%s)\n", k, buffer );
237 category_push ( k, NULL, 0, NULL /* fspath */ );
238 g_catmaps [ g_catmapcount ].target = category_query ( k );
239 g_catmaps [ g_catmapcount ].from = strdup ( buffer );
245 } // if key looks like catmap
247 searchpath = pnd_box_get_next ( searchpath );
248 } // while each conf key
253 mm_category_t *category_map_query ( char *cat ) {
256 for ( i = 0; i < g_catmapcount; i++ ) {
257 if ( strcasecmp ( g_catmaps [ i ].from, cat ) == 0 ) {
258 return ( g_catmaps [ i ].target );
265 unsigned char category_meta_push ( char *catname, pnd_disco_t *app, pnd_conf_handle ovrh ) {
268 // do we honour cat mapping at all?
269 if ( pnd_conf_get_as_int_d ( g_conf, "categories.map_on", 0 ) ) {
271 // is this guy mapped?
272 cat = category_map_query ( catname );
275 return ( category_push ( cat -> catname, app, ovrh, NULL /* fspath */ ) );
278 // not mapped.. but default?
279 if ( pnd_conf_get_as_int_d ( g_conf, "categories.map_default_on", 0 ) ) {
280 char *def = pnd_conf_get_as_char ( g_conf, "categories.map_default_cat" );
282 return ( category_push ( def, app, ovrh, NULL /* fspath */ ) );
286 } // cat map is desired?
288 // not default, just do it
289 return ( category_push ( catname, app, ovrh, NULL /* fspath */ ) );
292 unsigned char category_fs_restock ( mm_category_t *cat ) {
294 if ( ! cat -> fspath ) {
295 return ( 1 ); // not a filesystem browser tab
298 // clear any existing baggage
302 mm_appref_t *iter = cat -> refs, *next;
311 if ( cat -> disco ) {
312 pnd_disco_t *p = pnd_box_get_head ( cat -> disco );
315 n = pnd_box_get_next ( p );
316 pnd_disco_destroy ( p );
319 pnd_box_delete ( cat -> disco );
322 // rescan the filesystem
325 //pnd_log ( pndn_debug, "Restocking cat %s with path %s\n", cat -> catname, cat -> fspath );
328 if ( ( d = opendir ( cat -> fspath ) ) ) {
329 struct dirent *de = readdir ( d );
334 cat -> disco = pnd_box_new ( cat -> catname );
339 char fullpath [ PATH_MAX ];
340 sprintf ( fullpath, "%s/%s", cat -> fspath, de -> d_name );
341 int statret = stat ( fullpath, &buffy );
343 // if file is executable somehow or another
345 buffy.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)
348 // determine unique-id
349 sprintf ( uid, "%d", (int) de -> d_ino );
352 switch ( de -> d_type ) {
355 if ( strcmp ( de -> d_name, "." ) == 0 ) {
356 // ignore ".", but ".." is fine
358 disco = pnd_box_allocinsert ( cat -> disco, uid, sizeof(pnd_disco_t) );
359 disco -> object_type = pnd_object_type_directory; // suggest to Grid that its a dir
364 disco = pnd_box_allocinsert ( cat -> disco, uid, sizeof(pnd_disco_t) );
365 disco -> object_type = pnd_object_type_unknown; // suggest to Grid that its a file
370 // found a directory or executable?
372 // register with this category
373 disco -> unique_id = strdup ( uid );
374 disco -> title_en = strdup ( de -> d_name );
375 disco -> object_flags = PND_DISCO_GENERATED;
376 disco -> object_path = strdup ( cat -> fspath );
377 disco -> object_filename = strdup ( de -> d_name );
378 category_push ( cat -> catname, disco, 0, NULL /* fspath already set */ );
379 // if a override icon exists, cache it up
380 cache_icon ( disco, pnd_conf_get_as_int_d ( g_conf, "grid.icon_max_width", 50 ),
381 pnd_conf_get_as_int_d ( g_conf, "grid.icon_max_height", 50 ) );