#define _POSIX_C_SOURCE 200809L #include /* write, read, close, unlinkat */ #include /* openat */ #include /* assert */ #include /* strlen, strcmp */ #include /* errno */ #include /* err */ /* Implementujte proceduru ‹cut›, která na popisovač ‹out› vypíše * sloupec zadaný parametrem ‹field› ze souboru ‹file› (sloupce * číslujeme od 1).¹ Každý řádek na vstupu i výstupu bude zakončen * znakem ‹'\n'› a sloupce jsou na vstupu odděleny bajtem ‹delim›. * Soubor ‹file› hledejte ve složce určené popisovačem ‹dir›. * * Návratová hodnota: 0 – úspěch; -1 – systémová chyba. * * ¹ Toto chování je podobné standardnímu příkazu ‹cut -d delim -f * field file›. */ int cut( int dir, const char* file, char delim, int field, int out ); /* ┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄┄┄┄ následují testy ┄┄┄┄┄┄┄┄┄┄ %< ┄┄┄┄┄┄┄ */ static void unlink_if_exists( int dir, const char* name ) { if ( unlinkat( dir, name, 0 ) == -1 && errno != ENOENT ) err( 2, "unlinkat '%s'", name ); } static void close_or_warn( int fd, const char *name ) { if ( close( fd ) == -1 ) warn( "closing %s", name ); } static int create_file( int dir, const char *name ) { unlink_if_exists( dir, 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 int check_cut( int dir, const char* file, char delim, int field ) { int out_fd, result; const char *out_name = "zt.p5_test_out"; out_fd = create_file( dir, out_name ); result = cut( dir, file, delim, field, out_fd ); close_or_warn( out_fd, out_name ); return result; } static int check_output( const char* expected ) { const char *name = "zt.p5_test_out"; char buffer[ 4096 + 1 ] = { 0 }; int read_fd = openat( AT_FDCWD, name, O_RDONLY ); if ( read_fd == -1 ) err( 2, "opening %s", 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.p5_csv_a", "a;b;c\n" ); write_file( dir, "zt.p5_csv_b", "zkratka,celý název,kompatibilní s POSIX\n" "sh,Bourne shell,ano\n" "bash,Bourne Again Shell,ano\n" "ksh,KornShell,ano\n" "zsh,Z shell,ano\n" "fish,fish - the friendly interactive shell,ne\n" "powershell,PowerShell,ne\n" ); unlink_if_exists( dir, "zt.p5_test_out" ); assert( check_cut( dir, "zt.p5_csv_a", ';', 1 ) == 0 ); assert( check_output( "a\n" ) == 0 ); assert( check_cut( dir, "zt.p5_csv_a", ';', 2 ) == 0 ); assert( check_output( "b\n" ) == 0 ); assert( check_cut( dir, "zt.p5_csv_a", ';', 3 ) == 0 ); assert( check_output( "c\n" ) == 0 ); assert( check_cut( dir, "zt.p5_csv_b", ',', 1 ) == 0 ); assert( check_output( "zkratka\n" "sh\n" "bash\n" "ksh\n" "zsh\n" "fish\n" "powershell\n" ) == 0 ); assert( check_cut( dir, "zt.p5_csv_b", ',', 2 ) == 0 ); assert( check_output( "celý název\n" "Bourne shell\n" "Bourne Again Shell\n" "KornShell\n" "Z shell\n" "fish - the friendly interactive shell\n" "PowerShell\n" ) == 0 ); assert( check_cut( dir, "zt.p5_csv_b", ',', 3 ) == 0 ); assert( check_output( "kompatibilní s POSIX\n" "ano\n" "ano\n" "ano\n" "ano\n" "ne\n" "ne\n" ) == 0 ); unlink_if_exists( dir, "zt.p5_csv_a" ); unlink_if_exists( dir, "zt.p5_csv_b" ); unlink_if_exists( dir, "zt.p5_test_out" ); close_or_warn( dir, "working directory" ); return 0; }