read system call doesn't detect end of file - c

I'm trying to create a function that reads an entire file using a specific read size that can change anytime, but the read system call doesn't store the characters properly in the buffer, so far I'm only trying to print until the end of file like this:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
# define READ_SIZE (42)
int main(int argc, char **argv)
{
int fd;
int rd;
char *buffer;
buffer = malloc(READ_SIZE);
fd = open(argv[1], O_RDONLY);
while ((rd = read(fd, buffer, READ_SIZE)) > 0)
{
printf("%s", buffer);
}
return (0);
}
This is the file that I'm trying to read:
test1234
test123
test1
test2
test3
test4
test
This is the output of my program:
test123
test12
test1
test2
test3
test4
testest123
test12
test1
test2
test3
test4
tes
I can only use malloc, and read to handle this, open is only for testing, and I don't understand why it does this, usually read returns the number of bytes read in that file, and 0 if it reaches the end of file, so it's a bit weird to see this.

The printing of the character array lacks a null character. This is UB with "%s".
printf("%s", buffer); // bad
To limit printing a character array lacking a null character, use a precision modifier. This will print the character array up to that many characters or a null character - which ever is first.
// printf("%s", buffer);
printf("%.*s", rd, buffer);
Debug tip: Print text with sentinels to clearly indicate the result of each print.
printf("<%.*s>\n", rd, buffer);

Besides the very elegant solution provided by chux's answer you could as well just terminate the buffer (and with this only make it a C-"string") explicitly before printing:
while ((rd = read(fd, buffer, READ_SIZE-1)) > 0) /* read one less, to have a spare
char available for the `0`-terminator. */
{
buffer[rd] = '\0';
printf("'%s'", buffer);
}

Related

C reading formatted text from file

i have a question on a C program that I'm doing. The beginning of the track ask this:
"Process P ask as argument the path of a file in which every line ust be 16 characters length (included the end of line), and every line must start with "WAIT" or "NOWAIT" followed by a command."
The example of input file is:
WAIT ls
NOWAIT who
WAIT date
I made this code for now:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define MIN_SIZE 5
#define ROW_LEN 17
int main (int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Program usage: %s file_path.\n", argv[0]);
exit(1);
}
int fd = open(argv[1], O_RDONLY);
struct stat fd_info;
if(fd < 0) {
perror("Error opening file");
exit(2);
}
fstat(fd, &fd_info);
if(fd_info.st_size <= MIN_SIZE) {
printf("Size of file '%s' is less or equal than 5 bytes.\n", argv[1]);
exit(3);
}
char buf[ROW_LEN];
buf[ROW_LEN - 1] = '\0';
while ((read(fd, buf, ROW_LEN - 1)) > 0) {
char type[ROW_LEN], cmd[ROW_LEN];
sscanf(buf, "%s %s", type, cmd);
printf("type=%s; command=%s;\n", type, cmd);
}
return 0;
}
In this way i can read good only if in the file.txt I complete every row with spaces until it reaches 15 characters for each line (else it start reading next line too). But in the file that prof gave us there aren't spaces to complete the row. So my question is, how can I read correctly from the file? I can't understand that "every line must have 16 characters included end of line".
Thanks to all, I hope I explained good the question!
Firstly with this sentence you must considerate each line as a possible input, but the input is coming from anyone so anything can append and any errors in consequences.
You start on the good way
you must consider all your file and after your line > check if your line is good
you can use getline to get your file easily, and strlen and strcmp to check if your line is conform.
Finaly the part "include end of line", that mean that all the line must have a length of 16 character with '\0', so in your file the "visible" length must be at 15 for the maximum,
for example if the maximal length is 3 included end of line :
"abc" : incorrect because it's equal to {'a', 'b', 'c' '\0'};
"ab" : correct because it's equal to {'a', 'b', '\0'};

how to compare number of rows in redirected text file C

