How to change STDIN stream to binary - c

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.

Related

Segfault in getline

The following code is supposed to read the file "rules.txt" and write it to a device line by line.
The flow should be:
Read line from rules.txt
Echo it to device
The following code always end in segfault because of readline and I have no idea why:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>
#define BUFFER_LENGTH 256
int main()
{
char *line;
size_t len = BUFFER_LENGTH;
int fd = open("./rules.txt", O_RDONLY);
if(fd == -1)
{ perror("open failed"); return 0; }
FILE* fout = fopen("/sys/class/Rule_Table_Class/Rule_Table_Class_Rule_Table_Device/sysfs_att", "w+");
if(fout == NULL)
{ close(fd); perror("fopen failed, log.txt is busy!"); return 0; }
while (1)
{
line = (char*) malloc(len*sizeof(char));
if(line==NULL){
perror("malloc failed!"); return 0;
}
int bytesRead = getline(&line, &len, fd);
if (bytesRead == -1)
{
perror("Failed to read the message from the device.");
return errno;
}
sprintf(line,"%s","lala");
printf("line = %s", line);
}
fclose(fout);
close(fd);
return 0;
}
EDIT:
I corrected the code and still get a segfault. Here is the corrected code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>
#define BUFFER_LENGTH 256
int main()
{
FILE * fp;
char * line = NULL;
size_t len = 0;
ssize_t read;
fp = fopen("./rules.txt", "r");
if(fp == NULL)
{ perror("open failed"); return 0; }
FILE* fout = fopen("/sys/class/Rule_Table_Class/Rule_Table_Class_Rule_Table_Device/sysfs_att", "w+");
if(fout == NULL)
{ perror("fopen failed!"); return 0; }
while (1)
{
ssize_t bytesRead = getline(&line, &len, fp);
if (bytesRead == -1)
{
return 0;
}
printf("line = %s", line);
fprintf(line,"%s",fout);
}
fclose(fout);
fclose(fp);
return 0;
}
First of all, the proper core getline() loop is
/* FILE *in = fopen(..., "r"); */
char *line = NULL;
size_t size = 0;
while (1) {
ssize_t len = getline(&line, &size, in);
if (len < 0)
break;
/* You have 'len' chars at 'line', with line[len] == '\0'. */
}
so, it is not getline() that causes the segfault, it is your fprintf(line, "%s", fout); that should be either fprintf(fout, "%s", line); or just fputs(line, fout);, or fwrite(line, 1, bytesRead, fout); since the line can contain embedded NUL bytes that fprintf() and fputs() consider an end of string mark.
If we modify the code so that the source and target file names are taken as command-line arguments (with - denoting standard input or standard output), this is what I'd personally like to see:
/* SPDX-License-Identifier: CC0-1.0 */
/* This tells the GNU C library to expose POSIX features, including getline(). */
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
int main(int argc, char *argv[])
{
if (argc != 3 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
const char *self = (argc > 0 && argv && argv[0] && argv[0][0]) ? argv[0] : "(this)";
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", self);
fprintf(stderr, " %s SOURCE TARGET\n", self);
fprintf(stderr, "\n");
fprintf(stderr, "This copies all content from SOURCE to TARGET, line by line.\n");
fprintf(stderr, "Use '-' for standard input source or standard output target.\n");
fprintf(stderr, "\n");
return EXIT_SUCCESS;
}
const char *srcpath = argv[1];
const char *dstpath = argv[2];
/* If the path is "-", set it to NULL. */
if (srcpath && !strcmp(srcpath, "-"))
srcpath = NULL;
if (dstpath && !strcmp(dstpath, "-"))
dstpath = NULL;
FILE *src, *dst;
/* Open source for reading. If srcpath is NULL, use stdin. */
if (srcpath) {
src = fopen(srcpath, "r");
if (!src) {
fprintf(stderr, "%s: %s.\n", srcpath, strerror(errno));
return EXIT_FAILURE;
}
} else {
src = stdin;
}
/* Open target for writing. If dstpath is NULL, use stdout. */
if (dstpath) {
dst = fopen(dstpath, "w");
if (!dst) {
fprintf(stderr, "%s: %s.\n", dstpath, strerror(errno));
fclose(src);
return EXIT_FAILURE;
}
} else {
dst = stdout;
}
char *line = NULL;
size_t size = 0;
unsigned long linenum = 0;
while (1) {
ssize_t len = getline(&line, &size, src);
if (len < 0)
break;
linenum++;
if (fwrite(line, 1, len, dst) != (size_t)len) {
fprintf(stderr, "%s: Write error on line %lu.\n", dstpath ? dstpath : "(standard output)", linenum);
fclose(dst);
fclose(src);
return EXIT_FAILURE;
}
}
/* Technically, we don't need to release dynamically allocated (non-shared) memory,
because we're just about to exit, and the OS will automagically do that for us.
We can do this at any point we want during the loop, too. */
free(line);
line = NULL;
size = 0;
/* We do not know why getline() returned -1. Check if an error occurred, and whether we're at end of input. */
if (ferror(src) || !feof(src)) {
fprintf(stderr, "%s: Read error on line %lu.\n", srcpath ? srcpath : "(standard input)", linenum);
fclose(dst);
fclose(src);
return EXIT_FAILURE;
}
/* Check if any write errors have occurred. */
if (ferror(dst)) {
fprintf(stderr, "%s: Write error on line %lu.\n", dstpath ? dstpath : "(standard output)", linenum);
fclose(dst);
fclose(src);
}
/* Read errors should not occur at close time, but it costs very little for us to test anyway. */
if (fclose(src)) {
fprintf(stderr, "%s: Read error on line %lu.\n", srcpath ? srcpath : "(standard input)", linenum);
fclose(dst);
return EXIT_FAILURE;
}
/* Write errors can occur at close time, if the output has been buffered, or the target is on
a remote filesystem. Again, it costs us very little to check. */
if (fclose(dst)) {
fprintf(stderr, "%s: Write error on line %lu.\n", dstpath ? dstpath : "(standard output)", linenum);
return EXIT_FAILURE;
}
/* No errors; target is an identical copy of the source file. */
fprintf(stderr, "%lu lines copied successfully.\n", linenum);
return EXIT_SUCCESS;
}
The SPDX-License-Identifier is a common way to indicate the license of the code. I use CC0-1.0, which basically means "use as you wish, just don't blame the author for any issues: no guarantees, no warranties."
The #define _POSIX_C_SOURCE 200809L tells the GNU C library that we want it to expose POSIX.1-2008 features, which include getline(). If you look at man 3 getline, you'll see in the Synopsis section that glibc 2.10 and later require _POSIX_C_SOURCE to be defined to at least 200809L.
Most of the code is to print the usage, and getting the source and target file names from the command line, and handling - as a special name, "standard stream", so that it can be used even in pipes by specifying - as the input and/or output file name.
The getline() loop uses fwrite() to write the line to the target file. This way, if the input contains embedded NUL bytes (\0), the target will still be identical to the source file.
After the loop, we discard the line buffer, although since the program is just about to exit, we could omit that (since the OS will release all dynamically allocated (non-shared) memory when we exit anyway).
I like code that checks with ferror(src) || !feof(src) if an error occurred in src or src did not reach end of input; and that checks the return value of fclose(), in case a delayed (write) error is reported. Granted, fclose() should never fail for a read-only file, and fclose() should only fail for files we've written to in specific circumstances, but it costs very little to check, and this way the user running the program will be told if the program detected loss of data.
I believe it is morally reprehensible to ignore checking for such errors (especially "because they occur so rarely"), since such tests are the only way the human user can know whether the operation was successful, or if some odd problem occurred. It is up to the human to investigate any problems, and up to our programs to report detectable issues.
The getline function takes a FILE * as a parameter, not an int. Replace the following line:
int fd = open("./rules.txt", O_RDONLY);
By;
FILE *fin = fopen("./rules.txt", "r");
Fix the error checking in the following lines accordingly, like you did with fout.
Then replace the line:
int bytesRead = getline(&line, &len, fd);
Instead it should now use fin:
ssize_t bytesRead = getline(&line, &len, fin);
Note that getline returns ssize_t, not int.
You are also never writing to fout, but I guess you're still working on this code.
Make sure to enable compiler warnings, because your compiler would surely have warned you for using an int parameter where a FILE * was expected.

