The shortcode produced by this function - a list of all sites in a multisite - outputs above the content in the loop, no matter where it is placed in the editor.

I've looked at the other related questions and answers on WPSE and realize that it has to do with the function using `echo` instead of `return`, but it's not as easy as replacing the instances of `echo` with `return` in the function below. Or adding `echo=0` with a WP function like `wp_list_pages()`

Any ideas? Where is the function that needs to be returned instead of echoed?

    // Output a single menu item
    function projects_menu_entry($id, $title, $link_self)
    {
        global $blog_id;
        if ($link_self || $id != $blog_id) {
            echo '<li>';
            if ($id == $blog_id) {
                echo '<strong>';
            }
            $url = get_home_url($id);
            if (substr($url, -1) != '/') {
                // Note: I added a "/" to the end of the URL because WordPress
                // wasn't doing that automatically in v3.0.4
                $url .= '/';
            }
            echo '<a href="' . $url . '">' . $title . '</a>';
            if ($id == $blog_id) {
                echo '</strong>';
            }
            echo '</li>';
        }
    }
    
    // Output the whole menu
    // If $link_self is false, skip the current site - used to display the menu on the homepage
    function projects_menu($link_self = true)
    {
        global $wpdb;
    
        echo '<ul>';
    
        projects_menu_entry(1, 'Home', $link_self);
    
        $blogs = $wpdb->get_results("
            SELECT blog_id
            FROM {$wpdb->blogs}
            WHERE site_id = '{$wpdb->siteid}'
            AND spam = '0'
            AND deleted = '0'
            AND archived = '0'
            AND blog_id != 1
        ");
    
        $sites = array();
        foreach ($blogs as $blog) {
            $sites[$blog->blog_id] = get_blog_option($blog->blog_id, 'blogname');
        }
    
        natsort($sites);
        foreach ($sites as $blog_id => $blog_title) {
            projects_menu_entry($blog_id, $blog_title, $link_self);
        }
        echo '</ul>';
    }
    
    // Adds a [bloglist] shortcode

    function bloglist_shortcode($atts)
    {
        projects_menu(false);
    }
    
    add_shortcode('bloglist', 'bloglist_shortcode');