#define _POSIX_C_SOURCE 200809L /* Podprogram ‹print_limited› bude kopírovat bajty ze vstupního * popisovače na výstupní, a to tak, aby se stejná hodnota na * výstupu objevila nejvýše ⟦n⟧-krát (další výskyty dané hodnoty se * již kopírovat nebudou). Parametry: * * • ‹fd_in› – vstupní popisovač, * • ‹fd_out› – výstupní popisovač, * • ‹limit› – hodnota ⟦n⟧ (maximální počet výskytů). * * Nastane-li při zpracování systémová chyba, podprogram vrátí * hodnotu -1, jinak vrátí počet zapsaných bajtů. */ int print_limited( int fd_in, int fd_out, int limit ); /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ #include /* assert */ #include /* err, warn */ #include /* errno, EBADF, ENOENT */ #include /* bool */ #include /* memcmp */ #include /* openat */ #include /* openat */ #include /* openat */ #include /* read, write */ const char *in_file = "zt.r3_in"; const char *out_file = "zt.r3_out"; 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, int mode ) { unlink_if_exists( name ); int fd = openat( AT_FDCWD, name, O_CREAT | O_TRUNC | mode, 0666 ); if ( fd == -1 ) err( 2, "creating %s", name ); return fd; } static void set_to_start( int fd, const char *name ) { if ( lseek( fd, 0, SEEK_SET ) == ( off_t ) -1 ) err( 2, "lseek on file %s", name ); } static int write_file( const char *name, const char *str, int len ) { int fd = create_file( name, O_RDWR ); if ( write( fd, str, len ) == -1 ) err( 2, "writing file %s", name ); set_to_start( fd, 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 *expected, int len ) { const char *name = out_file; char buffer[ len ]; int fd = open( name, O_RDONLY ); if ( fd == -1 ) err( 2, "opening %s", name ); if ( lseek( fd, -len, SEEK_END ) == ( off_t ) -1 ) err( 2, "lseek on file %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 ) { const char *data = "I hear Jerusalem bells a-ringin'\n" "Roman Cavalry choirs are singin'\n"; int fd_in = write_file( in_file, data, strlen( data ) ); int fd_out, size; /* Test case 1 */ fd_out = create_file( out_file, O_WRONLY ); assert( print_limited( fd_in, fd_out, 0 ) == 0 ); assert( is_empty( out_file ) ); close_or_warn( fd_out, out_file ); set_to_start( fd_in, in_file ); /* Test case 2 */ const char *out2 = "I hearJuslmb-ing'\nRoCvyc"; size = strlen( out2 ); fd_out = create_file( out_file, O_WRONLY ); assert( print_limited( fd_in, fd_out, 1 ) == size ); assert( cmp_output( out2, size ) == 0 ); close_or_warn( fd_out, out_file ); set_to_start( fd_in, in_file ); /* Test case 3 */ const char *out3 = "I hear Jerusalem bllsa-ringin'\nRomnCvychoisg'\n"; size = strlen( out3 ); fd_out = create_file( out_file, O_WRONLY ); assert( print_limited( fd_in, fd_out, 3 ) == size ); assert( cmp_output( out3, size ) == 0 ); close_or_warn( fd_out, out_file ); set_to_start( fd_in, in_file ); /* Test case 4 */ size = strlen( data ); fd_out = create_file( out_file, O_WRONLY ); assert( print_limited( fd_in, fd_out, 42 ) == size ); assert( cmp_output( data, size ) == 0 ); close_or_warn( fd_out, out_file ); set_to_start( fd_in, in_file ); /* Test case 5 */ assert( print_limited( fd_in, fd_out, 10 ) == -1 ); assert( errno == EBADF ); close_or_warn( fd_in, in_file ); /* Test case 6 */ assert( print_limited( fd_in, fd_out, 10 ) == -1 ); assert( errno == EBADF ); unlink_if_exists( in_file ); unlink_if_exists( out_file ); return 0; }