#include <stdio.h>
#include <stdlib.h>
#define BUFFERSIZE 10
int main(int argc, char *argv[])
{
char address[BUFFERSIZE];
//checking text file on stdin which does not work
if (fgets(address, BUFFERSIZE, stdin) < 42)
{
fprintf(stderr, "The program needs at least 42 addresses for proper functionality.");
}
//while reads the redirected file line by line and print the content line by line
while(fgets(address, BUFFERSIZE, stdin) != NULL)
{
printf("%s", address);
}
return 0;
}
Hi, this is my code. Does not work. The problem is that I have a redirected external file adresy.txt into stdin and I need to check if the file has the required number of rows.
The minimum number of rows that a file must have is 42. If it has 42 or more rows the program can continue, if not, it throws out the fprintf(stderr, "The program needs at least 42 addresses for proper functionality.");
I tried it this way if (fgets(address, BUFFERSIZE, stdin) < 42) but it still tells me that I can not compare pointer and integer
like so: warning: comparison between pointer and integer
In the code extension I will compare the arguments from the user to what is in adresy.txt therefore I need argc and *argv [] but now i need to solve this.
Any advice how to fix it? Thanks for any help.
There are several problems in your code:
#define BUFFERSIZE 10 is odd as your lines but be at least 42 long.
you compare the pointer returned by fgets to 42 which is nonsense, BTW your compiler warned you.
With your method you actually display only one line out of two
You probably want this:
#define BUFFERSIZE 200 // maximum length of one line
int main(int argc, char *argv[])
{
char address[BUFFERSIZE];
while(fgets(address, BUFFERSIZE, stdin) != NULL)
{
// here the line has been read
if (strlen(address) < 42)
{
// if the length of the string read is < 42, inform user and stop
fprintf(stderr, "The program needs at least 42 addresses for proper functionality.");
exit(1);
}
// otherwise print line
printf("%s", address);
}
return 0;
}

Process returned -1 (0xFFFFFFFF)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main() {
printf("Transactional Shell Command Test.\n");
while(1) {
printf("Queue:");
char input[500];
fgets (input, 500, stdin);
if(strstr(input, "qb-write")){
printf("These are the commands you have queued:\n");
FILE *cmd = popen("cat /home/$USER/.queueBASH_transactions", "r");
char buf[256];
while (fgets(buf, sizeof(buf), cmd) != 0) {
printf("%s\n",buf);
}
pclose(cmd);
}
system(strncat("echo ",strncat(input," >> /home/$USER/.qb_transactions",500),500));
usleep(20000);
}
return 0;
}
I am attempting to make a concept for a transactional shell, and I'm having it output every command you enter into a file in the user's home directory. It's not completely finished, but I'm doing one part at a time. When I put in any input to the "shell", it crashes. Codeblocks tells me "Process returned -1 (0xFFFFFFFF)" and then the usual info about runtime. What am I doing wrong here?
strncat appends to its first argument in place, so you need to pass it a writable buffer as the first argument. You're passing a string literal ("echo "), which depending on your compiler and runtime environment may either overwrite unpredictable parts of the memory, or crash because it's trying to write to read-only memory.
char command[500];
strcpy(command, "echo ");
strncat(command, input, sizeof(command)-1-strlen(command));
strncat(command, " >> /home/$USER/.qb_transactions", sizeof(command)-1-strlen(command));
system(command);
As with the rest of your code, I've omitted error checking, so the command will be truncated if it doesn't fit the buffer. Also note that repeated calls to strncat are inefficient since they involve traversing the string many times to determine its end; it would be more efficient to use the return value and keep track of the remaining buffer size, but I'm leaving this as a follow-up exercise.
Of course invoking a shell to append to a file is a bad idea in the first place. If the input contains shell special characters, they'll be evaluated. You should open the log file and write to it directly.
char log_file[PATH_MAX];
strcpy(log_file, getenv("HOME"));
strncat(log_file, "/.qb_transactions", PATH_MAX-1-strlen(log_file));
FILE *log_file = fopen(log_file, "a");
…
while (1) {
…
fputs(cmd, log_file);
}
fclose(log_file);
(Once again, error checking omitted.)

Reading files to shared memory

