#define _POSIX_C_SOURCE 200809L #include /* dprintf */ #include /* assert */ #include /* strcmp, memset */ #include /* unlinkat, close */ #include /* open, openat */ #include #include /* Naprogramujte proceduru ‹cat›, která obdrží tyto 3 parametry: * * • ‹dir_fd› – popisovač adresáře, ve kterém bude hledat všechny * níže zmíněné soubory, * • ‹names› – jednoduše zřetězený seznam jmen souborů, které bude * podprogram ‹cat› načítat, * • ‹out_fd› – «výstupní» popisovač. * * Procedura ‹cat› zapíše obsahy všech souborů uvedených * v ‹name_list› (v zadaném pořadí) do popisovače ‹out›. * Nepodaří-li se nějaký soubor otevřít, přeskočte jej. Návratová * hodnota: * * • 0 – podařilo se otevřít, přečíst a zapsat všechny soubory, * • -1 – chyba zápisu do ‹out_fd›, nebo jiná fatální systémová * chyba, * • -2 – vše proběhlo v pořádku, ale některé soubory byly * přeskočeny, protože se je nepodařilo otevřít. */ struct name_list { const char *name; struct name_list *next; }; int cat( int dir_fd, struct name_list *names, int out_fd ); /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ static void unlink_if_exists( int dir, const char *name ) { if ( unlinkat( dir, name, 0 ) == -1 && errno != ENOENT ) err( 2, "unlinking %s", name ); } static void close_or_warn( int fd, const char *name ) { if ( close( fd ) == -1 ) warn( "closing %s", name ); } static int open_or_die( int dir_fd, const char *name ) { int fd = openat( dir_fd, name, O_RDONLY ); if ( fd == -1 ) err( 2, "opening %s", name ); return fd; } static int create_file( int dir_fd, const char *name ) { unlink_if_exists( dir_fd, name ); int fd; if ( ( fd = openat( dir_fd, name, O_CREAT | O_TRUNC | O_RDWR, 0666 ) ) == -1 ) err( 2, "creating %s", name ); return fd; } static int check_cat( int dir_fd, struct name_list *names ) { int out_fd, result; const char *out_name = "zt.r1_test_out"; out_fd = create_file( dir_fd, out_name ); result = cat( dir_fd, names, out_fd ); close_or_warn( out_fd, out_name ); return result; } static int check_output( const char* expected ) { const char *name = "zt.r1_test_out"; char buffer[ 4096 + 1 ] = { 0 }; int read_fd = open_or_die( AT_FDCWD, name ); if ( read( read_fd, buffer, 4096 ) == -1 ) err( 2, "reading %s", name ); close_or_warn( read_fd, name ); return strcmp( expected, buffer ); } static void write_file( int dir, const char *name, const char *str ) { int fd = create_file( dir, name ); if ( write( fd, str, strlen( str ) ) == -1 ) err( 2, "writing file %s", name ); close_or_warn( fd, name ); } int main( void ) { int dir; if ( ( dir = open( ".", O_RDONLY ) ) == -1 ) err( 2, "opening working directory" ); write_file( dir, "zt.r1_a", "contents of zt.r1_a\n" ); write_file( dir, "zt.r1_b", "contents of zt.r1_b\n" ); write_file( dir, "zt.r1_c", "contents of zt.r1_c\n" ); write_file( dir, "zt.r1_d", "contents of zt.r1_d\n" ); unlink_if_exists( dir, "zt.r1_test_out" ); unlink_if_exists( dir, "zt.r1_lst1" ); unlink_if_exists( dir, "zt.r1_lst2" ); unlink_if_exists( dir, "zt.r1_lst3" ); unlink_if_exists( dir, "zt.r1_lst4" ); struct name_list l1_1 = { "zt.r1_a", NULL }, l2_3 = { "zt.r1_c", NULL }, l2_2 = { "zt.r1_b", &l2_3 }, l2_1 = { "zt.r1_a", &l2_2 }, l3_6 = { "zt.r1_b", NULL }, l3_5 = { "zt.r1_a", &l3_6 }, l3_4 = { "zt.r1_d", &l3_5 }, l3_3 = { "zt.r1_c", &l3_4 }, l3_2 = { "zt.r1_b", &l3_3 }, l3_1 = { "zt.r1_a", &l3_2 }; assert( check_cat( dir, &l1_1 ) == 0 ); assert( check_output( "contents of zt.r1_a\n" ) == 0 ); assert( check_cat( dir, &l2_1 ) == 0 ); assert( check_output( "contents of zt.r1_a\n" "contents of zt.r1_b\n" "contents of zt.r1_c\n" ) == 0 ); assert( check_cat( dir, &l3_1 ) == 0 ); assert( check_output( "contents of zt.r1_a\n" "contents of zt.r1_b\n" "contents of zt.r1_c\n" "contents of zt.r1_d\n" "contents of zt.r1_a\n" "contents of zt.r1_b\n" ) == 0 ); unlink_if_exists( dir, "zt.r1_a" ); assert( check_cat( dir, &l3_1 ) == -2 ); assert( check_output( "contents of zt.r1_b\n" "contents of zt.r1_c\n" "contents of zt.r1_d\n" "contents of zt.r1_b\n" ) == 0 ); assert( check_cat( dir, &l1_1 ) == -2 ); assert( check_output( "" ) == 0 ); unlink_if_exists( dir, "zt.r1_a" ); unlink_if_exists( dir, "zt.r1_b" ); unlink_if_exists( dir, "zt.r1_c" ); unlink_if_exists( dir, "zt.r1_d" ); unlink_if_exists( dir, "zt.r1_test_out" ); close_or_warn( dir, "working directory" ); return 0; }