C Language: popen() with fread()? - c

I've been stuck on this for a few days and it's getting really frustrating.
I'm using popen() to call a command line process and get its output and store it in a C string. I was using fgets() but it seems that breaks after a new line, so I'm using fread(). The only problem is that the returned C string is sometimes messed up.
Here's my code:
const char *cmd = "date";//This the shell command
char buf[BUFSIZ];//Output of the command
FILE *ptr;
int c;
if ((ptr = popen(cmd, "r")) != NULL)
while(fread(buf, sizeof(buf),1, ptr))
while ((c = getchar()) != EOF)
printf("output = %s", buf);
(void) pclose(ptr);
The final C string sometimes has weird characters in it that shouldn't be there, or sometimes no string is even available. Can anybody please help? ):
Edit: Here is what I was doing when using fgets() The Shell command can be anything that outputs text though. Not just "date."
if ((ptr = popen(cmd, "r")) != NULL)while (fgets(buf, BUFSIZ, ptr) != NULL)printf("output = %s", buf);(void) pclose(ptr);

fread doesn't insert a NUL terminator after what it reads. You need to check the return value to know how much it read, and only print that much. If you read with fread, you typically want to write the data with fwrite, something on this order:
long bytes;
while ((bytes=fread(buf, sizeof(buf), 1, ptr))>0)
fwrite(buf, bytes, 1, stdout);

Well, fgets is the right way to do it.
FILE *ptr;
if (NULL == (ptr = popen(cmd, "r"))) {
/* ... */
}
while(fgets(buf, sizeof(buf), ptr) != NULL) {
/* There is stuff in 'buf' */
}
I think the reason fgets wasn't working for you is that you were doing something wrong.
Now, here's why I think you are running into trouble with your current code:
You are not checking how much fread actually returned
You are reading with getchar and discarding stuff
You don't have a NUL terminator in the buffer
Get this right and it will all be better: fread might legally read less than you told it to.

The output from date doesn't include the '\0' (NUL) character you need to properly terminate the string. Keep track of the number of characters read and put in the NUL yourself.
Though really, you should be using fgets, getline or similar text-oriented functions to read from a program such as date. getline is especially easy (and safe since it does some memory management for you):
FILE *fp = popen("date", "r");
char *ln = NULL;
size_t len = 0;
while (getline(&ln, &len, fp) != -1)
fputs(ln, stdout);
free(ln);
pclose(fp);

Below is the correct way to use fread for process output with popen:
const char *cmd = "date";
char buf[BUFSIZ];
FILE *ptr;
if ((ptr = popen(cmd, "r")) != NULL) {
/* Read one byte at a time, up to BUFSIZ - 1 bytes, the last byte will be used for null termination. */
size_t byte_count = fread(buf, 1, BUFSIZ - 1, ptr);
/* Apply null termination so that the read bytes can be treated as a string. */
buf[byte_count] = 0;
printf("%s\n", buf);
}
(void) pclose(ptr);
As you can see, the primary problem is to correctly deal with null termination. The two size parameter of fread is also important, you have to let it read character by character. Note that in the case of popen, fread will only return 0 if the process has exited without giving any output. It will not return 0 if it takes a long time for the process to print anything.
If the output is larger than BUFSIZ, you can wrap fread with a while loop.

Related

C - Print lines from file with getline()

