How to pipe output to another program in C, using read - c

I'm trying to read information printed by program A from program B. How can I pass data from A to B using read()?.
code for A
#include <stdio.h>
int main(int argc, char **argv)
{
int i, j;
char instruc_list[11][3] = {"sa", "sb", "ss", "pa", "pb",
"ra", "rb", "rr", "rra", "rrb", "rrr"};
i = 0;
while (i < 11)
{
j = 0;
while (j < 3)
{
printf("%c", instruc_list[i][j]);
j++;
}
i++;
printf("\n");
}
return (0);
}
code for B
int main()
{
char buf[4];
while ((read(0,buf, 4)))
{
printf("%s", buf);
}
printf("\n");
return 0;
}
When I run this two programs, I get the following result.

Use the popen() and pclose() functions defined in stdio.h to pipe output between programs.
Here's an example program of how to print the output of the ls shell command in your program, taken from this link:
FILE *fp;
int status;
char path[PATH_MAX];
fp = popen("ls *", "r");
if (fp == NULL)
/* Handle error */;
while (fgets(path, PATH_MAX, fp) != NULL)
printf("%s", path);
status = pclose(fp);
if (status == -1) {
/* Error reported by pclose() */
...
} else {
/* Use macros described under wait() to inspect `status' in order
to determine success/failure of command executed by popen() */
...
}
For your case, you'd call popen("./A", "r");.

You can use popen() to read the output of program A from program B.
Compile the first program:
gcc a.c -o a
In the program B:
#include <stdio.h>
int main(void)
{
char buf[4];
FILE *fp;
fp = popen("./a", "r");
while( !feof(fp)) {
fscanf(fp, "%s", buf);
printf("%s\n", buf);
}
return 0;
pclose(fp);
}
Now compile and execute the program B:
gcc b.c -o b
me#linux:$ ./b
The output I got is:
sa
sb
ss
pa
pb
ra
rb
rr
rra
rrb
rrr
rrr

