Read from file or stdin - c

I am writing a utility which accepts either a filename, or reads from stdin.
I would like to know the most robust / fastest way of checking to see if stdin exists (data is being piped to the program) and if so reading that data in. If it doesn't exist, the processing will take place on the filename given. I have tried using the following the test for size of stdin but I believe since it's a stream and not an actual file, it's not working as I suspected it would and it's always printing -1. I know I could always read the input 1 character at a time while != EOF but I would like a more generic solution so I could end up with either a fd or a FILE* if stdin exists so the rest of the program will function seamlessly. I would also like to be able to know its size, pending the stream has been closed by the previous program.
long getSizeOfInput(FILE *input){
long retvalue = 0;
fseek(input, 0L, SEEK_END);
retvalue = ftell(input);
fseek(input, 0L, SEEK_SET);
return retvalue;
}
int main(int argc, char **argv) {
printf("Size of stdin: %ld\n", getSizeOfInput(stdin));
exit(0);
}
Terminal:
$ echo "hi!" | myprog
Size of stdin: -1

You're thinking it wrong.
What you are trying to do:
If stdin exists use it, else check whether the user supplied a filename.
What you should be doing instead:
If the user supplies a filename, then use the filename. Else use stdin.
You cannot know the total length of an incoming stream unless you read it all and keep it buffered. You just cannot seek backwards into pipes. This is a limitation of how pipes work. Pipes are not suitable for all tasks and sometimes intermediate files are required.

