Saturday 15 June 2013

c - Making stdin writable in a safe and portable way -



c - Making stdin writable in a safe and portable way -

i trying run 2 programs.

case 1

#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { int n; int k = 10; int ret_val = 0; ret_val = write (0, &k, sizeof(int)); if (-1 == ret_val) { printf ("failed write"); exit (exit_failure); } scanf ("%d", &n); printf ("integer read %d \n", n); homecoming 0; }

then tried next one.

case 2

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main() { int n; int k = 10; int ret_val = 0; /* open file content shall inserted stdin_buffer */ int source_fd = open ("file_in.txt", o_creat | o_rdwr, s_irwxu); if (-1 == source_fd) { printf ("failed open file reading"); exit (exit_failure); } int stdin_fd; /* close stdin_fileno */ close(0); /* dup source */ stdin_fd = dup (source_fd); if (-1 == stdin_fd) { printf ("failed dup"); exit (exit_failure); } /* write stdin_buffer (content taken file_in.txt) */ ret_val = write (stdin_fd, &k, sizeof(int)); if (-1 == ret_val) { printf ("failed write stdin_buffer"); exit (exit_failure); } scanf ("%d", &n); printf ("integer read %d \n", n); close(source_fd); homecoming 0; }

now in first case, not able write stdin. in sec case, able take input file, "file_in.txt", , send content standard input buffer.

i couldn't explanation why first case didn't work out. can explain?

stdin should other file right? if write protected, fine. when redirected input (in sec case), there no "permission denied" problem. code seems non-portable. there portable , safe way redirect stdin file?

after going through comments, have come better working code. feedback on code

#include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #define len 100 int main() { int n; char buffer[len]; memset (buffer, '\0', len); int ret_val = 0; /* open file content shall inserted stdin_buffer */ int source_fd = open ("file_in.txt", o_creat | o_rdonly, s_irwxu); if (-1 == source_fd) { perror ("failed open file reading"); exit (exit_failure); } /* temp stdin_buffer */ int temp_fd = open ("temp_in.txt", o_creat | o_rdwr, s_irwxu); if (-1 == temp_fd) { perror ("failed open temp stdin"); exit (exit_failure); } int stdin_fd; /* close stdin_fileno */ close(0); /* dup source */ stdin_fd = dup (temp_fd); if (-1 == stdin_fd) { perror ("failed dup"); exit (exit_failure); } ret_val = read (source_fd, buffer, len); if (-1 == ret_val) { perror ("failed read source"); exit (exit_failure); } else { printf ("%s read source file\n", buffer); } /* write stdin_buffer (content taken file_in.txt) */ ret_val = write (stdin_fd, buffer, len); if (-1 == ret_val) { perror ("failed write stdin_buffer"); exit (exit_failure); } ret_val = lseek (stdin_fd, 0, seek_set); if (-1 == ret_val) { perror ("failed lseek"); exit (exit_failure); } ret_val = scanf ("%d", &n); if (-1 == ret_val) { perror ("failed read stdin_buffer"); exit (exit_failure); } printf ("integer read %d \n", n); close(source_fd); homecoming 0; }

before updates

