+
+static int catname_cmp ( const void *p1, const void *p2 ) {
+ //mm_category_t *c1 = (mm_category_t*) p1;
+ //mm_category_t *c2 = (mm_category_t*) p2;
+ mm_category_t *c1 = *( (mm_category_t**) p1 );
+ mm_category_t *c2 = *( (mm_category_t**) p2 );
+
+ if ( ( isalnum ( c1 -> catname [ 0 ] ) ) && ( ! isalnum ( c1 -> catname [ 1 ] ) ) ) {
+ return ( -1 );
+ } else if ( ( ! isalnum ( c1 -> catname [ 0 ] ) ) && ( isalnum ( c1 -> catname [ 1 ] ) ) ) {
+ return ( 1 );
+ } else if ( ( ! isalnum ( c1 -> catname [ 0 ] ) ) && ( ! isalnum ( c1 -> catname [ 1 ] ) ) ) {
+ return ( 0 );
+ }
+
+ int i = strcasecmp ( c1 -> catname, c2 -> catname );
+ //printf ( "cat name compare %p %s to %p %s = %d\n", p1, c1 -> catname, p2, c2 -> catname, i );
+
+ return ( i );
+}
+
+void category_sort ( void ) {
+ // we probably don't want to sort tab categories, since the user may have specified an ordering
+ // But we can sort invisi-cats, to make them easier to find, and ordered by parent category
+
+#if 0
+ qsort ( _categories_invis, _categories_inviscount, sizeof(mm_category_t), catname_cmp );
+#endif
+
+#if 1
+ qsort ( g_categories, g_categorycount, sizeof(mm_category_t*), catname_cmp );
+#endif
+
+ return;
+}
+
+void category_publish ( unsigned int filter_mask, char *param ) {
+ unsigned char interested;
+
+ // clear published categories
+ memset ( g_categories, '\0', sizeof(mm_category_t*) * MAX_CATS );
+ g_categorycount = 0;
+
+ // figure out the start
+ mm_category_t *iter = pnd_box_get_head ( m_categories );
+
+ // for each category we know...
+ while ( iter ) {
+
+ interested = 0;
+
+ // is this category desired?
+ if ( filter_mask == CFALL ) {
+ interested = 1;
+ } else if ( filter_mask == CFBYNAME ) {
+ if ( strcasecmp ( iter -> catname, param ) == 0 ) {
+ interested = 1;
+ }
+ } else if ( iter -> catflags == filter_mask ) {
+ interested = 1;
+ } // if
+
+ if ( interested ) {
+ // set us up the bomb; notice that we're just duplicating the pointers, not making
+ // any new data here; none of this should ever be free'd!
+ g_categories [ g_categorycount ] = iter;
+ g_categorycount++;
+ }
+
+ // next
+ iter = pnd_box_get_next ( iter );
+ }
+
+ // dump
+#if 0
+ unsigned int i;
+ for ( i = 0; i < g_categorycount; i++ ) {
+ printf ( "Unsorted cat %d %p: %s\n", i, &(g_categories [ i ]), g_categories [ i ] -> catname );
+ }
+#endif
+
+ // sort published categories
+ category_sort();
+
+ return;
+}
+
+unsigned int category_count ( unsigned int filter_mask ) {
+ mm_category_t *iter = pnd_box_get_head ( m_categories );
+ unsigned int count = 0;
+
+ // for each category we know...
+ while ( iter ) {
+
+ // is this category desired?
+ if ( iter -> catflags == filter_mask ) {
+ count++;
+ } // if
+
+ // next
+ iter = pnd_box_get_next ( iter );
+ }
+
+ return ( count );
+}
+
+int category_index ( char *catname ) {
+ unsigned char i;
+
+ for ( i = 0; i < g_categorycount; i++ ) {
+
+ if ( strcasecmp ( g_categories [ i ] -> catname, catname ) == 0 ) {
+ return ( i );
+ }
+
+ }
+
+ return ( -1 );
+}
+
+unsigned char category_contains_app ( char *catname, char *unique_id ) {
+
+ mm_category_t *c = pnd_box_find_by_key ( m_categories, catname );
+
+ if ( ! c ) {
+ return ( 0 ); // wtf?
+ }
+
+ if ( ! c -> refs ) {
+ return ( 0 ); // no apps at all
+ }
+
+ mm_appref_t *iter = c -> refs;
+
+ while ( iter ) {
+
+ if ( strcmp ( iter -> ref -> unique_id, unique_id ) == 0 ) {
+ return ( 1 );
+ }
+
+ iter = iter -> next;
+ }
+
+ return ( 0 );
+}