diff --git a/src/nsmd.cpp b/src/nsmd.cpp index 8562e4b..0c5e231 100644 --- a/src/nsmd.cpp +++ b/src/nsmd.cpp @@ -39,14 +39,12 @@ #include #include #include -#include #include #include -#include -#include #include #include #include +#include #include "Endpoint.hpp" /* for locking */ @@ -1677,51 +1675,141 @@ OSC_HANDLER( new ) return 0; } +int +fts_comparer_to_process_files_before_dirs( const FTSENT ** first, const FTSENT ** second ) +{ + /* + The argument compar() specifies a user-defined function which may + be used to order the traversal of the hierarchy. It takes two + pointers to pointers to FTSENT structures as arguments and should + return a negative value, zero, or a positive value to indicate if + the file referenced by its first argument comes before, in any + order with respect to, or after, the file referenced by its + second argument. The fts_accpath, fts_path, and fts_pathlen + fields of the FTSENT structures may never be used in this + comparison. If the fts_info field is set to FTS_NS or FTS_NSOK, + the fts_statp field may not either. If the compar() argument is + NULL, the directory traversal order is in the order listed in + path_argv for the root paths, and in the order listed in the + directory for everything else. + */ + + if ( (*first)->fts_info & FTS_F ) + return (-1); //first + else if ( (*second)->fts_info & FTS_F ) + return (1); //last + else + return strcmp((*first)->fts_name, (*second)->fts_name); + //return (0); //doesn't matter +} + + static lo_address list_response_address; -int -list_file ( const char *fpath, const struct stat *sb, int tflag ) +OSC_HANDLER( list ) { - if ( tflag == FTW_F ) - { - char *s; - s = strdup( fpath ); - if ( ! strcmp( "session.nsm", basename( s ) ) ) - { - free( s ); - s = strdup( fpath ); + //Parse the session_root recursively for session.nsm files and send names with /nsm/server/list + //Sessions can be structured with sub-directories. + //The file session.nsm marks a real session and is a 'leaf' of the session tree. + //No other sessions are allowed below a dir containing session.nsm . - s = dirname( s ); + GUIMSG( "Listing sessions" ); + list_response_address = lo_message_get_source( msg ); - memmove( s, s + strlen( session_root ) + 1, (strlen( s ) - strlen( session_root )) + 1); + //Use fts to walk the session_root + /* An array of paths to traverse. Each path must be null + * terminated and the list must end with a NULL pointer. */ + char *paths[] = { session_root, NULL }; - osc_server->send( list_response_address, "/reply", "/nsm/server/list", s ); + /* 2nd parameter: An options parameter. Must include either + FTS_PHYSICAL or FTS_LOGICAL---they change how symbolic links + are handled. - free( s ); - } - else - free( s ); - } + Last parameter is a comparator which you can optionally provide + to change the traversal of the filesystem hierarchy. - return 0; -} + Our comparator processes files before directories, so we can depend on that + to remember if we are already in a session-dir. + */ -OSC_HANDLER( list ) -{ - GUIMSG( "Listing sessions" ); + FTS *ftsp = fts_open(paths, FTS_LOGICAL, fts_comparer_to_process_files_before_dirs); + if(ftsp == NULL) + { + FATAL( "fts_open" ); + exit(EXIT_FAILURE); + } - list_response_address = lo_message_get_source( msg ); + FTSENT * currentSession = NULL; + while( 1 ) // call fts_read() enough times to get each file + { + FTSENT *ent = fts_read(ftsp); // get next entry (could be file or directory). + if( ent == NULL ) + { + if( errno == 0 ) + break; // No more items, bail out of while loop + else + { + // fts_read() had an error. + FATAL( "fts_read" ); + exit(EXIT_FAILURE); + } + } - ftw( session_root, list_file, 20 ); + // Handle Types of Files - // osc_server->send( lo_message_get_source( msg ), path, ERR_OK, "Done." ); + // Given a "entry", determine if it is a file or directory + if( ent->fts_info & FTS_D ) // We are entering into a directory + { + //printf( "Enter dir: %s\n", ent->fts_path ); + if ( currentSession != NULL ) + { + //printf( "already found current session.nsm: %s . Waiting to leave dir. Ignoring %s\n", currentSession->fts_name, ent->fts_path ); + + // Setup that no descendants of this file are visited. + int err = fts_set( ftsp, ent, FTS_SKIP ); + if ( err != 0 ) + { + FATAL( "fts_set" ); + exit(EXIT_FAILURE); + } + } + } + else if( ent->fts_info & FTS_DP ) // We are exiting a directory + { + //printf( "Exit dir: %s\n", ent->fts_path ); + if ( ent == currentSession ) + { + //printf( "Exit current session dir: %s\n", ent->fts_path ); + currentSession = NULL; + } + } + else if( ent->fts_info & FTS_F ) // The entry is a file. + { + //printf( "File: %s\n", ent->fts_path ); + if ( ! strcmp( "session.nsm", basename( ent->fts_path ) ) ) + { + //Convert path to session name: + char *s; + s = strdup( ent->fts_path ); + s = dirname( s ); + memmove( s, s + strlen( session_root ) + 1, (strlen( s ) - strlen( session_root )) + 1); + osc_server->send( list_response_address, "/reply", "/nsm/server/list", s ); + free( s ); + currentSession = ent->fts_parent; //save the directory entry. not the session.nsm entry. + } + } + } + + // close fts and check for error closing. + if(fts_close(ftsp) == -1) + FATAL( "fts_close" ); // As marker that all sessions were sent reply with an empty string, which is impossible to conflict with a session name osc_server->send( list_response_address, "/reply", "/nsm/server/list", "" ); - return 0; } + OSC_HANDLER( open ) { lo_address sender_addr;