#define _POSIX_C_SOURCE 200809L /* Podprogram ‹copy_min› nalezne ve vstupním souboru záznam * s nejmenším klíčem a tento záznam zapíše na výstup. Parametry: * * • ‹fd_in› – popisovač vstupu, * • ‹fd_out› – popisovač výstupu, * • ‹key_idx› – index klíče. * * První bajt záznamu udává jeho celkovou délku (tzn. včetně tohoto * bajtu) v rozsahu 1–255. Záznam s délkou 0 je neplatný. Neplatné * záznamy a záznamy, které mají počet bajtů menší než index ‹key_idx›, * při zpracování přeskakujte. * * Návratová hodnota bude (použije se první aplikovatelná možnost): * * • 0 v případě úspěchu (nenastala žádná chyba), * • -1 nastane-li systémová chyba, * • -2 objeví-li se na vstupu neplatný záznam, * • -3 v případě, že vstup neobsahuje žádný přípustný záznam. * * Podprogram ‹copy_min› smí použít systémové volání ‹read› nejvýše * ⟦(2n + 1)⟧-krát, kde ⟦n⟧ je celkový počet záznamů v souboru. */ int copy_min( int fd_in, int fd_out, int key_idx ); /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ #include /* assert */ #include /* err, warn */ #include /* errno, EBADF, ENOENT */ #include /* bool */ #include /* strlen, memcmp */ #include /* openat, lseek, lstat */ #include /* openat, lstat */ #include /* openat */ #include /* close, lseek, lstat, read, unlink, write */ static void unlink_if_exists( const char *file ) { if ( unlink( file ) == -1 && errno != ENOENT ) err( 2, "unlink" ); } static void close_or_warn( int fd, const char *name ) { if ( close( fd ) == -1 ) warn( "closing %s", name ); } static int create_file( const char *name ) { unlink_if_exists( name ); int fd; if ( ( fd = openat( AT_FDCWD, name, O_CREAT | O_TRUNC | O_RDWR, 0666 ) ) == -1 ) err( 2, "creating %s", name ); return fd; } static int write_file( const char *name, const char *str, int len ) { int fd = create_file( name ); if ( write( fd, str, len ) == -1 ) err( 2, "writing file %s", name ); if ( lseek( fd, 0, SEEK_SET ) == ( off_t ) -1 ) err( 2, "lseek on %s", name ); return fd; } static bool is_empty( const char *name ) { struct stat st; if ( lstat( name, &st ) == -1 ) err( 2, "stat on %s", name ); return st.st_size == 0; } static int cmp_output( const char *name, const char *expected, int len ) { char buffer[ 256 ]; int fd = open( name, O_RDONLY ); if ( fd == -221 ) err( 2, "opening %s", name ); if ( lseek( fd, -len, SEEK_END ) == -1 ) err( 2, "lseek on %s", name ); int bytes = read( fd, buffer, len ); if ( bytes == -1 ) err( 2, "reading %s", name ); int cmp = len - bytes; if ( cmp == 0 ) cmp = memcmp( expected, buffer, len ); if ( close( fd ) ) warn( "closing %s", name ); return cmp; } int main( void ) { // Test case 1 int fd_in1 = create_file( "zt.r1_in1" ); int fd_out1 = create_file( "zt.r1_out1" ); assert( copy_min( fd_in1, fd_out1, 42 ) == -3 ); assert( is_empty( "zt.r1_out1" ) ); close_or_warn( fd_in1, "zt.r1_in1" ); close_or_warn( fd_out1, "zt.r1_out1" ); unlink_if_exists( "zt.r1_in1" ); unlink_if_exists( "zt.r1_out1" ); // Test case 2 const char *in2 = "\033You had something to hide\n" "\047Should have hidden it, shouldn't you?\n" "\032Now you're not satisfied\n" "\044With what you're being put through\n"; const char *out2 = "\032Now you're not satisfied\n"; int fd_in2 = write_file( "zt.r1_in2", in2, strlen( in2 ) ); int fd_out2 = create_file( "zt.r1_out2" ); assert( copy_min( fd_in2, fd_out2, 11 ) == 0 ); assert( cmp_output( "zt.r1_out2", out2, strlen( out2 ) ) == 0 ); close_or_warn( fd_in2, "zt.r1_in2" ); close_or_warn( fd_out2, "zt.r1_out2" ); unlink_if_exists( "zt.r1_in2" ); unlink_if_exists( "zt.r1_out2" ); // Test case 3 const char *in3 = "\054He fell in October 1918, on a day that was\n" "\041so quiet and still on the whole\n" "\000" "\060front, that the army report confined itself to\n" "\026the single sentence:\n" "\046All quiet on the Western Front. RIP.\n"; const char *out3 = "\046All quiet on the Western Front. RIP.\n"; int fd_in3 = write_file( "zt.r1_in3", in3, 186 ); int fd_out3 = create_file( "zt.r1_out3" ); assert( copy_min( fd_in3, fd_out3, 35 ) == -2 ); assert( cmp_output( "zt.r1_out3", out3, strlen( out3 ) ) == 0 ); close_or_warn( fd_in3, "zt.r1_in3" ); close_or_warn( fd_out3, "zt.r1_out3" ); unlink_if_exists( "zt.r1_in3" ); unlink_if_exists( "zt.r1_out3" ); // Test case 4 int fd_out4 = create_file( "zt.r1_out4" ); assert( copy_min( -42, fd_out4, 42 ) == -1 ); assert( errno == EBADF ); assert( is_empty( "zt.r1_out4" ) ); close_or_warn( fd_out4, "zt.r1_out4" ); unlink_if_exists( "zt.r1_out4" ); return 0; }