Copy data from file X to file Y program in C

I tried to write basic program in C which copy data from file to another with given source path, destination path and buffer size as input.
my problem is the destination file filled with junk or something because its way larger than the source (get bigger depending on buffer size) and can't be open.
How do i read and write just the bytes in the source?
i'm working in linux, and this is the actually copying part:
char buffer[buffer_size];
int readable=1;
int writeable;
while(readable != 0){
readable = read(sourcef, buffer, buffer_size);
if(readable == -1){
close(sourcef);
close(destf);
exit_with_usage("Could not read.");
}
writeable = write(destf, buffer, buffer_size);
if(writeable == -1){
close(sourcef);
close(destf);
exit_with_usage("Could not write.");
}
}
writeable = write(destf, buffer, buffer_size);
must be
writeable = write(destf, buffer, readable);
Currently you do not write the number of characters you read but all the buffer, so the output file is too large
You also manage wrongly the end of the input file
The return value of read is :
On success, the number of bytes read is returned (zero indicates end of file)
On error, -1 is returned
A proposal :
/* you already check input and output file was open with success */
char buffer[buffer_size];
for(;;){
ssize_t readable = read(sourcef, buffer, buffer_size);
if(readable <= 0){
close(sourcef);
close(destf);
if (readable != 0)
/* not EOF */
exit_with_usage("Could not read.");
/* EOF */
break;
}
if (write(destf, buffer, n) != n) {
close(sourcef);
close(destf);
exit_with_usage("Could not write.");
}
}
I suppose exit_with_usage calls exit() so does not return
Note in theory write may write less than the expected number of characters without being an error, and the write has to be done in a loop, but in that case it is useless to manage that
read function returns how many bytes were read to buffer(which has buffer_size). Its not always the case actual bytes read has same value as buffer size(consider scenario if there are not enough bytes left in source file to fully fill your buffer). So you should write to destination file not buffer_size(third argument of the write function), but how many bytes have you read - that is readable variable in your code
You should exit when readable returns an error.So
while(readable != 0){
should be
while(readable != -1){
So that loop could be terminataed when an readfile is exhausted.
You see currently after the whole readfile has been read, calling read fails but write is being called repeatedly since execution has no exit path for failure on read. Also write should only write the number of bytes read. So the code would look like this:
char buffer[buffer_size];
int readable=1;
int writeable;
while(readable != -1){
readable = read(sourcef, buffer, buffer_size);
if(readable == -1){
close(sourcef);
close(destf);
exit_with_usage("Could not read.");
}
writeable = write(destf, buffer, readable);
if(writeable == -1){
close(sourcef);
close(destf);
exit_with_usage("Could not write.");
}
}
Simple code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h> // For system calls write, read e close
#include <fcntl.h>
#define BUFFER_SIZE 4096
int main(int argc, char* argv[]) {
if (argc != 3) {
printf("Usage %s Src_file Dest_file\n", argv[0]);
exit(1);
}
unsigned char buffer[BUFFER_SIZE] = {0};
ssize_t ReadByte = 0;
int src_fd, dst_fd;
// open file in read mode
if ((src_fd = open(argv[1], O_RDONLY)) == -1) {
printf("Failed to open input file %s\n", argv[1]);
exit(1);
}
// open file in write mode and already exists to overwrite
if ((dst_fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 644)) == -1) {
printf("Failed to create output file %s\n", argv[2]);
exit(1);
}
// loop
while (1) {
// read buffer
ReadByte = read(src_fd, buffer, sizeof(buffer));
// error with reading
if (ReadByte == -1) {
printf("Encountered an error\n");
break;
} else if (ReadByte == 0) {
// file end exit loop
printf("File copying successful.\n");
break;
}
// error with writing
if (write(dst_fd, buffer, ReadByte) == -1) {
printf("Failed to copying file\n");
break;
}
}
// Close file
close(src_fd);
close(dst_fd);
exit(0);
}
Run
./program src_file dest_file

Problems with C XOR executable file encryption / decryption

I'm trying to create a simple XOR crypter / decrypter in C for .exe files. I'm still pretty new in C and don't understand everything yet, especially memory stuff. So I've been following an online tutorial on how to make a simple XOR string crypter which worked fine. Now I wanted to modify it so I can en/decrypt executable files and decided to utilize the fwrite() and fread() functions. This is what I've come up with:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> // execve function
#define XOR_KEY 0xAA // key
#define JOB_CRYPT 1 // alter flow depending on the job
#define JOB_DECRYPT 2
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
void xorFile (char *infile, char *outfile) {
FILE *nFile, *eFile;
long nFileSize; // store file size of the file we want to read
char *buffer; // buffer for reading
char *eBuffer; // buffer for storing encrypted data
size_t rResult;
size_t wResult;
///// READ FILE /////
nFile = fopen(infile, "rb");
if(nFile == NULL) {
fputs("Error opening file...", stderr);
exit(EXIT_FAILURE);
}
fseek(nFile, 0, SEEK_END);
nFileSize = ftell(nFile);
rewind(nFile);
buffer = (char *) malloc(sizeof(char) * nFileSize);
if(buffer == NULL) {
fputs("Error allocating memory...", stderr);
exit(EXIT_FAILURE);
}
rResult = fread(buffer, 1, nFileSize, nFile);
if(rResult != nFileSize) {
fputs("Error reading file...", stderr);
exit(EXIT_FAILURE);
}
fclose(nFile);
printf("File size is: %ld\n", nFileSize);
printf("Buffer size is (pointer): %u\n", sizeof(buffer));
printf("Reading result: %lu\n", rResult);
////// WRITE TO FILE //////
eFile = fopen(outfile, "wb");
if(eFile == NULL) {
fputs("Error creating file...", stderr);
exit(EXIT_FAILURE);
}
eBuffer = (char *) malloc(sizeof(char) * nFileSize);
if(eBuffer == NULL) {
fputs("Error allocating memory (2)...", stderr);
exit(EXIT_FAILURE);
}
// encrypt byte by byte and save to buffer
printf("Proceeding with encryption!\n");
for(int i = 0; buffer[i] != EOF; i++) {
eBuffer[i] = buffer[i] ^ XOR_KEY;
}
printf("Proceeding with fwrite()!\n");
wResult = fwrite(eBuffer, 1, nFileSize, eFile);
fclose(eFile);
printf("eBuffer size is (pointer)%u\n", sizeof(eBuffer));
printf("Writing result: %lu\n", wResult);
free(buffer); // free buffers in heap
free(eBuffer);
}
//////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////
int main(int argc, char *argv[]) {
// checking if all parameters were given
if(argc < 4) {
fprintf(stderr, "Usage: %s [CRYPT | DECRYPT] [IN-FILE] [OUT-FILE]\n", argv[0]);
exit(EXIT_FAILURE);
}
int job;
// DOLOCIMO JOB
if(strcmp(argv[1], "CRYPT") == 0) {
job = JOB_CRYPT;
} else if (strcmp(argv[1], "DECRYPT") == 0) {
job = JOB_DECRYPT;
} else {
fprintf(stderr, "Please select [CRYPT | DECRYPT]!");
exit(EXIT_FAILURE);
}
// CRYPT/DECRYPT OUR FILE
xorFile(argv[2], argv[3]);
if(job == JOB_DECRYPT) {
char *args[] = {argv[3], NULL};
int errExec = execve(args[0], args, NULL);
if(errExec == -1) {
perror("Error executing file...");
exit(EXIT_FAILURE);
}
}
return 0;
}
I'm sorry for the ugly looking code but I first wanted to make it work and I'll refine it later.
Anyways, when I run it in command prompt, the encryption works fine, it generates an encrypted file, but when I run the decrpytion job, the program
crashes during the decryption process. Here's a picture of what happens so you can imagine it better.
Since I have less than 10 reputation, I'm not allowed to embedd pictures.
Here is a link to Imgur.
What's going wrong here? Am I creating a buffer overflow when I'm decrypting it?
Thank you!
Here's the problem:
for(int i = 0; buffer[i] != EOF; i++) {
eBuffer[i] = buffer[i] ^ XOR_KEY;
}
Binary files can contain bytes with any value. So the EOF value is valid and does not designate the end of the file. This means that if the file contains a byte with this value, the loop will quit early and you won't XOR all the bytes. If the file does not contain this byte, the loop will run past the end of the allocated memory which invokes undefined behavior which in this case manifests in a crash.
You know how many bytes you need to processes, so use that as your loop control:
for(int i = 0; i < nFileSize; i++) {
A few other minor corrections:
buffer = (char *) malloc(sizeof(char) * nFileSize);
if(buffer == NULL) {
fputs("Error allocating memory...", stderr);
exit(EXIT_FAILURE);
}
Don't cast the return value of malloc. Also, sizeof(char) is 1 by definition, so you can leave that out.
Also, if a system or library function fails, you should use perror to print the error message. This will print additional information regarding why the function failed.
buffer = malloc(nFileSize);
if(buffer == NULL) {
perror("Error allocating memory...");
exit(EXIT_FAILURE);
}

I am trying to print a txt file and it doesn't work in C homework

I'm writing code that's supposed to verify that a .txt file is a certain format.
I wrote my code as I saw in a tutorial and in the website
and for some reason my program doesn't even print my file.
Can you tell me what I'm doing wrong?
The code will do something far more complex, but I'm still trying to work on my basics.
Here's my code so far:
int main(int argc, char *argv[]) {
/* argv[0] = name of my running file
* argv[1] = the first file that i receive
*/
define MAXBUFLEN 4096
char source[MAXBUFLEN + 1];
int badReturnValue = 1;
char *error = "Error! trying to open the file ";
if (argc != 2) {
printf("please supply a file \n");
return badReturnValue;
}
char *fileName = argv[1];
FILE *fp = fopen(argv[1], "r"); /* "r" = open for reading */
if (fp != NULL) {
size_t newLen = fread(&source, sizeof(char), MAXBUFLEN, fp);
if (ferror(fp) != 0) {
printf("%s %s", error, fileName);
return badReturnValue;
}
int symbol;
while ((symbol = getc(fp)) != EOF) {
putchar(symbol);
}
printf("finish");
fclose(fp);
}
else {
printf("%s %s", error, fileName);
return badReturnValue;
}
}
I think you need a bit more explanations:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
// there might be a macro BUFLEN defined in stdio
// which size is optimized for reading in chunks.
// Test if avaiable otherwise define it
#ifndef BUFLEN
# define BUFLEN 4096
#endif
int main(int argc, char *argv[])
{
char source[BUFLEN];
char *filename;
FILE *fp;
size_t fpread, written;
char c;
int ret_fclose;
if (argc != 2) {
fprintf(stderr, "Usage: %s filename\n", argv[0]);
exit(EXIT_FAILURE);
}
// reset errno, just in case
errno = 0;
// work on copy
filename = malloc(strlen(argv[1]) + 1);
if (filename == NULL) {
fprintf(stderr, "Allocating %zu bytes failed\n", strlen(argv[1]) + 1);
exit(EXIT_FAILURE);
}
filename = strcpy(filename, argv[1]);
// try to open the file at 'filename'
fp = fopen(filename, "r");
if (fp == NULL) {
fprintf(stderr, "Opening file \"%s\" filename failed\n", filename);
// errno might got set to something usable, check and print
if (errno != 0) {
fprintf(stderr, "Error: %s\n", strerror(errno));
}
exit(EXIT_FAILURE);
}
// You have two options here. One is to read in chunks of MAXBUFLEN
while ((fpread = fread(&source, 1, BUFLEN, fp)) > 0) {
// Do something with the stuff we read into "source"
// we do nothing with it here, we just write to stdout
written = fwrite(&source, 1, fpread, stdout);
// you can use 'written' for error check when writing to an actual file
// but it is unlikely (but not impossible!) with stdout
// test if we wrote what we read
if ((fpread - written) != 0) {
fprintf(stderr, "We did not write what we read. Diff: %d\n",
(int) (fpread - written));
}
}
// fread() does not distinguish between EOF and error, we have to check by hand
if (feof(fp)) {
// we have read all, exit
puts("\n\n\tfinish\n");
// No, wait, we want to do it again in a different way, so: no exit
// exit(EXIT_SUCCESS);
} else {
// some error may have occured, check
if (ferror(fp)) {
fprintf(stderr, "Something bad happend while reading \"%s\"\n", filename);
if (errno != 0) {
fprintf(stderr, "Error: %s\n", strerror(errno));
}
exit(EXIT_FAILURE);
}
}
// the other way is to read it byte by byte
// reset the filepointers/errors et al.
rewind(fp);
// rewind() should have reseted errno, but better be safe than sorry
errno = 0;
printf("\n\n\tread and print \"%s\" again\n\n\n\n", filename);
// read one byte and print it until end of file
while ((c = fgetc(fp)) != EOF) {
// just print. Gathering them into "source" is left as an exercise
fputc(c, stdout);
}
// clean up
errno = 0;
ret_fclose = fclose(fp);
// even fclose() might fail
if (ret_fclose == EOF) {
fprintf(stderr, "Something bad happend while closing \"%s\"\n", filename);
if (errno != 0) {
fprintf(stderr, "Error: %s\n", strerror(errno));
}
exit(EXIT_FAILURE);
}
// The macros EXIT_FAILURE and EXIT_SUCCESS are set to the correct values for
// the OS to tell it if we had an eror or not.
// Using exit() is noot necessary here but there exits teh function atexit()
// that runs a given function (e.g: clean up, safe content etc.) when called
exit(EXIT_SUCCESS);
}
You read from the file twice but only print once.
If the file is to small the first reading will read all of the contents, and the second reading will not produce anything so you don't print anything.
I believe you have to reset the pointer after using fread.
Try fseek(fp, SEEK_SET, 0) to reset the pointer to the beginning of the file. Then print the file.

C: reading a file line by line

Input files have one entry per line and be of the form T S where T is the arrival time and S is the sector to be read.
I have a file.txt where it looks like
1 6
2 7
3 8
Would indicate three disk accesses for sectors 6, 7, and 8 arriving at time 1 2 and 3 respectively.
how do i parse it so that the first number goes to T and the second goes to S?
There are many ways to accomplish this task.
The scanf() (or perhaps fscanf()) method mentioned by pmg is perhaps most commonly taught to new programmers by academia.
The readLine() method, referred to by doniyor is an alternate approach which reads one character at a time to assemble a complete line, which then can be parsed as a string.
You might even try using fgets() to read an entire line, and then parse the numbers out of the line buffer.
The above list of methods is by no means complete. There are many other ways to accomplish the task.
Of course, you have probably have a full working understanding of these common methods, and you are looking for something with some zing! Perhaps the following will help you on your way:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main(
int I__argC,
char *I__argV[]
)
{
int rCode=0;
struct stat statBuf;
FILE *fp = NULL;
char *fileBuf = NULL;
size_t fileBufLength;
char *cp;
/* Verify that caller has supplied I__argV[1], which should be the path
* to the datafile.
*/
if(I__argC != 2)
{
rCode=EINVAL;
fprintf(stderr, "Usage: %s {data file path}\n", I__argV[0]);
goto CLEANUP;
}
/* Get the size of the file (in bytes);
* (assuming that we are not dealing with a sparse file, etc...)
*/
errno=0;
if((-1) == stat(I__argV[1], &statBuf))
{
rCode=errno;
fprintf(stderr, "stat(\"%s\", ...) failed. errno[%d]\n", I__argV[1], errno);
goto CLEANUP;
}
/* Open the caller-specified file in read mode. */
errno = 0;
fp=fopen(I__argV[1], "r");
if(NULL == fp)
{
rCode=errno;
fprintf(stderr, "fopen(\"%s\", \"r\") failed. errno[%d]\n", I__argV[1], errno);
goto CLEANUP;
}
/* Allocate a buffer large enough to hold the entire file content. */
fileBuf=malloc(statBuf.st_size);
if(NULL == fileBuf)
{
rCode=ENOMEM;
fprintf(stderr, "malloc(%zd) failed.\n", statBuf.st_size);
goto CLEANUP;
}
/* Read the file into the fileBuf. */
errno=0;
fileBufLength=fread(fileBuf, 1, statBuf.st_size, fp);
if(fileBufLength != statBuf.st_size)
{
rCode=errno;
fprintf(stderr, "fread() failed to read %zd file bytes. errno[%d]\n",
statBuf.st_size - fileBufLength, errno);
goto CLEANUP;
}
/* Parse the fileBuf for specific data. */
for(cp=fileBuf; cp < fileBuf + fileBufLength; ++cp)
{
long arrivalTime;
long sector;
/* Skip leading white-space (if any) */
if(isspace(*cp))
continue;
/* Parse the 'arrival time'. */
arrivalTime=strtol(cp, &cp, 10);
/* Skip leading white-space (if any) */
while(isspace(*cp))
++cp;
/* Parse the 'sector'. */
sector=strtol(cp, &cp, 10);
printf("%ld, %ld\n", arrivalTime, sector);
}
CLEANUP:
/* Free the fileBuf (if it was successfully allocated) */
if(fileBuf)
free(fileBuf);
/* Close the file (if it was successfully opened) */
if(fp)
fclose(fp);
return(rCode);
}

Resources