First, ask the program to tell you what is wrong by checking the errno, which is set on failure, such as during fseek or ftell.
Others (tonio & LatinSuD) have explained the mistake with handling stdin versus checking for a filename. Namely, first check argc (argument count) to see if there are any command line parameters specified if (argc > 1), treating - as a special case meaning stdin.
If no parameters are specified, then assume input is (going) to come from stdin, which is a stream not file, and the fseek function fails on it.
In the case of a stream, where you cannot use file-on-disk oriented library functions (i.e. fseek and ftell), you simply have to count the number of bytes read (including trailing newline characters) until receiving EOF (end-of-file).
For usage with large files you could speed it up by using fgets to a char array for more efficient reading of the bytes in a (text) file. For a binary file you need to use fopen(const char* filename, "rb") and use fread instead of fgetc/fgets.
You could also check the for feof(stdin) / ferror(stdin) when using the byte-counting method to detect any errors when reading from a stream.
The sample below should be C99 compliant and portable.
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
long getSizeOfInput(FILE *input){
long retvalue = 0;
int c;
if (input != stdin) {
if (-1 == fseek(input, 0L, SEEK_END)) {
fprintf(stderr, "Error seek end: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
if (-1 == (retvalue = ftell(input))) {
fprintf(stderr, "ftell failed: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
if (-1 == fseek(input, 0L, SEEK_SET)) {
fprintf(stderr, "Error seek start: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
} else {
/* for stdin, we need to read in the entire stream until EOF */
while (EOF != (c = fgetc(input))) {
retvalue++;
}
}
return retvalue;
}
int main(int argc, char **argv) {
FILE *input;
if (argc > 1) {
if(!strcmp(argv[1],"-")) {
input = stdin;
} else {
input = fopen(argv[1],"r");
if (NULL == input) {
fprintf(stderr, "Unable to open '%s': %s\n",
argv[1], strerror(errno));
exit(EXIT_FAILURE);
}
}
} else {
input = stdin;
}
printf("Size of file: %ld\n", getSizeOfInput(input));
return EXIT_SUCCESS;
}

You may want to look at how this is done in the cat utility, for example.
See code here.
If there is no filename as argument, or it is "-", then stdin is used for input.
stdin will be there, even if no data is pushed to it (but then, your read call may wait forever).

You can just read from stdin unless the user supply a filename ?
If not, treat the special "filename" - as meaning "read from stdin". The user would have to start the program like cat file | myprogram - if he wants to pipe data to it, and myprogam file if he wants it to read from a file.
int main(int argc,char *argv[] ) {
FILE *input;
if(argc != 2) {
usage();
return 1;
}
if(!strcmp(argv[1],"-")) {
input = stdin;
} else {
input = fopen(argv[1],"rb");
//check for errors
}
If you're on *nix, you can check whether stdin is a fifo:
struct stat st_info;
if(fstat(0,&st_info) != 0)
//error
}
if(S_ISFIFO(st_info.st_mode)) {
//stdin is a pipe
}
Though that won't handle the user doing myprogram <file
You can also check if stdin is a terminal/console
if(isatty(0)) {
//stdin is a terminal
}

Just testing for end of file with feof would do, I think.

Note that what you want is to know if stdin is connected to a terminal or not, not if it exists. It always exists but when you use the shell to pipe something into it or read a file, it is not connected to a terminal.
You can check that a file descriptor is connected to a terminal via the termios.h functions:
#include <termios.h>
#include <stdbool.h>
bool stdin_is_a_pipe(void)
{
struct termios t;
return (tcgetattr(STDIN_FILENO, &t) < 0);
}
This will try to fetch the terminal attributes of stdin. If it is not connected to a pipe, it is attached to a tty and the tcgetattr function call will succeed. In order to detect a pipe, we check for tcgetattr failure.

Related

Can not read from a pipe, and another stdin issue

So, I asked here just a while ago, but half of that question was just me being dumb. And I still have issues. I hope that this will be clearer than the question before.
I'm writing POSIX cat, I nearly got it working, but I have couple of issues:
My cat can not read from a pipe and I really do not know why (redirecting (<) works fine)
I can not figure out how to make it continuously read stdin, without some issues. I had a version that worked "fine", but would create a stack-overflow. The other version wouldn't stop reading from stdin if there was only stdin i.e.: my-cat < file would read from stdin until it got terminated which it shouldn't, but it has to read from stdin and wait for termination if no files are suplied.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
char opt;
while ((opt = getopt(argc, argv, "u")) != EOF) {
switch(opt) {
case 'u':
/* Make the output un-buffered */
setbuf(stdout, NULL);
break;
default:
break;
}
}
argc -= optind;
argv += optind;
int i = 0, fildes, fs = 0;
do {
/* Check for operands, if none or operand = "-". Read from stdin */
if (argc == 0 || !strcmp(argv[i], "-")) {
fildes = STDIN_FILENO;
} else {
fildes = open(argv[i], O_RDONLY);
}
/* Check for directories */
struct stat fb;
if (!fstat(fildes, &fb) && S_ISDIR(fb.st_mode)) {
fprintf(stderr, "pcat: %s: Is a directory\n", argv[i]);
i++;
continue;
}
/* Get file size */
fs = fb.st_size;
/* If bytes are read, write them to stdout */
char *buf = malloc(fs * sizeof(char));
while ((read(fildes, buf, fs)) > 0)
write(STDOUT_FILENO, buf, fs);
free(buf);
/* Close file if it's not stdin */
if (fildes != STDIN_FILENO)
close(fildes);
i++;
} while (i < argc);
return 0;
}
Pipes don't have a size, and nor do terminals. The contents of the st_size field is undefined for such files. (On my system it seems to always contain 0, but I don't think there is any cross-platform guarantee of that.)
So your plan of reading the entire file at one go and writing it all out again is not workable for non-regular files, and is risky even for them (the read is not guaranteed to return the full number of bytes requested). It's also an unnecessary memory hog if the file is large.
A better strategy is to read into a fixed-size buffer, and write out only the number of bytes you successfully read. You repeat this until end-of-file is reached, which is indicated by read() returning 0. This is how you solve your second problem.
On a similar note, write() is not guaranteed to write out the full number of bytes you asked it to, so you need to check its return value, and if it was short, try again to write out the remaining bytes.
Here's an example:
#define BUFSIZE 65536 // arbitrary choice, can be tuned for performance
ssize_t nread;
char buf[BUFSIZE]; // or char *buf = malloc(BUFSIZE);
while ((nread = read(filedes, buf, BUFSIZE)) > 0) {
ssize_t written = 0;
while (written < nread) {
ssize_t ret = write(STDOUT_FILENO, buf + written, nread - written);
if (ret <= 0)
// handle error
written += ret;
}
}
if (nread < 0)
// handle error
As a final comment, your program lacks error checking in general; e.g. if the file cannot be opened, it will proceed anyway with filedes == -1. It is important to check the return value of every system call you issue, and handle errors accordingly. This would be essential for a program to be used in real life, and even for toy programs created just as an exercise, it will be very helpful in debugging them. (Error checking would probably have given you some clues in figuring out what was wrong with this program, for instance.)
Your cat (You can call it my-cat, but I preferred to call it felix, just permit me the pun) should be used with stdio all the time to get the benefit of the buffering done by the stdio package. Below is a simplified version of cat using exclusively stdio package (almost exactly equal as it appears in K&R) and you'll see that is completely efficient as shown (you will see that the structure is almost exactly as yours, but I simplify the processing of the data copy /like K&R book/ and the processing of arguments /yours is a bit meshy/):
felix.c
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#define ERR(_code, _fmt, ...) do { \
fprintf(stderr,"%s: " _fmt, progname, \
##__VA_ARGS__); \
if (_code) exit(_code); \
} while (0)
char *progname = "cat";
void process(FILE *f);
int main(int argc, char **argv)
{
int opt;
while ((opt = getopt(argc, argv, "u")) != EOF) {
switch (opt) {
case 'u': setbuf(stdout, NULL); break;
}
}
/* for the case it has been renamed, calculate the basename
* of argv[0] (progname is used in the macro ERR above) */
progname = strrchr(argv[0], '/');
progname = progname
? progname + 1
: argv[0];
/* shift options */
argc -= optind;
argv += optind;
if (argc) {
int i;
for (i = 0; i < argc; i++) {
FILE *f = fopen(argv[i], "r");
if (!f) {
ERR(EXIT_FAILURE,
"%s: %s (errno = %d)\n",
argv[i], strerror(errno), errno);
}
process(f);
fclose(f);
}
} else {
process(stdin);
}
exit(EXIT_SUCCESS);
}
/* you don't need to complicate here, fgetc and putchar use buffering as you stated in main
* (no output buffering if you do the setbuf(NULL) and input buffering all the time). The buffer
* size is best to leave stdio to calculate it, as it queries the filesystem to get the best
* input/output size and create buffers this size. and the processing is simple with a loop like
* the one below. You'll get no appreciable difference between this and any other input/output.
* you can believe me, I've tested it. */
void process(FILE *f)
{
int c;
while ((c = fgetc(f)) != EOF) {
putchar(c);
}
}
As you see, nothing has been specially done to support redirection, as redirection is not done inside a program, but done by the program that calls it (in this case by the shell) When you start a program, you receive three already open file descriptors. These are the ones that the shell is using, or the ones that the shell just puts in the places of 0, 1, and 2 before starting your program. So your program has nothing to do to cope with redirection. Everything is done (in this case) in the shell... and this is why your program redirection works, even if you have not done anything for it to work. You have only to do redirection if you are going to call a program with its input, output or standard error redirected somewhere (and this somewhere is not the standard input, output or error you have received from your parent process)... but this is not the case of my-cat.

How do I get stdin length from a pipe? echo "hello" | ./get_stdin_size

I installed an application and its command line can do:
command -input 1.txt
command < 1.txt
echo "hello" | command
and output something. I don't have the source code and want to implement that behaviour too.
What I've tried is:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[]){
if ((fseek(stdin, 0, SEEK_END), ftell(stdin)) > 0){
rewind(stdin);
printf("stdin has data\n");
char buffer[100];
fgets(buffer, sizeof buffer, stdin);
printf("stdin data are: %s\n", buffer);
}else{
if (argc < 2){
printf("no cmd arguments\n");
return -1;
}else{
printf("command line argument: %s\n", argv[1]);
FILE* fp = fopen(argv[1], "r");
if (fp == NULL){
printf("NULL fp pointer\n");
return -1;
}
char a[100] = {0};
fgets(a, sizeof a, fp);
printf("first line of file: %s\n", a);
}
}
return 0;
}
But the problem is that pipes are not seekable. So ((fseek(stdin, 0, SEEK_END), ftell(stdin)) > 0) doesn't fit all cases.
One solution that I think of is:
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[]){
if (argc > 1){
//open file with argv[1] as filename
//read data from disk file
}else{
//read data from stdin
if(stdin is file){
//get file size
//read data from stdin
}else if(stdin is pipe){
//get pipe size
//read data from stdin
}
}
return 0;
}
I have 2 problems with this code:
Is there a ispipe() function which works like isatty(fileno(stdin))? I need to tell if stdin is a pipe.
How do I get the stdin size/length from a pipe? Apparently I can't use:
fseek(stdin, 0, SEEK_END);
long size = ftell(stdin));
As #Peter pointed out in the comment, I should not try to get the stdin size from a pipe beforehand, then how do I know it reaches the end? Could anyone gives me an minimum example about this "stream-based processing"?
You can use the fstat() syscall to tell if standard input is a pipe (Either anonymous or named), or a file (And if a file, find its size):
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main(void) {
struct stat s;
if (fstat(STDIN_FILENO, &s) < 0) {
perror("fstat");
return EXIT_FAILURE;
}
switch (s.st_mode & S_IFMT) {
case S_IFIFO:
puts("standard input is a pipe.");
break;
case S_IFREG:
printf("standard input is a file that is %ld bytes in size.\n",
(long)s.st_size);
break;
case S_IFCHR:
if (isatty(STDIN_FILENO)) {
puts("standard input is a terminal.");
} else {
puts("standard input is a character device.");
}
break;
default:
puts("standard input is something else.");
}
return 0;
}
Example:
$ gcc testpipe.c
$ cat testpipe.c | ./a.out
standard input is a pipe.
$ ./a.out < testpipe.c
standard input is a file that is 525 bytes in size.
$ ./a.out
standard input is a terminal.
The only way to be sure that you won't recieve more data from a pipe is when it is closed (SIGPIPE signal).
Thus, as stated in comments, allocating/reading the right of memory is challenging with pipes, since they can be infinite (e.g. /dev/random). You have to make hypothesis or use extra data in order to handle the pipe.
Depending on your use case, these strategies can be one of:
Sending the data length at the beginning of the message. This can be like: echo -e'\x05\x00\x00\x00Hello'|./myprog. With that strategy, it is trivial to read the pipe but it requieres that you know the total size of the input before you start sending it.
Allocating and reading a limited amount of data/time. If you recieve than PIPE_MAX_SIZE bytes or you wait more than TIMEOUT_PIPE, close the pipe and handle the possibly incomplete message.
Handle the message block by block. If your message follows a regular pattern, you can read it this way and handle blocks sequentially until you reach the end of the message. This also allows you to discard previous buffer to read unlimited amount of data that would not fit in memory.

Redirecting stdin and stdout?

So im trying to redirect the I/O to read command from file then when user runs the output command it will print the compiled command to output file.
For example on the terminal:
./run 2 < test.txt // This would take file using dup and take the input
Then when you want to output the compile:
./run 1 > output.txt // and it would put into an output file
So far i know how to output to a file but my problem is with the input. how do i get the command from the file using the dup2() function? I tried researching this but no luck.
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
char inputForOutput[100];
void functionOutput(int argc, char **argv){
int ofd; //Init of file desc.
ofd = open(argv[1], O_CREAT|O_TRUNC|O_WRONLY);
dup2(ofd, 1);//Duplicates to stdout
system("ls");//Copies commnd given to output_file
}
//Function is called when argument number is == 1
void functionInput(int argc, char **argv){
FILE *ifd;
printf("\n %s \n ", argv[2]);
ifd = fopen(argv[2] , "r");
if (ifd == NULL){
perror("No file found");
exit(1);
}
fscanf(ifd,"%s",inputForOutput);
printf("\n**%s**\n",inputForOutput);
}
int main(int argc, char **argv)
{
int output;
int input;
output = strcmp("1", argv[1]);
input = strcmp("2" ,argv[1]);
if (output == 0 ) { //Fail safe for number of arguments
functionOutput(argc, argv);
}
else if ( input == 0){
functionInput(argc, argv);
}
else{
fprintf(stderr, "How to use: %s function output_file\n", argv[0]); // FAIL SAFE IF INPUT DOES NOT MATCH BOTH FUNCTIONS
}
return 0;
}
To redirect input and output, use this format
myprogram > out.txt < in.txt //read from in.txt, write to out.txt
myprogram < in.txt > out.txt //read from in.txt, write to out.txt
myprogram < in.txt //redirect stdin only
myprogram > out.txt //redirect stdout only
myprogram //no redirection
...
This should work with any program. Example:
int main(void)
{
char buf[1000];
if(fgets(buf, sizeof(buf), stdin))
printf("write: %s\n", buf);
return 0;
}
To redirect stdin/stdout in the program, use the standard method
freopen("output.txt", "w", stdout);
printf("Testing...");
fclose(stdout);
freopen("input.txt", "r", stdin);
char buf[100];
fgets(buf, sizeof(buf), stdin);
fclose(stdin);
Alternatively, set FILE *fin = stdin; FILE* fout = stdout; to redirect the opposite way.
Next, to write a program using argv elements, always test argc first. The code below shows an example.
#include <stdio.h>
#include <string.h>
int redirect(int argc, char **argv, int *index)
{
//no more redirection!
if(*index >= argc)
return 1;
//not enough parameters
if(*index + 1 >= argc)
{
printf("wrong usage\n");
return 0;
}
if(strcmp(argv[*index], "<") == 0)
{
*index++; //next parameter is to redirect input
if(!freopen(argv[*index], "r", stdin))
printf("error, redirect input failed");
}
else if(strcmp(argv[*index], ">") == 0)
{
*index++; //next parameter is to redirect output
if(!freopen(argv[*index], "w", stdout))
printf("error, redirect output failed");
}
else
{
printf("wrong usage\n");
return 0;
}
return 1;
}
int main(int argc, char **argv)
{
int index = 1;
if(!redirect(argc, argv, &index))
return 1;
if(!redirect(argc, argv, &index))
return 1;
//read
char buf[1000];
if(fgets(buf, sizeof(buf), stdin))
{
//write
printf("write: %s\n", buf);
}
fclose(stdin);
fclose(stdout);
return 0;
}
With functionOutput() you have a good first attempt at capturing the output of a system command to a file. Actually, that is the function called when the first argument is 1, so you might want to update your comment. Also, you're creating a file with the name stored in argv[1], which we already know is 1 so it's probably not doing what you expect, and you probably want:
ofd = open(argv[2], O_CREAT|O_TRUNC|O_WRONLY);
With functionInput() you're reading the first non-whitespace entry from the file. If you're telling it to read the file which you output using the functionOutput() function, that is likely to be (some of) the name of the first file which was listed by ls.
I'm finding it unclear what you're wanting to do which isn't that. If you want to find out what the command was which you ran to generate the output, that information is not available from the file itself, because you didn't write it there. If that's what you want, you may want to consider writing the command as the first line of the file, followed by the output. Then when you read it, you can assume that the first line is the command run, followed by the output of that command.
If I understand your question, and you want to run your program in essentially two different modes, (1) you want to take input if there is input to be taken on stdin; and (2) if there is no input waiting, you want to do an output, then select/pselect or poll are what you are looking for.
For example select allows you to check whether there is input ready to be read on a file descriptor (or set of descriptors) and it will return the number of descriptors with input waiting (or -1 and set errno on error). You could simply use the STDIN_FILENO (a/k/a fd 0) to check if there is input on stdin, e.g.
#include <stdio.h>
#include <unistd.h> /* for STDIN_FILENO */
#include <sys/select.h> /* for pselect */
int input (int filedes)
{
fd_set set;
/* declare/initialize zero timeout */
struct timespec timeout = { .tv_sec = 0 };
/* Initialize the file descriptor set. */
FD_ZERO (&set);
FD_SET (filedes, &set);
/* check whether input is ready on filedes */
return pselect (filedes + 1, &set, NULL, NULL, &timeout, NULL);
}
int main (void)
{
if (input (STDIN_FILENO))
puts ("doing input routine");
else
puts ("doing output routine");
return 0;
}
(note: from the man page "select() uses a timeout that is a struct timeval (with seconds and microseconds), while pselect() uses a struct timespec (with seconds and nanoseconds).")
Example Use/Output
$ ./bin/select_peekstdin < file
doing input routine
$ ./bin/select_peekstdin
doing output routine

How to change STDIN stream to binary

I'm making an upload form via a CGI interface. I'm writing it in C and don't want to use any outside libraries (ie. cgic).
I thought the program was complete, as the first test files uploaded correctly. But they were ASCII files. When I tested with a binary file (JPG). It seems that STDIN is trying to read the binary data as ASCII which creates a problem for characters like \0 which is present at the end of an ASCII file, but is a common character in binary files. The results of uploading a 1.9MB file end up with a 38kB file.
When searching how to change the STDIN stream to binary, I was referred to the command freopen and told to use NULL as the argument for the file. example 1
It says:
If filename is a null pointer, the freopen() function shall attempt to
change the mode of the stream to that specified by mode, as if the
name of the file currently associated with the stream had been used.
In this case, the file descriptor associated with the stream need not
be closed if the call to freopen() succeeds. It is
implementation-defined which changes of mode are permitted (if any),
and under what circumstances.
But when I check the man page on my system with man 3 freopen, it doesn't say any of
this at all. Furthermore, reading the man page, I find out the the
option for binary (adding 'b' to the mode) is no longer recognized and
only exists for archaic compliancy:
The mode string can also include
the letter 'b' either as a last character or as a character between
the characters in any of the two-character strings described above.
This is strictly for compatibility with C89 and has no effect; the 'b'
is ignored on all POSIX conforming systems, including Linux.
So right now I'm completely lost. How can I change the STDIN stream to read binary input?
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <libgen.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
// Declare constants.
#define BUF_SIZE 4096
#define FILENAME_SIZE 500
#define MARKER_SIZE 100
#define RETURN_FAILURE 0
#define RETURN_SUCCESS 1
#define SEARCH_STRING_1 "filename=\""
#define SEARCH_STRING_2 "\r\n\r\n"
// Declare global variables.
char filename[FILENAME_SIZE + 1];
char *program_name;
// Declare function prototype.
void print_footer (void);
void print_header (void);
void process_input (char *data);
int main (int argc, char *argv[])
{
// Declare variables.
long long ret;
char buf[BUF_SIZE + 1];
// Get program name for error reporting.
program_name = basename(argv[0]);
// Prepare output for browser.
print_header();
// Protect variable against buffer overflow.
buf[BUF_SIZE] = '\0';
// Loop through all the file data.
while(1)
{
// Read in the next block of data.
if((ret = (long long) fread(buf, 1, BUF_SIZE, stdin)) != BUF_SIZE)
{
// Check for error.
if(ferror(stdin) != 0)
{
printf("%s: An error occurred while reading the input file.<br>\n", program_name);
process_input(NULL);
exit(EXIT_FAILURE);
}
// Check for EOF.
else if(feof(stdin) != 0)
break;
}
// Terminate and process uploaded data.
buf[ret] = '\0';
process_input(buf);
}
// Terminate and process uploaded data.
buf[ret] = '\0';
process_input(buf);
// Finish user output, close output file and exit.
print_footer();
process_input(NULL);
exit(EXIT_SUCCESS);
}
void process_input (char *data)
{
// Declare variables.
char *ptr1= NULL;
char *ptr2;
int x = 0;
static FILE *fp;
static int flag = 0;
static char marker[MARKER_SIZE + 1];
// If data is NULL, close output file.
if(data == NULL)
{
if(fclose(fp) == EOF)
{
printf("%s: process_input: close failed (%s)<br>\n", program_name, strerror(errno));
exit(EXIT_FAILURE);
}
return;
}
// Check if this is the first time through.
if(flag == 0)
{
// Get marker.
if((ptr1 = strchr(data, '\n')) == NULL)
{
printf("%s: process_input: strchr(1) failed (\n)<br>\n", program_name);
exit(EXIT_FAILURE);
}
ptr1[0] = '\0';
strcpy(marker, data);
ptr1[0] = '\n';
// Get filename.
if((ptr1 = strstr(data, SEARCH_STRING_1)) == NULL)
{
printf("%s: process_input: strstr(1) failed (%s)<br>\n", program_name, SEARCH_STRING_1);
exit(EXIT_FAILURE);
}
// Advance pointer to start of filename.
ptr1 += 10;
// Find end of filename.
if((ptr2 = strchr(ptr1, '"')) == NULL)
{
printf("%s: process_input: strchr(2) failed (\")<br>\n", program_name);
exit(EXIT_FAILURE);
}
// Terminate and store filename.
ptr2[0] = '\0';
strcpy(filename, ptr1);
ptr2[0] = '"';
// Remove spaces from filename.
while(filename[x] != '\0')
{
if(filename[x] == ' ')
filename[x] = '.';
x++;
}
// Open output file.
if((fp = fopen(filename, "wb")) == NULL)
{
printf("%s: process_input: fopen failed (%s) (%s)<br>\n", program_name, strerror(errno), filename);
exit(EXIT_FAILURE);
}
// Find start of file data.
if((ptr1 = strstr(data, SEARCH_STRING_2)) == NULL)
{
printf("%s: process_input: strstr(2) failed (%s)<br>\n", program_name, SEARCH_STRING_2);
fclose(fp);
exit(EXIT_FAILURE);
}
// Set flag.
flag++;
// Advance pointer to start of file data.
ptr1 += 4;
// Change STDIN stream to binary.
if(freopen(NULL, "rb", stdin) == NULL)
{
printf("%s: process_input: freopen failed (%s)<br>\n", program_name, strerror(errno));
fclose(fp);
exit(EXIT_FAILURE);
}
}
// Catch everything else.
else
{
ptr1 = data;
if((ptr2 = strstr(ptr1, marker)) != NULL)
ptr2[0 - 2] = '\0';
}
// Write file data.
if(fwrite(ptr1, 1, strlen(ptr1), fp) != strlen(ptr1))
{
printf("%s: process_input: write failed (%s)<br>\n", program_name, strerror(errno));
fclose(fp);
exit(EXIT_FAILURE);
}
}
void print_footer (void)
{
printf("\nMade it!\n");
}
void print_header (void)
{
printf("Content-type: text/plain\r\n\r\n");
}
Ok, it appears what #NominalAnimal said was correct. You can store binary data in a string, but the moment you use any function in the string.h library, it almost always changes what is stored in that string (if the data is binary).
The easy solution is to make a separate function that takes a pointer to the binary data and do your string searches in that function, returning what pertinent information is needed. That way, the original data is never changed.
'stdin' is a macro of STDIN_FILENO, which is egal to 0. See also 'unistd.h'.
You are not showing your code, but I think you stop when you encounter a '\0' or a non-ascii char, since you said you were using 'fread()'.
You have to stop when fread() function returns 0, which means it stopped to read : it encountered EOF.

Binary output in Windows

I wrote a program that reads a binary file, does some process with its contents and writes the results to a different file. In Linux it works perfectly, but in Windows it does not work; the output files are always 1KB...
This is a simplified version of the program:
#include <stdio.h>
void copyFile(char* source, char* dest);
int main (int argc, char* argv[])
{
if (argc != 3)
printf ("usage: %s <source> <destination>", argv[0]);
else
{
copyFile(argv[1], argv[2]);
}
}
void encryptFile(char* source, char* destination)
{
FILE *sourceFile;
FILE *destinationFile;
int fileSize;
sourceFile = fopen(source, "r");
destinationFile = fopen(destination, "w");
if (sourceFile == 0)
{
printf ("Could not open source file\n");
return;
}
if (destinationFile == 0)
{
printf ("Could not open destination file\n");
return;
}
// Get file size
fseek(sourceFile, 0, SEEK_END); // Seek to the end of the file
if (ftell(sourceFile) < 4)
return; // Return if the file is less than 4 bytes
fseek(sourceFile, 0, SEEK_SET); // Seek back to the beginning
fseek(sourceFile, 0, SEEK_SET); // Seek back to the beginning
int currentChar;
while ((currentChar = fgetc(sourceFile)) != EOF)
{
fputc(currentChar, destinationFile);
}
fclose(sourceFile);
fclose(destinationFile);
}
I would love to give you more details of the problem, but I don't have much experience programming C in Windows and I really don't know where may be the problem.
You should use the b flag to fopen:
fopen(source, "rb")
fopen(destination, "wb");
I understand that due to some (brain-damage) subjective decisions, on win32 reaching 0x1A on the input stream triggers an EOF if the file is not opened in "binary mode".
EDIT
In never looked into it but somebody is telling me now that 0x1A was used in DOS as a soft EOF.
Well, you're not opening the files in binary mode (use "wb" and "rb"). This doesn't matter on Linux, but it does on Windows, which will transform certain bytes when reading/writing a file in text mode. For example:
\r\n <--> \n
\x1a (Ctrl-Z) is treated as an EOF indicator
You need to use "rb" and "wb" with fopen.

Resources