I am trying to write a simple C program that loads a text-file, prints the first line to screen, waits for the user to press enter and then prints the next line, and so on.
As only argument it accepts a text-file that is loaded as a stream "database". I use the getline()-function for this, according to this example. It compiles fine, successfully loads the text-file, but the program never enters the while-loop and then exits.
#include <stdio.h>
#include <stdlib.h>
FILE *database = NULL; // input file
int main(int argc, char *argv[])
{
/* assuming the user obeyed syntax and gave input-file as first argument*/
char *input = argv[1];
/* Initializing input/database file */
database = fopen(input, "r");
if(database == NULL)
{
fprintf(stderr, "Something went wrong with reading the database/input file. Does it exist?\n");
exit(EXIT_FAILURE);
}
printf("INFO: database file %s loaded.\n", input);
/* Crucial part printing line after line */
char *line = NULL;
size_t len = 0;
ssize_t read;
while((read = getline(&line, &len, database)) != -1)
{
printf("INFO: Retrieved line of length %zu :\n", read);
printf("%s \n", line);
char confirm; // wait for user keystroke to proceed
scanf("%c", &confirm);
// no need to do anything with "confirm"
}
/* tidy up */
free(line);
fclose(database);
exit(EXIT_SUCCESS);
}
I tried it with fgets() -- I can also post that code --, but same thing there: it never enters the while-loop.
It might be something very obvious; I am new to programming.
I use the gcc-compiler on Kali Linux.
Change your scanf with fgetline using stdin as your file parameter.
You should step through this in a debugger, to make sure your claim that it never enters the while loop is correct.
If it truly never enters the while loop, it is necessarily because getline() has returned -1. Either the file is truly empty, or you have an error reading the file.
man getline says:
On success, getline() and getdelim() return the number of
characters
read, including the delimiter character, but not including the termiā€
nating null byte ('\0'). This value can be used to handle embedded
null bytes in the line read.
Both functions return -1 on failure to read a line (including end-of-
file condition). In the event of an error, errno is set to indicate
the cause.
Therefore, you should enhance your code to check for stream errors and deal with errno -- you should do this even when your code works, because EOF is not the only reason for the function
to return -1.
int len = getline(&line, &len, database);
if(len == -1 && ferror(database)) {
perror("Error reading database");
}
You can write more detailed code to deal with errno in more explicit ways.
Unfortunately handling this thoroughly can make your code a bit more verbose -- welcome to C!

Transfer file in C delievers mangled data