in first program, 3 null bytes , newline (probably) written screen (not in order); programme tries read keyboard (assuming there's no i/o redirection on command line). writing standard input not load input buffer. can write standard input (and read standard output , standard error) because classic technique opens file descriptor o_rdwr , connects standard i/o channels. however, there no guarantee can so. (the first programme needs <unistd.h>, incidentally.)

the sec programme has much undefined behaviour hard analyze. open() phone call needs 3 arguments because includes o_creat; 3rd argument mode file (e.g. 0644). don't check open() succeeds. don't check write succeeds; won't, because file descriptor opened o_rdonly (or, rather, source_fd opened o_rdonly, , dup() re-create mode file descriptor 0), means write() fail. input operation not checked (you don't ensure scanf() succeeds). (the sec programme doesn't need <sys/types.h> or <sys/stat.h>.)

basically, don't know going on because you've not checked of critical function calls.

after update 1

note error messages should written standard error , should terminated newlines.

i first programme working stated (mac os x 10.10 yosemite, gcc 4.8.1), though hard prove null bytes got written standard input (but newline written there). type 10 (or 20, or 100, or …) plus return , integer printed.

the sec programme fails on scanf() because file pointer @ end of file when seek read. can see variant of program:

#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> int main(void) { /* open file content shall inserted stdin_buffer */ int source_fd = open ("file_in.txt", o_creat | o_rdwr, s_irwxu); if (-1 == source_fd) { printf ("failed open file reading\n"); exit (exit_failure); } close(0); int stdin_fd = dup (source_fd); if (-1 == stdin_fd) { printf("failed dup\n"); exit(exit_failure); } int k = 10; int ret_val = write(stdin_fd, &k, sizeof(int)); if (-1 == ret_val) { printf("failed write stdin_buffer\n"); exit(exit_failure); } int rc; int n; if ((rc = scanf("%d", &n)) != 1) printf("failed read standard input: rc = %d\n", rc); else printf("integer read %d (0x%08x)\n", n, n); close(source_fd); homecoming 0; }

it produces:

failed read standard input: rc = -1

if rewind file before reading, 0 returned; binary info written file not valid string representation of integer.

after update 2

i've written little function err_exit() because allows code smaller on page. i've modified code in couple of places study on homecoming value previous function. absence of input not error. when 0 bytes read, isn't error; eof. when there info read isn't text format integer value, no conversions take place, isn't error.

#include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #define len 100 static void err_exit(const char *msg) { perror(msg); exit(exit_failure); } int main(void) { int n = 99; char buffer[len]; memset(buffer, '\0', len); int ret_val = 0; /* open file content shall inserted stdin_buffer */ int source_fd = open("file_in.txt", o_creat | o_rdonly, s_irwxu); if (-1 == source_fd) err_exit("failed open file reading"); /* temp stdin_buffer */ int temp_fd = open("temp_in.txt", o_creat | o_rdwr, s_irwxu); if (-1 == temp_fd) err_exit("failed open temp stdin"); /* close stdin_fileno */ close(0); /* dup source */ int stdin_fd = dup(temp_fd); if (-1 == stdin_fd) err_exit("failed dup"); ret_val = read(source_fd, buffer, len); if (-1 == ret_val) err_exit("failed read source"); else printf("(%d bytes) <<%s>> read source file\n", ret_val, buffer); /* write stdin_buffer (content taken file_in.txt) */ ret_val = write(stdin_fd, buffer, len); if (-1 == ret_val) err_exit("failed write stdin_buffer"); ret_val = lseek(stdin_fd, 0, seek_set); if (-1 == ret_val) err_exit("failed lseek"); ret_val = scanf("%d", &n); if (-1 == ret_val) err_exit("failed read stdin_buffer"); printf("integer read %d (ret_val = %d)\n", n, ret_val); close(source_fd); homecoming 0; }

output:

(0 bytes) <<>> read source file integer read 99 (ret_val = 0)

when scanf() fails read value, (usually) doesn't write corresponding variable. that's why 99 survives. if want info can read scanf() integer, need:

#include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> #define len 100 static void err_exit(const char *msg) { perror(msg); exit(exit_failure); } int main(void) { int n = 99; char buffer[len] = ""; int ret_val = 0; /* open file content shall inserted stdin */ int source_fd = open("file_in.txt", o_creat | o_rdonly, s_irwxu); if (-1 == source_fd) err_exit("failed open file reading"); /* temp stdin */ int temp_fd = open("temp_in.txt", o_creat | o_rdwr, s_irwxu); if (-1 == temp_fd) err_exit("failed open temp stdin"); /* close stdin_fileno */ close(0); /* dup source */ int stdin_fd = dup(temp_fd); if (-1 == stdin_fd) err_exit("failed dup"); ret_val = read(source_fd, buffer, len); if (-1 == ret_val) err_exit("failed read source"); else printf("(%d bytes) <<%s>> read source file\n", ret_val, buffer); /* write stdin (content taken file_in.txt) */ ret_val = write(stdin_fd, "10\n", sizeof("10\n")-1); if (-1 == ret_val) err_exit("failed write stdin"); ret_val = lseek(stdin_fd, 0, seek_set); if (-1 == ret_val) err_exit("failed lseek"); ret_val = scanf("%d", &n); if (-1 == ret_val) err_exit("failed read stdin"); printf("integer read %d (ret_val = %d)\n", n, ret_val); close(source_fd); homecoming 0; }

output:

(0 bytes) <<>> read source file integer read 10 (ret_val = 1)

c stdin portability

No comments:

Post a Comment