I am reading a binary file that I want to offload directly to the Xeon Phi through Cilk and shared memory.
As we are reading fairly much data at once each time and binary data the preferred option is to use fread.
So if I make a very simple example it would go like this
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
_Cilk_shared uint8_t* _Cilk_shared buf;
int main(int argc, char **argv) {
printf("Argv is %s\n", argv[1]);
FILE* infile = fopen(argv[1], "rb");
buf = (_Cilk_shared uint8_t*) _Offload_shared_malloc(2073600);
int len = fread(buf, 1, 2073600, infile);
if(ferror(infile)) {
perror("ferror");
}
printf("Len is %d and first value of buf is %d\n", len, *buf);
return 0;
}
The example is very simplified from the real code but enough to examplify the behavior.
This code would then return
ferror: Bad address
Len is 0 and first value of buf is 0
However if we switch out the fread for a fgets (not very suitable for reading binary data, specially with the return value) things work great.
That is we switch fgets((char *) buf, 2073600, infile); and then drop the len from the print out we get
first value of buf is 46
Which fits with what we need and I can run _Offload_cilk on a function with buf as an argument and do work on it.
Is there something I am missing or is fread just not supported? I've tried to find as much info on this from both intel and other sites on the internet but I have sadly been unable to.
----EDIT----
After more research into this it seems that running fread on the shared memory with a value higher than 524287 (524287 is 19 bits exactly) fread gets the error from above. At 524287 or lower things work, and you can run as many fread as you want and read all the data.
I am utterly unable to find any reason written anywhere for this.
I don't have a PHI, so unable to see if this would make a difference -- but fread has it's own buffering, and while that may be turned of for this type of readind, then I don't see why you would go through the overhead of using fread rather than just using the lower level calls of open&read, like
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdint.h>
_Cilk_shared uint8_t* _Cilk_shared buf;
int main(int argc, char **argv) {
printf("Argv is %s\n", argv[1]);
int infile = open(argv[1], O_RDONLY); // should test if open ok, but skip to make code similar to OP's
int len, pos =0, size = 2073600;
buf = (_Cilk_shared uint8_t*) _Offload_shared_malloc(size);
do {
buf[pos]=0; // force the address to be mapped to process memory before read
len = read(infile, &buf[pos], size);
if(len < 0) {
perror("error");
break;
}
pos += len; // move position forward in cases where we have no read the entire data in first read.
size -= len;
} while (size > 0);
printf("Len is %d (%d) and first value of buf is %d\n", len, pos, *buf);
return 0;
}
read & write should work with shared memory allocated without the problem you are seeing.
Can you try to insert something like this before the fread calls?
memset(buf, 0, 2073600); // after including string.h
This trick worked for me, but I don't know why (lazy allocation?).
FYI, you can also post a MIC question on this forum.

Why system call read() does not work when using user input

It`s a file copying program.
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
int main()
{
int fd1,fd2, ndata;
char data[128];
char *openname[1], *creatname[1];
write(1, "Write open file name\n", 24); //user input
read(0, openname, 30 );
write(1, "Write creat file name\n", 25); //user input
read(0, creatname,30);
if((fd1 = open(openname, 0666))<0)
{
perror("cannot open the file");
exit(1);
}
if((fd2 = creat(creatname,0666))<0)
{
perror("cannot create the file");
exit(1);
}
while((ndata = read(fd1, data, 128))>0)
{
if((write(fd2, data, ndata))!=ndata)
{
perror("cannot write the file");
exit(1);
}
}
close(fd1);
close(fd2);
write(1, "File copy is done.",19);
return 0;
}
This code ain`t work. This code print the error message:
cannot open the file.
but if i change the code to this :
if((fd1 = open("copy.c", 0666))<0)
and this :
if((fd2 = creat("real.c",0666))<0)
worked well.
Why this error happend? Please answer.
Your declarations of openname and creatname are incorrect. They should be:
char openname[31], creatname[31];
read() does not add a null terminator to the input, you need to add it. read() returns the number of bytes read. So it should be:
int nread = read(0, openname, sizeof openname -1);
openname[nread-1] = '\0'; // subtract 1 to overwrite the newline
The type of openname and creatname is wrong, and gcc -Wall -g would have warned you. Declare e.g. char openname[256];
And you should use fgets(openname, sizeof(openname), stdin); to read it.
If you insist on using read, take care of the newline (if any) and add a zero terminating byte.
Learn also to use the gdb debugger.
read is very low level. In this case, it reads 30 bytes, including your enter key and also without a terminating null-byte. So the filename won't be what you think you've entered, it will contain additional garbage (and could even make your program crash due to the missing null-termination). You want to use fgets or readline instead.
In a nutshell, by using read() to input the file names, you are making this unnecessarily hard for yourself: it does not terminate the input with NUL, is not guaranteed to read the number of characters you expect, etc.
My advice would be to stick with scanf() or fgets().

Resources