In program A, you're not writing the null terminators for the 3-letter strings... and in program B, you're not adding a null char after the characters you read (and haven't initialised buf, so it might not contain one). That's why you're getting garbage between the 3-letter strings you read... printf() is continuing past the characters you read because it hasn't found a null yet.
Also note that read() can return -1 for error, which would still test as true for your while loop. You should at least check that read() returns greater than 0 (rather than just non-zero), if not put in more thorough error handling.
So with some changes to address these issues, program B might become:
int main()
{
char buf[4];
int ret; // ** for the return from read()
while ((ret = read(0,buf, 4)) > 0) // ** read >0 (no error, and bytes read)
{
fwrite(buf, 1, ret, stdout); // ** write the number of chars
// you read to stdout
}
printf("\n");
return 0;
}
As for program A, right now it writes 3 characters for both the 2-letter and the 3-letter strings -- which means it includes the null char for the 2-letter strings but not for the 3-letter strings. With the changes to program B above, you don't need to write the null characters at all... so you could change:
while (j < 3)
to:
while (j < 3 && instruc_list[i][j] != 0)
to stop when the null character is reached (though it's still inefficient to use a printf() call just to write a single char -- perhaps putchar(instruc_list[i][j]); would be better). Or, you could just replace that inner while loop with:
fputs(instruc_list[i], stdout);
...which would then write the string in instruc_list[i] up to but not including the null char, and also change instruc_list[11][3] to instruc_list[11][4] so that it has room for the null char from the 3-letter string literals in the initialiser list.

Related

Segmentation fault reported in mac terminal

We were supposed to extract strings from a provided file, the output matches the expect, but it reports segmentation fault in the end and I don't know why.
#include<stdio.h>
#include<string.h>
int main(int argc, char *argv[]){
char str[100];
char f;
int len = 0;
FILE *file;
file = fopen(argv[1],"r");
//read only here, so use "r"
if(file==NULL){
printf("The file doesn't exist.\n");
return 1;
}
while(feof(file)==0){
//if feof returns 0 it means it havent reaches the end yet
fread(&f,sizeof(f),1,file);//read in the file
//printabel character between 32 and 126
if(f>=32&&f<=126){
str[len] = f;
len++;
continue;//keep doing it(for ->while)
}
if(strlen(str)>3){
//a string is a run of at least 4
printf("The output is:%s\n",str);
len=0;//reset
memset(str, 0, sizeof(str));
//reset the str so it wont get too big(overflow)
}
}
//close the file and return
fclose(file);
return 0;
}
This is not true
while(feof(file)==0){
//if feof returns 0 it means it havent reaches the end yet
And a very common mistake.
This returns 0 if you have Not read past the end of file. Its s subtle but important detail. Your last read may have read up-to the end of file but not past it. This means there is actually no data left to read but feof() will still return 0.
This is why you must test the result of the read operation.
fread(&f,sizeof(f),1,file);
If this returns zero then you failed to read anything.
Which is why you should structure your loop to test the result of the read (not feof()).
while (fread(&f,sizeof(f),1,file) == 1)
{
// You have successfully read an object from the stream
}
Your code has some fundamental errors:
See Why is while ( !feof (file) ) always wrong?
You don't check if fread returns 0, meaning that no more character could be
read, yet you continue with your algorithm
str is not '\0'-terminated, the strlen(str)>3 yields undefined
behaviour in the first iteration and will likely be evaluated as true right in the first iteration.
Then the printf would also yield undefined behaviour for the same reason.
Don't use the ASCII code directly, it's hard to read, you have to look up in
the ASCII table to see what 32 is and what 126. Better use the character
constants
if(f>= ' ' && f <= '~'){
...
}
This is easier to read and you get the intention of the code immediately.
So the program can be rewritten like this:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]){
char str[100];
char f;
int len = 0;
FILE *file;
file = fopen(argv[1],"r");
//read only here, so use "r"
if(file==NULL){
printf("The file doesn't exist.\n");
return 1;
}
memset(str, 0, sizeof str);
while(fread(&f, sizeof f, 1, file) == 1)
{
if(f >= ' ' && f <= '~')
{
str[len++] = f;
continue;
}
if(strlen(str) > 3) // or if(len > 3)
{
printf("The output is: %s\n", str);
len = 0;
memset(str, 0, sizeof str);
}
}
fclose(file);
return 0;
}

Unix (in C) trying to write tail for XV6

Hi stackoverflow(ers)!
I'm learning Unix using the XV6 OS (documentation found here) and have been trying to write a tail function in C. The expected output of:
tail is to give the last 10 lines of the file
tail - is to give the last of lines of the file
tail ... is to give the last 10 lines of files ...
tail - ... is to give the last of lines of ...
grep | tail is to give the last 10 sentences in which contain
I have written two versions of tail, one implemented using char* [] and the other by writing to a file and then reading from it (both posted below)
My version which implements the tail using char* [] seems to be more accurate to the actual command. However in the version where I'm writing to a temporary file and then reading from it I'm getting more lines as output and I'm not sure why that is happening. My guess is, while reading from one file and writing to another the placement of '\n' are getting messed up. I'd highly appreciate help in figuring it out!
Please don't get mad at me if I'm doing something silly. I'm new to C in Unix and only trying to learn.
tail.c using char* []
#include "types.h"
#include "stat.h"
#include "user.h"
#include "fcntl.h"
char buf [512];
void tail (int fd, int toSub) {
int n;
int numLines = 0;
int linesToPrint = 0;
char *buffer;
buffer = (char*) malloc (500000);
int buffSize = 0;
while ((n = read(fd, buf, sizeof(buf))) > 0) {
for (int i = 0; i<n; i++) {
buffer[buffSize] = (char)buf[i];
buffSize++;
if(buf[i] == '\n')
numLines++;
}
}
if (n < 0) {
printf (1, "tail: read error \n");
exit ();
}
if (numLines < toSub)
linesToPrint = 0;
linesToPrint = numLines - toSub;
int counter = 0;
for (int i = 0; i < buffSize; i++) {
if (counter >= linesToPrint)
printf(1,"%c",buffer[i]);
if (buffer[i] == '\n')
counter++;
}
free (buffer);
}
int main (int argc, char *argv[]) {
int toSub = 10;
int fd = -1;
if (argc <= 1) {
tail (0, toSub);
exit();
}
else if (argc > 1 && argv[1][0] == '-') {
char getToSub [10];
for (int k=1; k<strlen(argv[1]); k++) {
getToSub[k-1] = argv[1][k];
}
toSub = (atoi)(getToSub);
}
else {
if((fd = open (argv[1], toSub)) < 0) {
printf (1, "tail: cannot open %s\n", argv[1]);
exit ();
}
tail (fd, toSub);
close (fd);
}
if (argc > 2) {
for (int i=2; i<argc; i++) {
if((fd = open (argv[i], 0)) < 0) {
printf (1, "tail: cannot open %s\n", argv[i]);
exit ();
}
else {
tail (fd, toSub);
close (fd);
}
}
}
exit();
}
tail.c using write
#include "types.h"
#include "stat.h"
#include "user.h"
#include "fcntl.h"
char buf [512];
void tail (int fd, int toSub) {
int n;
int numLines;
int linesToPrint;
int ptrDump;
ptrDump = open ("tailDump", O_CREATE | O_RDWR);
while ((n = read(fd, buf, sizeof(buf))) > 0) {
write (ptrDump, buf, sizeof(buf));
for (int i = 0; i<n; i++) {
if(buf[i] == '\n')
numLines++;
}
}
if (n < 0) {
printf (1, "tail: read error \n");
exit ();
}
if (numLines < toSub)
linesToPrint = 0;
linesToPrint = numLines - toSub;
close (ptrDump);
ptrDump = open ("tailDump", 0);
int counter = 0;
while ((n = read(ptrDump, buf, sizeof(buf))) > 0) {
for (int i = 0; i<n; i++) {
if (counter > linesToPrint)
printf(1,"%c",buf[i]);
if (buf[i] == '\n')
counter++;
}
}
close (ptrDump);
unlink("tailDump");
}
int main (int argc, char *argv[]) {
int toSub = 10;
int fd = -1;
if (argc <= 1) {
tail (0, toSub);
exit();
}
else if (argc > 1 && argv[1][0] == '-') {
char getToSub [10];
for (int k=1; k<strlen(argv[1]); k++) {
getToSub[k-1] = argv[1][k];
}
toSub = (atoi)(getToSub);
}
else {
if((fd = open (argv[1], toSub)) < 0) {
printf (1, "tail: cannot open %s\n", argv[1]);
exit ();
}
tail (fd, toSub);
close (fd);
}
if (argc > 2) {
for (int i=2; i<argc; i++) {
if((fd = open (argv[i], 0)) < 0) {
printf (1, "tail: cannot open %s\n", argv[i]);
exit ();
}
else {
tail (fd, toSub);
close (fd);
}
}
}
exit();
}
I have the code put up on my Github (found here) as well in tail_using_str.c and tail_using_file.c
I think your problem is here:
while ((n = read(fd, buf, sizeof(buf))) > 0) {
write (ptrDump, buf, sizeof(buf));
You read in n bytes but when you write, you write sizeof(buf) bytes. In other words, you may write too many bytes.
Maybe you want this instead:
while ((n = read(fd, buf, sizeof(buf))) > 0) {
write (ptrDump, buf, n);
^
note
Please don't get mad at me if I'm doing something silly. I'm new to C in Unix and only trying to learn.
Thus this answer, which is not strictly necessary, since the core question you've asked has already been answered. Your posted question actually raises a bunch more questions not explicitly asked, which I intend to answer here.
The expected output of: ... tail - is to give the last of lines of the file
According to who? Not according to POSIX, and not according to UNIX V7, where tail(1) first appeared.
(Well, actually tail(1) first appeared in PWB/UNIX, but that wasn't widely used.)
grep | tail is to give the last 10 sentences in which contain
You mean last 10 lines, not sentences. grep does not produce sentences.
(Except in Soviet Unix, where grep sentences you!)
char *buffer;
buffer = (char*) malloc (500000);
This and the following exit call create a memory leak. You may say that it's harmless since the OS will give the memory back on program exit, but it's sloppy, and tools like Valgrind will call you on it.
Either free() your buffers before all possible exit points from the function, or declare this buffer on the stack instead:
char buffer[500000]
You might not be able to declare a buffer that big on the stack, depending on xv6's limits. A common modern limit for the stack size is 2 MiB, and that's for the entire stack, used by all of the functions in your deepest call chain. This is configurable is modern systems, but may not be configurable in xv6.
If you're forced to go with the malloc() option, you can do that on a single line:
char *buffer = (char*) malloc (500000);
Additionally:
it is bad style to have buf and buffer. Lazy. Give each buffer a purpose-driven name, like lineBuf and accumBuf
buffSize is confusingly named. It isn't clear which buffer it refers to, and it isn't the size of the buffer anyway. Call it something like accumBytes to solve both problems.
You're missing a bunch of #includes necessary on modern POSIX systems, and you have some that don't work on such. I'd see if xv6 has stdio.h.h, stdlib.h, string.h and unistd.h, and #include them for POSIX portability. I'd also see if you can #include types.h via sys/types.h, as that's necessary at least on macOS, and probably other Unixes. user.h isn't needed on modern systems, so if you don't actually need it on xv6, remove it.
Your in-memory variant reads the entire file into RAM and then skips back over the bytes in RAM it doesn't want to print. A bit of thought will show how you can both cut the buffer size down and not make two passes over the input data. (Hint: accumBuf[toSub][sizeof(lineBuf)]. Feel free to multiply the second term by some amount if you wish to allow lines greater than sizeof(lineBuf) bytes.)
if(buf[i] == '\n') numLines++;
You should probably check for a non-'\n' byte at the end of the accumulation buffer and add another line for it. Lines without LF terminators aren't quite kosher, but the user expectation is typically that you treat that trailing fragment as a line.
printf (1, "tail: read error \n");
What is this 1, noise? Are you trying to specify stdout? That's only correct for write, not printf. printf() already sends to stdout. (Indeed, you have to use fprintf() to send anywhere else.)
Since these are only in your error cases, that means you must not be testing for errors.
That's another reason to write code for POSIX portability even though you're ultimately targeting xv6: modern Unix system C compilers are much stricter about the code they're willing to accept. Modern C compilers do much of what we had to rely on tools like lint for in the past.
exit()
exit(2) takes a parameter, the exit status code, traditionally 0 for a clean exit and nonzero for an error. The only reason your compiler is letting you get away with that is that early C compilers did not strictly check the argument list given against the function's declared parameters. In fact, xv6 is probably shipping a K&R compiler which didn't even have function prototypes to declare the parameter lists with. The programmer was expected to do the right thing without being warned.
linesToPrint = numLines - toSub;
That isn't "lines to print", it's "lines to skip printing". It took me a good 5 minutes of staring at the code to get past that semantic mismatch. The compiler doesn't care, but variable names aren't for the compiler. If they were only for the compiler, we'd just call them all a, b, etc.
printf("%c",buffer[i]);
Use putchar() here.
int counter = 0;
Again, lazy. Count of what?
I'm only halfway through the first program, but that's enough commentary. I hope you've learned a few things from this.

Getting segmentation fault(core dumped)

My program has to encrypt/decrypt the textfile but I'm getting segmentation fault(core dumped) when I do this:
./program 9999 input.txt output.txt
The program takes every character from the input file and converts it based on the passed key. It compiles fine when I compile in CodeBlocks and does not give any errors. Could smb tell me what's wrong with the code? Thanks!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//Checks if the input arguments are less than 2 (starting from 0)
//A user should enter 3 arguments so as not to reach this method
int badProgram(const char *const program){
printf("The program is missing some of the files!");
return -1;
}
//Encrypts the passed inputFile and
//Produces its output to the passed outputFile
//Key is also passed as the seeding number
int encryptFile(FILE *input, FILE *output){
char c;
char p;
int r = 0;
char p1 = 0;
char c1 = 0;
while((p = fgetc(input)) != EOF){
r = rand() % 97;
//change all displayable characters [0...96]
if(p == 't'){
p1 = 0;
}
else if(p == '\n'){
p1 = 1;
}
else{
p1 = p - 30;
}
c1 = p1 ^ r;//bitwise xor
if(c1 == 0){
c = 't';
}
else if(c1 == 1){
c = '\n';
}
else{
c = c1 + 30;
}
//Write
fprintf(output, "%c", c);
}
}
int main(int argc, char *argv[])
{
//Check the number of the entered arguments
if(argc < 2){
return badProgram(argv[0]);
}
else{
FILE *input;
FILE *output;
//Seed a number into Random Generator
int key = *argv[0];
srand(key);
input = fopen(argv[1], "r");
output = fopen(argv[2], "w");
encryptFile(input, output);
}
return 0;
}
The **input.txt** looks like this:
Hello, World!
Bye!
Couple of things that are wrong with your code:
int key = *argv[0]; is most likely not doing what you think it does. What it actually does is the following:
assign an ASCII value of the first character of the [0] argument (program name) to an int variable
It is likely that what you intended to do there is:
int key = atoi(argv[1]); // this converts "9999" into an int 9999
input = fopen(argv[1], "r"); opens a file named (in your case) "9999" for reading and fails. You never check for the error so this is causing a crash the moment you are trying to use the input FILE pointer. The fix is to use the argv[2]
Similarly you should be using argv[3] for the output file
Your encryptFile function must return a value as it is declared int (don't know why you want to return a value from it as you never use it)
Once you fix the above issues your program no longer crashes
Update
A bit of explanation for the above issues and general info:
The argv lists all the input parameters as strings (char*) where the first ([0]) argument is the executable name and is not your first argument "after" the program name
One should always check the results of file operations as they are quite likely to fail during "normal" program operation
C/C++ doesn't "automatically" convert a string into an int (or a double, for that matter) but provides a whole set of functions to deal with numbers' parsing. Some examples of those functions are: 'atoi', 'atol', 'atof'

Debug Assertion Error in C

got some code here that won't compile correctly because it is saying that my pointer is already null when i am testing for a not null expression in my main function. here is the code :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXCODE 53
#define MAXMESSAGE 256
void getCode(char *codeIn, char *filename) {
FILE *codeFile;
/* Open the file with the code */
codeFile = fopen(filename, "r");
if (codeFile == NULL) {
printf("Error opening the code file - program terminated\n");
exit(1);
}
/* Read the first (and assumed only) line from the file */
fgets(codeIn, MAXCODE, codeFile);
/* Terminate the string with /0 */
codeIn[MAXCODE] = '\0';
/* Close the file */
fclose(codeFile);
return;
}
int getMessage(int *message, char *filename) {
FILE *messageFile;
int counter = 0;
/* Open the file with the message */
messageFile = fopen(filename, "r");
if (messageFile == NULL) {
printf("Error opening the message file - program terminated\n");
exit(1);
}
/* Read one number at a time from the file and store it */
while (!feof (messageFile))
{
fscanf (messageFile, "%d", (message+counter));
counter++;
}
/* Close the file */
fclose(messageFile);
return (counter);
}
void sortMessage(int *message, int size) {
int i, j, temp;
for (i=0; i<size-1; i++) {
for (j=i; j<size; j++) {
if (message[i]>message[j]) {
temp = message[i];
message[i] = message[j];
message[j] = temp;
}
}
}
return;
}
void decodeMessage(char *codeIn, int *message, int size) {
FILE *outputFile;
int i = 0;
/* Open the output file */
outputFile = fopen("csis.txt", "w");
if (outputFile == NULL) {
printf("Error opening the output file - program terminated\n");
exit(1);
}
for (i=0; i< size; i++) {
fprintf(outputFile, "%c", codeIn[message[i]%100]);
printf("%c", codeIn[message[i]%100]);
}
printf("\n");
/* Close the file */
fclose(outputFile);
return;
}
int main(int argc, char *argv[])
{
char code[MAXCODE];
int msg[MAXMESSAGE];
int msgSize;
if (argc != 3) {
printf("This program takes two arguments: the name of the file with the code, and the name of the file with the encoded message\n");
}
getCode(code, argv[1]);
msgSize = getMessage(msg, argv[2]);
sortMessage(msg, msgSize);
decodeMessage(code, msg, msgSize);
return;
}
So basically my code is using two files called codefile.txt and msgfile.txt to decode the secret message and write the decoded sequence to a new text file called csis.
As woolstar pointed out in the comments, you don't need to NUL terminate your codeIn array following fgets, because fgets will do that for you. In fact, this constitutes an overflow which we can best see by considering what happens when MAXCODE is 1: codeIn contains only one element: codeIn[0], and accessing codeIn[1] is an error.
Similarly, since MAXCODE is 53 and that's how many elements pointed to by codeIn, codeIn[message[i]%100] is suspicious because there's a potential for message[i]%100 to be an invalid index. While we're on this note, it might be wise to make message[i] an unsigned int so that it can't be negative. The format specifier (for printf and scanf) corresponding to unsigned int is %u.
while ( !feof(messageFile) ) is wrong because the EOF flag isn't set until an attempt is made at reading. Between attempting to read and your EOF test, however, you've incremented counter which means you've counted one too many items. Perhaps your loop should look like this:
while (fscanf(messageFile, "%d", (message+counter)) == 1)
{
counter++;
}
Note that this code assumes you've chosen to keep message[i] as an int. If you've chosen to use unsigned int instead, of course you'll want to use the %u format specifier.
You can probably see that feof is mostly superfluous... You can usually test for erroneous reads by checking the return value. Try to avoid feof in the future.
Your main function has a return type of int, yet at the end of it you have a return; statement which doesn't return an int value. Remove that. It's probably causing errors during compilation.
Presumably, when argv != 3 you want to return from main so you don't end up processing invalid arguments... Make sure you return an int value, e.g.
if (argc != 3) {
printf("This program takes two arguments: the name of the file with the code, and the name of the file with the encoded message\n");
return 0;
}

Not reading from stdin properly

I'm trying to mimic the behavior of the unix utility cat, but when I call a command of the form:
cat file1 - file2 - file3
My program will output file1 correctly, then read in from stdin, then when I press EOF, it will print file 2 then file 3, without reading from stdin for the second time.
Why might this be?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ASCII_LENGTH 255
int printfile(FILE *source, int N);
int main(int argc, char *argv[])
{
int currentarg = 1; //the argument number currently being processed
FILE *input_file;
//if there are no arguments, dog reads from standard input
if(argc == 1 || currentarg == argc)
{
input_file = stdin;
printfile(input_file,0);
}
else
{
int i;
for(i = currentarg; i < argc; i++)
{
printf("%d %s\n",i,argv[i]);
//if file is a single dash, dog reads from standard input
if(strcmp(argv[i],"-") == 0)
{
input_file = stdin;
printfile(input_file,0);
fflush(stdin);
fclose(stdin);
clearerr(stdin);
}
else if ((input_file = fopen(argv[i], "r")) == NULL)
{
fprintf(stderr, "%s: %s: No such file or directory\n", argv[0], argv[i]);
return 1;
}
else
{
printfile(input_file,0);
fflush(input_file);
fclose(input_file);
clearerr(input_file);
}
}
}
return 0;
}
int printfile(FILE *source, int N)
{
//used to print characters of a file to the screen
//characters can be shifted by some number N (between 0 and 25 inclusive)
char c;
while((c = fgetc(source)) != EOF)
{
fputc((c+N)%ASCII_LENGTH,stdout);
}
printf("***** %c %d",c,c==EOF);
return 0;
}
For one thing, you can't expect to be able to read from stdin after you've closed it:
fclose(stdin);
fflush(stdin); is undefined behaviour, as is fflush on all files open only for input. That's sort of like flushing the toilet and expecting the waste to come out of the bowl, because fflush is only defined for files open for output! I would suggest something like for (int c = fgetc(stdin); c >= 0 && c != '\n'; c = fgetc(stdin)); if you wish to discard the remainder of a line.
Furthermore, fgetc returns int for a reason: Inside the int will be an unsigned char value or EOF. c should be an int, not a char. EOF isn't a character! It's a negative int value. This differentiates it from any possible characters, because successful calls to fgetc will only return a positive integer rather than a negative EOF. fputc expects input in the form of an unsigned char value. char isn't required to be unsigned. Providing your fgetc call is successful and you store the return value into an int, that int should be safe to pass on to fputc.

Resources