#define _POSIX_C_SOURCE 200809L #include /* NULL */ /* Souborové systémy bývají organizovány jako hierarchie souborů * a složek. Vaším úkolem je naprogramovat procedury pro vyjádření * obsahu podstromu zadaného adresářem. * * Výstup se bude realizovat skrze strukturu ‹node›, která má * následující atributy: * • ‹name› – ukazatel na nulou zakončený řetězec se jménem * souboru; * • ‹type› – hodnota výčtového typu níže popisující typ souboru; * • ‹error› – hodnota ‹0›, nebo informace o nastalé chybě; * • ‹dir› – v případě, že se jedná o adresář, obsahuje ukazatel na * zřetězený seznam souborů, anebo ‹NULL›, pokud je adresář * prázdný; * • ‹next› – ukazatel na další prvek ve stejném adresáři, anebo * ‹NULL›, pokud je poslední. * * Tato struktura reprezentuje jednu položku v adresářovém * podstromě. Položka je buď adresářem, nebo jiným souborem. Jelikož * se jedná o reprezentaci zřetězeným seznamem, struktura obsahuje * ukazatele na následníka a potomka (‹next› a ‹dir›). * * Atributy ‹name›, ‹dir› a ‹next› jsou ukazatele na dynamicky * alokovanou paměť, kterou následně musí být možné uvolnit * zavoláním ‹free›. * * Strukturu nemůžete nijak měnit. * * Výčtový typ ‹file_type› popisuje pět typů souborů, které budeme * rozlišovat: adresář, obyčejný soubor, symbolický odkaz, socket * a vše ostatní. */ enum file_type { t_directory, t_regular, t_symlink, t_socket, t_other, }; struct node { char *name; enum file_type type; int error; struct node *dir; struct node *next; }; /* Úkol spočívá v implementaci dvou procedur níže. První z nich je * ‹tree_create›, která bere parametry: * • ‹at› – popisovač adresáře, ve kterém hledat složku, nebo * konstanta ‹AT_FDCWD›; * • ‹root_name› – název počátečního adresáře, který se má * prohledávat; * • ‹out› – výstupní ukazatel, do něhož má být uložen ukazatel * na alokovanou zřetězenou strukturu. * * Její návratovou hodnotou bude: * • ‹0› – úspěch; * • ‹-1› – selhání pří přístupu k některému souboru či složce; * • ‹-2› – selhání při alokaci, což je kritická chyba, a výstupní * ‹out› nechť je nastaven na ‹NULL›. * * Jestliže je návratová hodnota ‹-1›, u dotčených souborů bude * atribut ‹error› nastaven na odpovídající hodnotu proměnné * ‹errno›. Jinak má být hodnota tohoto atributu ‹0›. */ int tree_create( int at, const char *root_name, struct node **out ); /* Je rovněž potřeba implementovat odpovídající uvolňovací * proceduru. Tou je zde ‹tree_free›, která musí být schopna * přijmout výstupní ukazatel z ‹tree_create› a ten uvolnit, včetně * všech přidělených zdrojů. */ void tree_free( struct node *tree ); /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ #include /* assert */ #include /* mkdirat */ #include /* mkdirat, openat */ #include /* unlinkat, close */ #include /* strcmp */ #include /* unlinkat */ #include /* errno */ #include /* err, warn */ static void close_or_warn( int fd, const char *name ) { if ( close( fd ) == -1 ) warn( "closing %s", name ); } static void unlink_if_exists( int at, const char *name ) { if ( unlinkat( at, name, 0 ) == -1 && errno != ENOENT ) err( 2, "unlinking %s", name ); } static void rmdir_if_exists( int at, const char *name ) { if ( unlinkat( at, name, AT_REMOVEDIR ) == -1 && errno != ENOENT ) err( 2, "removing directory %s", name ); } static int open_dir_at( int at, const char *name ) { int fd = openat( at, name, O_RDONLY | O_DIRECTORY ); if ( fd == -1 ) err( 2, "opening dir %s", name ); return fd; } static void create_file( int at, const char *name ) { int fd = openat( at, name, O_CREAT | O_TRUNC | O_WRONLY, 0666 ); if ( fd == -1 ) err( 2, "creating %s", name ); close_or_warn( fd, name ); } static int create_dir( int at, const char *dir ) { rmdir_if_exists( at, dir ); if ( mkdirat( at, dir, 0755 ) == -1 ) err( 2, "creating directory %s", dir ); return open_dir_at( at, dir ); } int main( void ) { unlink_if_exists( AT_FDCWD, "zt.b_root/folder/file_b" ); rmdir_if_exists( AT_FDCWD, "zt.b_root/folder" ); unlink_if_exists( AT_FDCWD, "zt.b_root/file_a" ); rmdir_if_exists( AT_FDCWD, "zt.b_root" ); int root = create_dir( AT_FDCWD, "zt.b_root" ); int folder = create_dir( root, "folder" ); create_file( root, "file_a" ); create_file( folder, "file_b" ); close_or_warn( folder, "folder" ); struct node *tree; tree_create( AT_FDCWD, "zt.b_root", &tree ); assert( tree->type == t_directory ); assert( tree->dir != NULL ); assert( tree->next == NULL ); assert( strcmp( tree->name, "zt.b_root" ) == 0 ); assert( tree->dir->next != NULL ); assert( tree->dir->next->next == NULL ); assert( tree->dir->type == t_directory || tree->dir->next->type == t_directory ); const struct node *tree_folder; const struct node *tree_file; if ( tree->dir->type == t_directory ) { tree_folder = tree->dir; tree_file = tree->dir->next; } else { tree_folder = tree->dir->next; tree_file = tree->dir; } assert( strcmp( tree_folder->name, "folder" ) == 0 ); assert( strcmp( tree_file->name, "file_a" ) == 0 ); assert( tree_folder->type == t_directory ); assert( tree_folder->dir != NULL ); assert( strcmp( tree_folder->dir->name, "file_b" ) == 0 ); tree_free( tree ); close_or_warn( root, "zt.b_root" ); unlink_if_exists( AT_FDCWD, "zt.b_root/folder/file_b" ); rmdir_if_exists( AT_FDCWD, "zt.b_root/folder" ); unlink_if_exists( AT_FDCWD, "zt.b_root/file_a" ); rmdir_if_exists( AT_FDCWD, "zt.b_root" ); return 0; }