#define _POSIX_C_SOURCE 200809L #include /* errno, EEXIST, ENOENT */ #include /* mkdirat, openat, unlinkat */ #include /* strlen */ #include /* mkdirat */ #include /* close, faccessat, unlinkat, write */ /* Napište podprogram ‹prune_empty›, kterému je předán popisovač * otevřené složky, a který nalezne a odstraní všechny prázdné * podsložky (kdekoliv ve stromě, tzn. ne pouze přímé potomky). * Vznikne-li tímto procesem nějaká nově prázdná složka, tato bude * podobně odstraněna (netýká se kořenové). * * Nezáporná návratová hodnota určuje počet odstraněných složek, -1 * indikuje systémovou chybu. */ int prune_empty( int dir_fd ); /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ #include /* err, warn */ #include /* bool, false, true */ #include /* assert */ static void unlink_if_exists( int fd, const char *name, bool is_dir ) { if ( unlinkat( fd, name, is_dir ? AT_REMOVEDIR : 0 ) == -1 && errno != ENOENT ) err( 2, "unlinking %s", name ); } static void close_or_warn( int fd, const char *name ) { if ( fd >= 0 && close( fd ) == -1 ) warn( "closing %s", name ); } static int mkdir_or_die( int dir_fd, const char *name ) { int fd; if ( mkdirat( dir_fd, name, 0777 ) == -1 && errno != EEXIST ) err( 1, "creating directory %s", name ); if ( ( fd = openat( dir_fd, name, O_DIRECTORY ) ) == -1 ) err( 1, "opening newly created directory %s", name ); return fd; } static int create_file( int dir_fd, const char *name ) { unlink_if_exists( dir_fd, name, false ); int fd = openat( dir_fd, name, O_CREAT | O_TRUNC | O_RDWR, 0666 ); if ( fd == -1 ) err( 2, "creating %s", name ); return fd; } static void write_file( int dir, const char *name, const char *str ) { int fd = create_file( dir, name ); int len = strlen( str ); if ( write( fd, str, len ) != len ) err( 2, "writing file %s", name ); close_or_warn( fd, name ); } void cleanup( void ) { int root_fd = openat( AT_FDCWD, "zt.r5_root", O_DIRECTORY ); if ( root_fd == -1 ) return; unlink_if_exists( root_fd, "One/Has", false ); unlink_if_exists( root_fd, "One/Landmine", true ); unlink_if_exists( root_fd, "One", true ); unlink_if_exists( root_fd, "Time/Ticking/Away", true ); unlink_if_exists( root_fd, "Time/Ticking", true ); unlink_if_exists( root_fd, "Time", true ); close_or_warn( root_fd, "zt.r5_root" ); unlink_if_exists( AT_FDCWD, "zt.r5_root", true ); } int main( void ) { cleanup(); int root_fd = mkdir_or_die( AT_FDCWD, "zt.r5_root" ); /* Test case 1. */ assert( ( prune_empty( root_fd ) == 0 ) ); /* Test case 2. */ int subdir1 = mkdir_or_die( root_fd, "One" ); int subdir2 = mkdir_or_die( root_fd, "Time" ); int subdir3 = mkdir_or_die( subdir1, "Landmine" ); write_file( subdir1, "Has", "Taken My sight" ); int subdir4 = mkdir_or_die( subdir2, "Ticking" ); int subdir5 = mkdir_or_die( subdir4, "Away" ); close_or_warn( subdir1, "zt.r5_root/One" ); close_or_warn( subdir2, "zt.r5_root/Time" ); close_or_warn( subdir3, "zt.r5_root/One/Landmine" ); close_or_warn( subdir4, "zt.r5_root/Time/Ticking" ); close_or_warn( subdir5, "zt.r5_root/Time/Ticking/Away" ); assert( ( prune_empty( root_fd ) == 4 ) ); assert( ( faccessat( root_fd, "One", F_OK, 0 ) == 0 ) ); assert( ( faccessat( root_fd, "One/Has", F_OK, 0 ) == 0 ) ); assert( ( faccessat( root_fd, "One/Landmine", F_OK, 0 ) == -1 ) ); assert( ( errno == ENOENT ) ); assert( ( faccessat( root_fd, "Time", F_OK, 0 ) == -1 ) ); assert( ( errno == ENOENT ) ); assert( ( faccessat( root_fd, "Time/Ticking", F_OK, 0 ) == -1 ) ); assert( ( errno == ENOENT ) ); assert( ( faccessat( root_fd, "Time/Ticking/Away", F_OK, 0 ) == -1 ) ); assert( ( errno == ENOENT ) ); /* Test case 3. */ unlink_if_exists( root_fd, "One/Has", false ); assert( ( prune_empty( root_fd ) == 1 ) ); assert( ( faccessat( root_fd, "One", F_OK, 0 ) == -1 ) ); assert( ( errno == ENOENT ) ); close_or_warn( root_fd, "zt.r5_root" ); cleanup(); return 0; }