The loop that reads from the file and prints the result is not printing the first 5 characters from all files read. If I print them 1 character at a time it works fine, but I'm reading floating point numbers from a file which I need to be processed later. The numbers are seperated by white space which is why I'm trying to print only when encountering white space.
here is my source code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#define BUFFER_SIZE 100
#define READ_END 0
#define WRITE_END 1
//main function
int main(int argc, char *argv[])
{
//process ids
int processIds[argc - 1];
int pipes[argc - 1][2];
char *fileNames[argc - 1];
int status;
//check if at least one data file
if (argc == 1)
{
return -1;
//end program
}
//get file names and store in array fileNames
int i;
for (i = 0; i < (argc - 1); i++)
{
fileNames[i] = argv[i + 1];
}
//create child processes
for (i = 0; i < (argc - 1); i++)
{
//make a pipe for communication
if (pipe(pipes[i]))
{
printf("bad pipe");
return -1;
}
//make a child process
processIds[i] = fork();
//check if child or paren process
if (processIds[i] == 0)
{
//child process
//close unused end
close(pipes[i][READ_END]);
//make file
FILE *dataset = fopen(fileNames[i], "r");
//test if file opened
if (dataset == 0)
{
printf("could not find/open file");
return -1;
}
//read and process file
char *x;
char *num = "";
int min, max;
while ((*x = fgetc(dataset)) != EOF)
{
if ((x == " ") || (x == "\t") || (x == "\n"))
{
//end of number
printf("A%s B%s", num, num);
num = "";
}
else
{
strcat(num, x);
}
//printf("%c", x);
}
printf("\n\n");
char msg[BUFFER_SIZE];
write(pipes[i][WRITE_END], msg, strlen(msg) + 1);
fclose(dataset);
exit(0);
}
else if (processIds[i] < 0)
{
//error
return -1;
}
else
{
//parent process closes write end of pipe
close(pipes[i][WRITE_END]);
}
}
//wait for children
for (i = 0; i < (argc - 1); i++)
{
//create a read buffer
char read_buf[BUFFER_SIZE];
//wait and get pid of completed child
int pid = wait(&status);
//find which child completed
int j = 0;
while (pid != processIds[j] && j < (argc - 1))
{
//pid not recognized, should not happen
if (j >= (argc - 1))
{
printf("bad pid");
return -1;
}
j++;
}
//read from completed child
read(pipes[j][READ_END], read_buf, BUFFER_SIZE);
}
return 0;
}
i can spot the following misses concerning your code:
1) Memory allocation
you need to allocate your num variable
example :
char *num = malloc(10);
2) returned type
'fgetc' doesn't return a pointer nor a char, so you should define
int x;
x = fgetc(dataset);
3) your condition is wrong
if ((x == ' ') || (x == '\t') || (x == '\n'))
note that ' ' is not " "
NOW as my suggestion to read those separate strings:
1) read all the file in buffer :
char buff[SIZE];
dataset= fopen("fileName.txt", "r");
if( dataset!= NULL ){
while ( !feof(fp ) ){
memset(buff, '\0', sizeof( buff) );
fgets(buff, SIZE, (FILE*)dataset);
}
fclose(dataset);
}
2) use strchr to find next space and print your strings
see example of use here
Related
I have an exercise where I need to interact with a C program through pipe.
I have the following source, which I can't modify.
#include <stdio.h>
#include <stdlib.h>
int main()
{
int number;
int answer;
number = rand() % 100;
printf("Print the double of the number %d\n", number);
scanf("%d", &answer);
if(number * 2 == answer)
printf("Success\n");
else
printf("Error\n");
}
I tried to interact with this program with this code
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv, char **env)
{
int STDIN_PIPE[2];
int STDOUT_PIPE[2];
pipe(STDIN_PIPE);
pipe(STDOUT_PIPE);
pid_t pid = fork();
if(pid == 0)
{
char *path = "/path/to/binary";
char *args[2];
args[0] = path;
args[1] = NULL;
close(STDIN_PIPE[1]);
close(STDOUT_PIPE[0]);
dup2(STDIN_PIPE[0], STDIN_FILENO);
dup2(STDOUT_PIPE[1], STDOUT_FILENO);
execve(path, args, env);
}
else
{
char buf[128];
close(STDIN_PIPE[0]);
close(STDOUT_PIPE[1]);
while(read(STDOUT_PIPE[0], buf, 1))
write(1, buf, 1);
}
}
But when I run it, it falls in an infinite loop without printing nothing.
I have fixed a number of issues in your code, added a lot of error checks and completed it so that the end goal is reached.
In the child process, srand() must be called to initialize the random number generator or you always get the same value.
The in the child process, you must flush(stdout) after printing the question so that it is really written to the pipe.
And finally, scanf() return value must be checked.
In the main process, I added a lot of error checks. And I write a readLine function to - guess what - read a line from the pipe. A line is terminated by the end-of-line character \n.
There is still room for some enhancements...
I tested my code using Visual Studio Code configured for gcc and running under Ubuntu 20.04.
Here is the child process source:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
int number;
int answer;
time_t t;
srand((unsigned)time(&t));
number = rand() % 100;
printf("Print the double of the number %d\n", number);
fflush(stdout);
int n = scanf("%d", &answer);
if (n != 1) {
printf("Invalid input\n");
return 1;
}
if ((number * 2) == answer) {
printf("Success\n");
return 0;
}
printf("Error %d is not 2 * %d\n", answer, number);
return 1;
}
And here is the main process source:
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
int readLine(int fd, char *buf, int bufSize);
int main(int argc, char **argv, char **env)
{
int STDIN_PIPE[2];
int STDOUT_PIPE[2];
if (pipe(STDIN_PIPE))
{
perror("pipe(STDIN_PIPE)");
return 1;
}
if (pipe(STDOUT_PIPE)) {
perror("pipe(STDOUT_PIPE)");
return 1;
}
pid_t pid = fork();
if (pid == 0)
{
char *path = "../Child/Child"; // Path to child process, adapt to your environment
char *args[2];
args[0] = path;
args[1] = NULL;
if (dup2(STDIN_PIPE[0], STDIN_FILENO) == -1) {
perror("dup2(STDIN) failed");
return 1;
}
if (dup2(STDOUT_PIPE[1], STDOUT_FILENO) == -1) {
perror("dup2(STDIN) failed");
return 1;
}
// Close all pipe ends
close(STDIN_PIPE[0]); // Close read end of STDIN_PIPE
close(STDIN_PIPE[1]); // Write end of STDIN_PIPE
close(STDOUT_PIPE[0]); // Read end of STDOUT_PIPE
close(STDOUT_PIPE[1]); // Close write end of STDOUT_PIPE
if (execve(path, args, env) == -1) {
perror("execve failed");
return 1;
}
}
else
{
char buf[128];
int bufSize = sizeof(buf) / sizeof(buf[0]);
int i;
// Read the question asked by child process
if (readLine(STDOUT_PIPE[0], buf, bufSize) < 0) {
printf("readLine failed.\n");
return 1;
}
// We receive something like "Print the double of the number 83"
printf("Child process question is \"%s\".\n", buf);
// Extract the number at end of string
i = strlen(buf) - 1;
while ((i >= 0) && isdigit(buf[i]))
i--;
int value = atoi(buf + i + 1);
// Write our answer to write end of STDIN_PIPE
char answer[128];
int answerSize = sizeof(answer) / sizeof(answer[0]);
int answerLen = snprintf(answer, answerSize, "%d\n", value * 2);
printf("Our answer is \"%d\".\n", value * 2);
if (write(STDIN_PIPE[1], answer, answerLen) != answerLen) {
printf("write failed.\n");
return 1;
}
// Read the response (success or failure) sent by child process
if (readLine(STDOUT_PIPE[0], buf, bufSize) < 0) {
printf("readLine failed.\n");
return 1;
}
if (strcasecmp(buf, "Success") == 0)
printf("Child process returned success.\n");
else
printf("Child process returned failure.\n");
// Close all pipe ends
close(STDIN_PIPE[0]); // Close read end of STDIN_PIPE
close(STDIN_PIPE[1]); // Write end of STDIN_PIPE
close(STDOUT_PIPE[0]); // Read end of STDOUT_PIPE
close(STDOUT_PIPE[1]); // Close write end of STDOUT_PIPE
}
return 0;
}
// Read a line from file descriptor
// A line is any characters until \n is received or EOF
// \n is not kept
// Return the number of characters read or <0 if error:
// -1 => Input buffer overflow
// -2 => read() failed and errno has the error
int readLine(int fd, char *buf, int bufSize)
{
int i = 0;
while (1)
{
// Check if enough room in the buffer
if (i >= bufSize) {
printf("Input buffer overflow\n");
return -1;
}
// Read one character from the pipe
ssize_t n = read(fd, buf + i, 1);
if (n == -1)
{
perror("read() failed");
return -2;
}
if (n == 0)
{
// EOF received, that's OK
return i;
}
// NUL terminate the buffer
buf[i + 1] = 0;
// Check for end of line character
if (buf[i] == '\n') {
buf[i] = 0; // Remove ending \n
return i;
}
i++;
}
}
I am trying to use multiple processes with the fork and waitpid functions to modify a program that reads through a file and return the total number of lines, words, and characters. The program compiles and runs fine, but the output always displays zero for all three values even though the file is not empty. Does anyone know how I can fix this? The user enters the file name and the number of child processes they want when they run the program. Here is my code:
//wc.h
#ifndef WC_H
#define WC_H
#include <stdio.h>
typedef struct count_t {
int linecount;
int wordcount;
int charcount;
} count_t;
count_t word_count(FILE* fp, long offset, long size);
extern int crashRate;
#endif
//wc_mul.c
#include "wc.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#define MAX_PROC 100
#define MAX_FORK 100
int crashRate = 0;
count_t word_count(FILE* fp, long offset, long size)
{
char ch;
long rbytes = 0;
count_t count;
// Initialize counter variables
count.linecount = 0;
count.wordcount = 0;
count.charcount = 0;
printf("[pid %d] reading %ld bytes from offset %ld\n", getpid(), size, offset);
if(fseek(fp, offset, SEEK_SET) < 0) {
printf("[pid %d] fseek error!\n", getpid());
}
while ((ch=getc(fp)) != EOF && rbytes < size) {
// Increment character count if NOT new line or space
if (ch != ' ' && ch != '\n') { ++count.charcount; }
// Increment word count if new line or space character
if (ch == ' ' || ch == '\n') { ++count.wordcount; }
// Increment line count if new line character
if (ch == '\n') { ++count.linecount; }
rbytes++;
}
srand(getpid());
if(crashRate > 0 && (rand()%100 < crashRate))
{
printf("[pid %d] crashed.\n", getpid());
abort();
}
return count;
}
int main(int argc, char **argv)
{
long fsize;
FILE *fp;
int numJobs;
//plist_t plist[MAX_PROC];
count_t total, count, buf;
int i, j, pid, status, p[2];
int nFork = 0;
if(argc < 3) {
printf("usage: wc <# of processes> <filname>\n");
return 0;
}
if(argc > 3) {
crashRate = atoi(argv[3]);
if(crashRate < 0) crashRate = 0;
if(crashRate > 50) crashRate = 50;
}
printf("crashRate RATE: %d\n", crashRate);
numJobs = atoi(argv[1]);
if(numJobs > MAX_PROC) numJobs = MAX_PROC;
total.linecount = 0;
total.wordcount = 0;
total.charcount = 0;
// Open file in read-only mode
fp = fopen(argv[2], "r");
if(fp == NULL) {
printf("File open error: %s\n", argv[2]);
printf("usage: wc <# of processes> <filname>\n");
return 0;
}
fseek(fp, 0L, SEEK_END);
fsize = ftell(fp);
fclose(fp);
// calculate file offset and size to read for each child
for(i = 0; i < numJobs; i++) {
if(nFork++ > MAX_FORK) return 0;
pid = fork();
printf("%d\n", pid);
if(pid < 0) {
printf("Fork failed.\n");
} else if(pid == 0) {
// Child
fp = fopen(argv[2], "r");
if (pipe(p) != 0)
exit(1);
count = word_count(fp, 0, fsize);
write(p[1], count, MAX_PROC);
close(p[0]);
// send the result to the parent through the message queue
fclose(fp);
return 0;
}
}
waitpid(pid, &status, 0);
close(p[1]);
for (j=0; j < numJobs; j++) {
read(p[0], buf, MAX_PROC);
total.linecount += buf.linecount;
total.wordcount += buf.wordcount;
total.charcount += buf.charcount;
}
// Parent
// wait for all children
// check their exit status
// read the result from normalliy terminated child
// re-crete new child if there is one or more failed child
printf("\n========== Final Results ================\n");
printf("Total Lines : %d \n", total.linecount);
printf("Total Words : %d \n", total.wordcount);
printf("Total Characters : %d \n", total.charcount);
printf("=========================================\n");
return(0);
}
No code modifies total.linecount in the process that prints it and this variable was not placed in shared memory. So the output will always be zero.
You either need to place the variable in shared memory or you need to modify it in the same process in which you print it. The fork function creates a new process that inherits its parent's current view of memory but does not share modifications to memory unless memory is explicitly made shareable.
Were this not so, your code would explode as all the different fork children try to modify the same fp variable!
Where is the code that sums the results from the various children?
The code I've written finds out the number of words in multiple text files by creating multiple processes with each process being responsible for one file to count its words.
What I want to do is using pipes to find out total number of words in all files.
So the parent should:
creates a pipe between the each child and itself so it can get the number of words from each child
reports the total number of words in all the files by adding the numbers received through pipes
checks the exit status of each child and prints out how that child
exited
also let each child:
sends the number of the words to the parent via the pipe
send 0 as word count through the pipe to the parent if the file does
not exist or any other error happens
returns/exits with 0 if it is successfull in opening the file and
counting the words in that file, returns/exits with 1 if there is an
error (e.g., file does not exist etc.)
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#define MAX_CHAR 100
pid_t getpid(void);
pid_t getppid(void);
char* itoa(int i, char b[]){
char const digit[] = "0123456789";
char* p = b;
if(i<0){
*p++ = '-';
i *= -1;
}
int shifter = i;
do{ //Move to where representation ends
++p;
shifter = shifter/10;
}while(shifter);
*p = '\0';
do{ //Move back, inserting digits as u go
*--p = digit[i%10];
i = i/10;
}while(i);
return b;
}
int countWords(char * fp, int pid) {
FILE * file;
int words = 0;
char word[MAX_CHAR];
//execute this function only if child process of parent, no gradchild is allowed to execute this function!
if (pid == getppid()) {
file = fopen(fp, "r");
if (file == NULL) {
return -1;
}
//find string in the file and count the words.
while (fscanf(file, "%s", word) != EOF) {
words++;
}
return words;
} else {
return -1;
}
return 0;
}
int main(int argc, char * arvg[]) {
//if invalid arguments
if (argc < 2) {
fprintf(stderr, "ERROR: INVALID ARGUMENTS");
exit(-1);
}
int count = 0, pid, ppid, status, totalwords;
int result = -1;
int fd[2];
char string[100];
char readbuffer[80];
int *write_fd = &fd[1];
int *read_fd = &fd[0];
result = pipe(fd);
if(-1 == result){
perror("pipe");
return -1;
}
//creates (argc - 1) child processes using fork()
pid = (int) malloc((argc - 1) * sizeof(int));
//parent pid
ppid = getpid();
//each child process to count the number of words in each file
for (int i = 1; i < argc; i++) {
//child process
pid = fork();
if( pid == -1){
perror("failed to fork");
return -1;
}else if (pid == 0) {
// call a function to count the number of words in file arvg[i]
int words = countWords(arvg[i], ppid);
close(*read_fd);
if (words >= 0) {
printf("Child process pid_%d for %s :number of words is %d\n", i, arvg[i], words);
//I don't know how to write int into the pipe,so below might be wrong
write(*write_fd, words, 1);
return 0;
} else if (words == -1) {
printf("Child process pid_%d for %s :does not exists\n", i, arvg[I]);
//I don't know how to write int into the pipe,so below might be wrong
write(STDOUT_FILENO, words, 1);
exit(1);
}
} else {
close(*write_fd);
//and I have no idea how to read int from pipes
read(*read_fd, &readbuffer, 1);
totalwords += ???
close(*read_fd);
//Wait until all child processes exit/return
if (ppid == getpid()) {
wait( & status);
}
//inspect their exit codes, WEXITSTATUS = return code when child exits
if (WEXITSTATUS(status) == 1) {
count++;
}
}
}
printf("Main process created %d child processes to count words in %d files\n", argc - 1, argc - 1);
printf("Total words is %d", totalwords);
printf("%d files have been counted sucessfully!\n", argc - 1 - count);
printf("%d files did not exist.\n", count);
return 0;
}```
Can someone help me to figure out this? I don't really know how to achieve my goal with pipe.
found some issues with the code. I fixed them for you (however, I would have done the same thing slight differently)
reading and writing int from a pipe is pretty straight forward, just typecast correctly while reading or writing to an int.
malloc to a pid was not necessary. Also malloc returns a pointer and should have been typecasted with (int*)
always add the right includes while using calls. Manual page or reading about the calls while trying to understand the parameters passed and return values is extremely useful.
Enough said, here is your working code
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAX_CHAR 100
pid_t getpid(void);
pid_t getppid(void);
char* itoa(int i, char b[]){
char const digit[] = "0123456789";
char* p = b;
if(i<0){
*p++ = '-';
i *= -1;
}
int shifter = i;
do{ //Move to where representation ends
++p;
shifter = shifter/10;
}while(shifter);
*p = '\0';
do{ //Move back, inserting digits as u go
*--p = digit[i%10];
i = i/10;
}while(i);
return b;
}
int countWords(char * fp, int pid) {
FILE * file;
int words = 0;
char word[MAX_CHAR];
//execute this function only if child process of parent, no gradchild is allowed to execute this function!
if (pid == getppid()) {
file = fopen(fp, "r");
if (file == NULL) {
return -1;
}
//find string in the file and count the words.
while (fscanf(file, "%s", word) != EOF) {
words++;
}
return words;
} else {
return -1;
}
return 0;
}
int main(int argc, char * arvg[]) {
//if invalid arguments
if (argc < 2) {
fprintf(stderr, "ERROR: INVALID ARGUMENTS");
exit(-1);
}
int count = 0, pid, ppid, status, totalwords = 0;
int result = -1;
int fd[2];
char string[100];
char readbuffer[80];
int *write_fd = &fd[1];
int *read_fd = &fd[0];
int recvd = 0;
result = pipe(fd);
if(-1 == result){
perror("pipe");
return -1;
}
//creates (argc - 1) child processes using fork()
//pid = (int) malloc((argc - 1) * sizeof(int));
//parent pid
ppid = getpid();
//each child process to count the number of words in each file
for (int i = 1; i < argc; i++) {
//child process
pid = fork();
if( pid == -1){
perror("failed to fork");
return -1;
}else if (pid == 0) {
printf ("%d child running \n", i);
// call a function to count the number of words in file arvg[i]
int words = countWords(arvg[i], ppid);
close(*read_fd);
if (words >= 0) {
printf("Child process pid_%d for %s :number of words is %d\n", i, arvg[i], words);
//I don't know how to write int into the pipe,so below might be wrong
write(*write_fd, (void *)&words, 1);
return 0;
} else if (words == -1) {
printf("Child process pid_%d for %s :does not exists\n", i, arvg[i]);
//I don't know how to write int into the pipe,so below might be wrong
write(STDOUT_FILENO, (void *)&words, 1);
exit(1);
}
} else {
close(*write_fd);
//and I have no idea how to read int from pipes
read(*read_fd, (void*)&recvd, 1);
totalwords += recvd;
printf("recvd %d \n", totalwords);
close(*read_fd);
//Wait until all child processes exit/return
if (ppid == getpid()) {
wait( & status);
}
//inspect their exit codes, WEXITSTATUS = return code when child exits
if (WEXITSTATUS(status) == 1) {
count++;
}
}
}
printf("Main process created %d child processes to count words in %d files\n", argc - 1, argc - 1);
printf("Total words is %d\n", totalwords);
printf("%d files have been counted sucessfully!\n", argc - 1 - count);
printf("%d files did not exist.\n", count);
return 0;
}
Well, the first time around, I did not focus on the algo. I fixed all of it. The problem is forking in a loop and reading and writing it would lead to wrong results. Moreover, Parent needs to look for EOF to ensure all read has happened. Anyways, Here is the code that should work
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAX_CHAR 100
pid_t getpid(void);
pid_t getppid(void);
char* itoa(int i, char b[]){
char const digit[] = "0123456789";
char* p = b;
if(i<0){
*p++ = '-';
i *= -1;
}
int shifter = i;
do{ //Move to where representation ends
++p;
shifter = shifter/10;
}while(shifter);
*p = '\0';
do{ //Move back, inserting digits as u go
*--p = digit[i%10];
i = i/10;
}while(i);
return b;
}
// count word from file provided
int countWords(char * fp, int pid) {
FILE * file;
int words = 0;
char word[MAX_CHAR];
//execute this function only if child process of parent, no gradchild is allowed to execute this function!
if (pid == getppid()) {
file = fopen(fp, "r");
if (file == NULL) {
return -1;
}
//find string in the file and count the words.
while (fscanf(file, "%s", word) != EOF) {
words++;
}
return words;
} else {
return -1;
}
return 0;
}
//do everything related to child here in this function
void child_process(int write_fd, char *filename, int ppid)
{
// call a function to count the number of words in file argv[i]
printf("counting words of %s\n", filename);
int words = countWords(filename, ppid);
if (words >= 0) {
printf("Child process pid for %s :number of words is %d\n", filename, words);
write(write_fd, (void *)&words, 1);
close(write_fd);
exit(0);
} else if (words == -1) {
printf("Child process pid for %s :does not exist\n", filename);
write(STDOUT_FILENO, (void *)&words, 1);
close(write_fd);
exit(1);
}
return;
}
int main(int argc, char * argv[]) {
//if invalid arguments
if (argc < 2) {
fprintf(stderr, "ERROR: INVALID ARGUMENTS");
exit(-1);
}
int pid = 0;
int ppid = 0;
int totalwords = 0;
int fd[2] = {0};
int write_fd = 0;
int read_fd = 0;
int recvd = 0;
// open a pipe
if(-1 == pipe(fd)){
perror("pipe");
return -1;
}
// assign write_fd and read_fd
write_fd = fd[1];
read_fd = fd[0];
//parent pid
ppid = getpid();
//each child process to count the number of words in each file
pid = fork();
for (int i = 0; i < argc-1; i++)
{
//child process
if (pid == 0) {
close(read_fd);
child_process(write_fd, argv[i+1], ppid);
break;
} else {
pid = fork();
}
}
// don't let child run beyond this point
if (pid == 0) {
exit(0);
}
// parent only code
if (pid > 0)
{
close(write_fd);
while (read(read_fd, (void*)&recvd, 1) > 0)
{
wait(NULL);
totalwords += recvd;
}
close(read_fd);
}
printf("Main process created %d child processes to count words in %d files\n", argc - 1, argc - 1);
printf("Total words is %d\n", totalwords);
printf("%d files have been counted sucessfully!\n", argc - 1);
}
I'm trying to read from the pipe once and print out the results, but I get a double output.
I thought the read and write sizes were incorrect (Why is the output printed twice with write() and not with print() in IPC using pipe?), but I printed out the size in the child before the write and then tried inputted the same size to the read function and I still get a double output.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
typedef struct
{
int PID;
char *filename;
int wordCount;
int lineCount;
int byteCount;
} filestr;
int pipe(int pd[2]);
void mywc(FILE *fp, char *name, filestr *fileA);
int isParam(char* fileName);
int getParam(int argCount, char **args);
void error_exit(char *s);
int main(int argc, char *argv[])
{
int pd[2], status, pid, param,
wordT=0, lineT=0, byteT=0;
filestr fileAtr;
//param = getParam(argc, argv);
//printf("PARAM: %d\n", param);
if(pipe(pd) == -1)
error_exit("pipe() failed");
for(int i = 1; i < argc; ++i)
{
pid = fork();
if(pid == -1)
{
perror("fork");
exit(1);
}
else if(pid == 0)
{
FILE *file = fopen(argv[i], "r");
if(file != 0){
mywc(file, argv[i], &fileAtr);
wait(NULL);
close(pd[0]);
printf("SIZE: %d\n", sizeof(fileAtr));
if(write(pd[1], &fileAtr, sizeof(fileAtr)) == -1)
error_exit("write() failed");
}
exit(0);
}
}
int sum;
for(int j = 0; j < argc; ++j)
{
close(pd[1]);
if(read(pd[0], &fileAtr, (32*sizeof(int))) == -1)
error_exit("read() failed");
printf("PID : %d\n", fileAtr.PID);
printf("File Name : %s\n", fileAtr.filename);
printf("Words : %d\n", fileAtr.wordCount);
printf("Lines : %d\n", fileAtr.lineCount);
printf("Bytes : %d\n\n", fileAtr.byteCount);
wordT += fileAtr.wordCount;
lineT += fileAtr.lineCount;
byteT += fileAtr.byteCount;
}
printf("Grand Total: word: %d line: %d byte: %d\n", wordT, lineT, byteT);
}
void mywc(FILE *fp, char *name, filestr *fileA)
{
int c, lineCount=0, wordCount=0, byteCount=0;
while( (c = getc(fp)) != EOF )
{
if( c == ' ' )
{
wordCount++;
byteCount++;
}
else if ( c == '\n')
{
wordCount++;
byteCount++;
lineCount++;
}
else
{
byteCount++;
}
}
fileA->PID = getpid();
fileA->filename = name;
fileA->wordCount = wordCount;
fileA->lineCount = lineCount;
fileA->byteCount = byteCount;
}
int getParam(int argCount, char **args)
{
int param;
for(int i = 0; i < argCount; i++)
{
if((strcmp(args[i], "-lwc") == 0) || (strcmp(args[i], "-lcw") == 0) || (strcmp(args[i], "-wlc") == 0)
|| (strcmp(args[i], "-wcl") == 0) || (strcmp(args[i], "-cwl") == 0) || (strcmp(args[i], "-clw") == 0))
param = 3;
else if((strcmp(args[i], "-w") == 0))
param = 0;
else if((strcmp(args[i], "-l") == 0))
param = 1;
else if((strcmp(args[i], "-c") == 0))
param = 2;
}
return param;
}
void error_exit(char *s)
{
fprintf(stderr, "\nError: %s\n", s);
exit(1);
}
My Ouput:
SIZE: 32
PID : 14896
File Name : test
Words : 4
Lines : 4
Bytes : 26
PID : 14896
File Name : test
Words : 4
Lines : 4
Bytes : 26
Grand Total: word: 8 line: 8 byte: 52
EDIT: Added full code
The loop that prints your output
for(int j = 0; j < argc; ++j)
{
...
}
Goes from j = 0 instead of j = 1. argc is 2. So it prints the output twice.
A combination of two problems: First, for(int i = 1; i < argc; ++i) will run argc - 1 times, but for(int j = 0; j < argc; ++j) will run argc times. Second, if(read(pd[0], &fileAtr, (32*sizeof(int))) == -1) only handles one thing that read can do. In addition to failing with -1, it can also return 0 when it gets to EOF, or return some bytes, but fewer than you asked for (called a partial read). In your case, the EOF is happening, but your code doesn't notice, so it just happily goes along with the data that happened to already be there (in this case, a duplicate of the final result).
When I simply pipe ls -l | sort with this, the program just spits out the results from ls -l infinitely. Can anyone see what's wrong?
Assume that you only have to look at the main function. This will compile, though.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/wait.h>
#define COMMAND_LINE_LENGTH 256
#define HISTORY_LENGTH 10
#define TOKEN_MAX 50
#define DIRECTORY_LENGTH 5
#define DIRECTORY_PREFIX "/bin/"
struct prog_def
{
//Binary location
char *bin;
//Is this program expecting a pipe?
int expecting_pipe;
//Arguments
char *args[TOKEN_MAX + 1];
pid_t pid;
} prog_def;
int get_prog_defs(const char* buf, struct prog_def prog_defs[])
{
char *line = malloc(strlen(buf) + 1);
int prog_count = 0;
char* token;
strcpy(line, buf);
line[strlen(buf)] = 0;
while(1)
{
int arg_count = 0;
//The first time through we have to pass the line
token = strtok(line, " ");
//Each subsequent call we have to pass NULL
//http://www.cplusplus.com/reference/cstring/strtok/
line = NULL;
//Start building the binary location string
prog_defs[prog_count].bin = (char*)malloc(strlen(token) + DIRECTORY_LENGTH + 1);
//Concatenate the directory prefix and command name
strcat(prog_defs[prog_count].bin, DIRECTORY_PREFIX);
strcat(prog_defs[prog_count].bin, token);
//The first argument execvp will expect is the binary location itself
//Redundant but if I wasn't too lazy to read the doc then I'd know why
prog_defs[prog_count].args[arg_count++] = prog_defs[prog_count].bin;
while(1)
{
prog_defs[prog_count].expecting_pipe = 0;
//Check next token for end, pipe, IO redirection, or argument
token = strtok(NULL, " ");
//If we've consumed all tokens
if (token == NULL)
break;
//Pipe
if (strcmp(token, "|") == 0)
{
prog_defs[prog_count - 1].expecting_pipe = 1;
break;
}
//Regular argument
prog_defs[prog_count].args[arg_count++] = token;
}
++prog_count;
if (token == NULL) break;
}
return prog_count;
}
int main(int argc, char** argv)
{
char command[COMMAND_LINE_LENGTH] = {0};
//Generic loop counter
int x = 0;
while(1)
{
printf(">");
//Get the command
gets(command);
struct prog_def prog_defs[TOKEN_MAX];
int prog_count = get_prog_defs(command, prog_defs);
//Keep the previous out fd for the in of the subsequent process
int prev_out_fd = open("/dev/null", O_RDONLY);
for (x = 0; x < prog_count; ++x)
{
//Create a pipe for both processes to share
int pipefd[2];
if (x != prog_count -1)
{
pipe(pipefd);
}
prog_defs[x].pid = fork();
if(prog_defs[x].pid == 0)
{
dup2(prev_out_fd, STDIN_FILENO);
close(pipefd[1]);
if(x != prog_count - 1)
{
dup2(pipefd[1], STDOUT_FILENO);
close(pipefd[0]);
close(pipefd[1]);
}
execvp(prog_defs[x].bin, prog_defs[x].args);
prev_out_fd = pipefd[0];
close(pipefd[1]);
}
close(prev_out_fd);
prev_out_fd = pipefd[0];
close(pipefd[1]);
}
printf("\n");
for (x = 0; x < prog_count; ++x)
{
waitpid(prog_defs[x].pid, NULL, 0);
}
}
}
You call malloc to get some memory for a string, which will be uninitialized, so contain random garbage. You then call strcat which will attempt to append another string to the random garbage and almost certainly run off the end of the malloc'd space, leading to random confusing behavior and crashes.
You also use prog_defs[prog_count - 1] before ever incrementing prog_count, so the first time through the loop, (when prog_count == 0) this will write before the start of the array, which also leads to random confusing behavior and crashes.