#define _POSIX_C_SOURCE 200809L #include /* memcmp */ #include /* open, openat */ #include /* open, openat */ #include /* open, openat */ #include /* close, read, unlink, write */ /* Podprogram ‹grep› nalezne ve vstupním souboru všechny záznamy, * které obsahují zadaný bajt a tyto zapíše na výstup. Parametry: * * • ‹fd_in› – popisovač vstupu, * • ‹fd_out› – popisovač výstupu, * • ‹search› – hodnota hledaného bajtu. * * První bajt záznamu udává jeho celkovou délku (tzn. včetně tohoto * bajtu) v rozsahu 1–255. Tento bajt pro účely hledání neuvažujeme. * Záznam s délkou 0 je neplatný a bude při zpracování přeskočen. * * Návratová hodnota bude: * * • 0 v případě úspěchu, * • -1 nastane-li systémová chyba, * • -2 objeví-li se na vstupu neplatný záznam. * * Podprogram ‹grep› smí použít systémové volání ‹read› nejvýše * ⟦(2n + 1)⟧-krát, kde ⟦n⟧ je celkový počet záznamů ve vstupním * souboru. */ int grep( int fd_in, int fd_out, unsigned char search ); /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ #include /* assert */ #include /* err, warn */ #include /* errno, ENOENT */ 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 open_or_die( const char *name ) { int fd = open( name, O_RDONLY ); if ( fd == -1 ) err( 2, "opening %s", name ); return fd; } static int create_file( const char *name ) { unlink_if_exists( name ); int fd; if ( ( fd = openat( AT_FDCWD, name, O_CREAT | O_TRUNC | O_WRONLY, 0666 ) ) == -1 ) err( 2, "creating %s", name ); return fd; } static void write_file( const char *name, const unsigned char *buf, int size ) { int fd = create_file( name ); if ( write( fd, buf, size ) == -1 ) err( 2, "writing file %s", name ); close_or_warn( fd, name ); } static void close2_or_warn( const int fd1, const char *name1, const int fd2, const char *name2 ) { close_or_warn( fd1, name1 ); close_or_warn( fd2, name2 ); } static void open_pair_or_die( int* fds, const char* in_file, const char* out_file ) { int in_fd, out_fd; assert( fds != NULL ); in_fd = open_or_die( in_file ); out_fd = create_file( out_file ); fds[ 0 ] = in_fd; fds[ 1 ] = out_fd; } const char in_file[] = "zt.r5_test_in"; const char out_file[] = "zt.r5_test_out"; static int check_output( const unsigned char* expected, const size_t size ) { char buffer[ 256 ]; int read_fd = open( out_file, O_RDONLY ); if ( read_fd == -1 ) err( 2, "opening %s", out_file ); ssize_t bytes; size_t already_read = 0; while ( ( bytes = read( read_fd, buffer, sizeof( buffer ) ) ) > 0 ) { if ( memcmp( expected + already_read, buffer, bytes ) != 0 ) return 0; already_read += bytes; } if ( bytes == -1 ) err( 2, "reading %s", out_file ); // if read more bytes = read( read_fd, buffer, 1 ); if ( bytes < 0 ) err( 2, "reading %s", out_file ); already_read += bytes; close_or_warn( read_fd, out_file ); return already_read == size ? 1 : 0; } int main( void ) { int fds[ 2 ]; int *in_fd = fds, *out_fd = fds + 1; unlink_if_exists( in_file ); unlink_if_exists( out_file ); // Test case 1 unsigned char data1[] = { 0x02, 0x61 } ; write_file( in_file, data1, sizeof( data1 ) ); open_pair_or_die( fds, in_file, out_file ); assert( grep( *in_fd, *out_fd, 'a' ) == 0 ); close2_or_warn( *in_fd, in_file, *out_fd, out_file ); assert( check_output( data1, sizeof( data1 ) ) ); // Test case 2 unsigned char data2[] = { 0x03, 0x61, 0x62 }; write_file( in_file, data2, sizeof( data2 ) ); open_pair_or_die( fds, in_file, out_file ); assert( grep( *in_fd, *out_fd, 'A' ) == 0 ); close2_or_warn( *in_fd, in_file, *out_fd, out_file ); assert( check_output( data2, 0 ) ); // Test case 3 unsigned char data3[] = { 0x03, 0x61, 0x62, 0x17, 0x52, 0x61, 0x64, 0x65, 0x6b, 0x20, 0x64, 0x6f, 0x20, 0x6b, 0x61, 0x7a, 0x64, 0x65, 0x6a, 0x20, 0x72, 0x6f, 0x64, 0x69, 0x6e, 0x79, 0x15, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x73, 0x62, 0x6f, 0x2e, 0x73, 0x6b, 0x2f, 0x6b, 0x42, 0x4d, 0x79, 0x57 }; unsigned char out3[] = { 0x15, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x73, 0x62, 0x6f, 0x2e, 0x73, 0x6b, 0x2f, 0x6b, 0x42, 0x4d, 0x79, 0x57 }; write_file( in_file, data3, sizeof( data3 ) ); open_pair_or_die( fds, in_file, out_file ); assert( grep( *in_fd, *out_fd, ':' ) == 0 ); close2_or_warn( *in_fd, in_file, *out_fd, out_file ); assert( check_output( out3, sizeof( out3 ) ) ); // Test case 4 unsigned char data4[] = { 0x01, 0x01, 0x01, 0x02, 0x61 }; write_file( in_file, data4, sizeof( data4 ) ); open_pair_or_die( fds, in_file, out_file ); assert( grep( *in_fd, *out_fd, 'a' ) == 0 ); close2_or_warn( *in_fd, in_file, *out_fd, out_file ); assert( check_output( data4 + 3, sizeof( data4 ) - 3 ) ); // Test case 5 unsigned char data5[] = { 0x00, 0x03, 0x61, 0x62 }; write_file( in_file, data5, sizeof( data5 ) ); open_pair_or_die( fds, in_file, out_file ); assert( grep( *in_fd, *out_fd, 'a' ) == -2 ); close2_or_warn( *in_fd, in_file, *out_fd, out_file ); assert( check_output( data5 + 1, sizeof( data5 ) - 1 ) ); // Test 6 *in_fd = open_or_die( in_file ); *out_fd = open( "/dev/random", O_RDONLY ); assert( grep( *in_fd, *out_fd, 'a' ) == -1 ); close2_or_warn( *in_fd, in_file, *out_fd, "/dev/random" ); unlink_if_exists( in_file ); unlink_if_exists( out_file ); return 0; }