I'm a newbie in C programming. I am writing a function where the client copies a file from the server. However, when I open my newly created file it contains a lot of additional characters. How can I prevent it from copying useless data?
Relevant sections of the server follow
if (file = fopen(buf, "r")){
//send the file
// while(fgets(buffer, 1024, file) != NULL){
// res = write(new_fd, &buffer, sizeof(buffer));
// }
while(!feof(file)){
fscanf(file,"%s",buffer);
write(new_fd, &buffer, sizeof(buffer));
}
fclose(file);
}
Relevant sections of the client follow
fp = fopen ("testfile", "w");
while(read(sockfd, &buffer, sizeof(buffer)) != -1){
fputs(buffer, fp);
}
fclose(fp);
Two things: First don't do while (!feof(...)), it doesn't work as you expect it to. The reason is that the EOF flag is not set until after a failed read operation, so you will call fscanf once when the file has already reached the end. Instead to while (fscanf(...) == 1).
Secondly, depending on how you declare buffer, don't use &buffer or sizeof(buffer). Neither in the sender or the receiver. If buffer is a pointer then &buffer will return a pointer to that pointer, and sizeof(buffer) will return the size of the pointer and not what it points to. Besides, if buffer is an array then it might not be completely filled by the input, so why send data you don't need? Only send strlen(buffer) + 1 bytes (the +1 is for the string terminator).
Oh and a third thing, don't use fscanf to read a line, use fgets instead. Or even better, to be more effective, use fread to fill the complete buffer and send it all in less calls.
read() does not null terminate it's buffer. fputs requires a null terminated buffer.
while(read(sockfd, &buffer, sizeof(buffer)) != -1){
fputs(buffer, fp);
You can add the terminator with something like this:
int n = 0;
char buffer[SOME_CONSTANT];
while((n = read(sockfd, buffer, sizeof(buffer - 1))) != -1){
buffer[n] = 0;
fputs(buffer, fp);
}
Note also the declaration and use of buffer vs &buffer in the call to read.
Finally, see all of Joachim Pileborg's suggestions!

Reading from file which is being modified with C

I have a programme which is writing results to a file and I would like to read in real-time from that file. It is a normal text file and external programme always write a whole line. I need to run it just on a Linux system.
int last_read = 0;
int res;
FILE *file;
char *buf;
char *end = buf + MAXLINE - 1;
int c;
int fileSize;
char *dst;
while (external_programme_is_running()) {
file = fopen(logfile, "r"); //without opening and closing it's not working
fseek(file, 0, SEEK_END);
fileSize = ftell(file);
if (fileSize > last_read) {
fseek(file, last_read, SEEK_SET);
while (!feof(file)) {
dst = buf;
while ((c = fgetc(file)) != EOF && c != '\n' && dst < end)
*dst++ = c;
*dst = '\0';
res = ((c == EOF && dst == buf) ? EOF : dst - buf);
if (res != -1) {
last_read = ftell(file);
parse_result(buf)
}
}
}
fclose(file);
}
Is this a correct approach? Or would it be better to check the modification time and then open the file? Is is possible that reading would crash in case that the file would be modified at the very same time?
To avoid the need to close, re-open, and re-seek for every loop iteration, call clearerr on the stream after reading EOF.
You shouldn't have any problems if you read at the same time the other program writes. The worst that would happen is that you wouldn't get to see what was written until the next time you open the file.
Also, your approach of comparing the last seek position to the end of the file is a fine way to look for additions to the file, since the external program is simply writing additional lines. I would recommend adding a sleep(1) at the end of your loop, though, so you don't use a ton of CPU.
There's no problem in reading a file while another process is writing to it. The standard tail -f utility is used often for that very purpose.
The only caveat is that the writing process must not exclusively lock the file.
Regarding your read code (ie. using fgetc()), since you said that the writing process will be writing a line at a time, you might want to look at fgets() instead.

How to determine the size of popen stream?

I want to determine the stream size retruned by the popen() function call. I tried to use fseek and ftell but it returns the size as -1. Can anyone suggest me how to determine the file size? The following is the code what I am using....
char return_val[256];
FILE *fp = NULL;
char line[256];
memset (return_val, 0, 256);
/* set the defalut value */
strncpy (return_val, "N/A", 4);
char cmd[] = "if [ -f /etc/version ]; then cut -d, -f1 -s /etc/version ; fi";
/* Open the command for reading. */
fp = popen(cmd, "r");
if (fp != NULL)
{
/* read the line from file */
fgets (line, 256, fp);
if( line != NULL)
{
/* copy the data */
strncpy(return_val, line, strnlen (line, 256));
}
/* close the file */
pclose (fp);
}
You can't. popen gives you a pipe, a FIFO First In First Out stream. Characters go in on the one side and they come out at the other. It is in the nature of a pipe that you don't know in advance how many bytes there will be transmitted.
You may either resort to in band signalling, how many bytes there are to come, or you implement buffering and dynamic allocation on your side.
Also seeking is not possible on a pipe, because its merely a kind of "Portal" between those processes. You know, like in the game "Portal"/"Portal2".

Read one string at a time from a file in C

How do I read input one string at a time to call another function in C. I thought this would work, but my output hangs:
#define BUFFMT "%255"
#define LINE_LEN 256
#define START_COUNT 1
// filename is declared in the main file elsewhere. I know the file opens since I tried an //old method I use to read one line at time using fgets, but I didn't know how to do one //string at a time. Thanks.
FILE *OpenFile(const char *fileName)
{
FILE *fptr;
if ((fptr = fopen(fileName, "r")) == NULL) {
fprintf(stderr, "Error opening file %s, exiting...", fileName);
exit(EXIT_FAILURE);
}
return fptr;
}
LIST *CreateList(FILE *fp)
{
char buf[LINE_LEN];
while (scanf(BUFFMT"s", buf) != EOF) {
printf("%s: \n", buf);
}
}
scanf() is going to read from the terminal, so it's going to hang waiting for you to type in your input. Use fscanf(fp, BUFFMT"s", buf) instead.
Try this instead of your scanf:
fgets (buf, sizeof (buf), fp)
Have you tried using fgets()
fgets()
fgets() reads up to size-1 characters
from stream and stores them in buffer.
fgets() stores the null character
('\0') after the last character read
into the buffer and returns 'buffer'
if everything works fine, or NULL on
error or end of file.

Resources