I am trying to write the shell program in C for I/O redirection. The input and output is working fine, but I also have to append output to the end of a file with the '>>', but it's not working. Whenever, I use >> in the command line, it just overwrites the output to the file. And I also get a warning when I run the program: warning: multi-character character constant. I would really appreciate it if someone can please assist me in the right direction to fix this problem.
// Check for redirected input
input = redirect_input(args, &input_filename);
switch(input) {
case -1:
printf("Syntax error!\n");
continue;
break;
case 0:
break;
case 1:
printf("Redirecting input from: %s\n", input_filename);
break;
}
// Check for redirected output
output = redirect_output(args, &output_filename);
switch(output) {
case -1:
printf("Syntax error!\n");
continue;
break;
case 0:
break;
case 1:
printf("Redirecting output to: %s\n", output_filename);
break;
}
// Check for redirected append output
append = redirect_append(args, &append_filename);
switch(append) {
case -1:
printf("Syntax error!\n");
continue;
break;
case 0:
break;
case 1:
printf("Redirecting output to: %s\n", output_filename);
break;
}
// Do the command
do_command(args, block,
input, input_filename,
output, output_filename,
append, append_filename);
}
}
/*
* Do the command
*/
int do_command(char **args, int block,
int input, char *input_filename,
int output, char *output_filename,
int append, char *append_filename) {
int result;
pid_t child_id;
int status;
// Fork the child process
child_id = fork();
// Check for errors in fork()
switch(child_id) {
case EAGAIN:
perror("Error EAGAIN: ");
return;
case ENOMEM:
perror("Error ENOMEM: ");
return;
}
if(child_id == 0) {
// Set up redirection in the child process
if(input)
freopen(input_filename, "r", stdin);
if(output)
freopen(output_filename, "w+", stdout);
if (append)
freopen(append_filename, "a", stdout);
// Execute the command
result = execvp(args[0], args);
exit(-1);
}
// Wait for the child process to complete, if necessary
if(block) {
printf("Waiting for child, pid = %d\n", child_id);
result = waitpid(child_id, &status, 0);
}
}
/*
* Check for input redirection
*/
int redirect_input(char **args, char **input_filename) {
int i;
int j;
for(i = 0; args[i] != NULL; i++) {
// Look for the <
if(args[i][0] == '<') {
//free(args[i]);
// Read the filename
if(args[i+1] != NULL) {
*input_filename = args[i+1];
} else {
return -1;
}
// Adjust the rest of the arguments in the array
for(j = i; args[j-1] != NULL; j++) {
args[j] = args[j+2];
}
return 1;
}
}
return 0;
}
/*
* Check for output redirection
*/
int redirect_output(char **args, char **output_filename) {
int i;
int j;
for(i = 0; args[i] != NULL; i++) {
// Look for the >
if(args[i][0] == '>') {
//free(args[i]);
// Get the filename
if(args[i+1] != NULL) {
*output_filename = args[i+1];
} else {
return -1;
}
// Adjust the rest of the arguments in the array
for(j = i; args[j-1] != NULL; j++) {
args[j] = args[j+2];
}
return 1;
}
}
return 0;
}
/*
* Check for append redirection
*/
int redirect_append(char **args, char **append_filename) {
int i;
int j;
for(i = 0; args[i] != NULL; i++) {
// Look for the >>
if(args[i][0] == '>' && args[i][1] == '>') {
//free(args[i]);
// Read the filename
if(args[i+2] != NULL) {
*append_filename = args[i+2];
} else {
return -1;
}
// Adjust the rest of the arguments in the array
for(j = i; args[j-1] != NULL; j++) {
args[j] = args[j+2];
}
return 1;
}
}
return 0;
}
if(args[i][0] == '>>') {
Should be:
if(args[i][0] == '>' && args[i][1] == '>') {
This is the source of your "multi-character constant" warning as well as your output redirection problem.
Related
This is supposed to flips upper and lower case letters but its not flipping just adding random characters.
int in = open(argv[1], O_RDONLY);
int out = open(argv[2], O_CREAT | O_WRONLY, 0624);
char buff[65];
buff[64] = '\0';
if(argc < 2){
printf("Not enough arguments");
return 1;
}
else if(argv[1] == 0 || argv[2] == 0){
printf("No file");
return 1;
}
int i = read(in,buff,64);
for (i = 0; buff[i]!='\0'; i++) {
if(buff[i] >= 'a' && buff[i] <= 'z') {
printf("%d", buff[i]-32);
} else if (buff[i] >= 'A' && buff[i] <= 'Z') {
printf("%d", buff[i]+32);
} else {
printf("%d", buff[i]);
}
}
write(out, buff, 64);
close(in);
close(out);
return 0;
}
How do I get it to read the character and flip without extras?
If your input file does not contain a '\0' as last character, your condition buff[i]!='\0' depends on random contents.
Change these lines:
char buff[65];
buff[64] = '\0';
to this line:
char buff[65] = { 0 };
However, read() tells you the number of bytes it read. You can use that value to mark the end:
int n = read(in,buff,64);
for (i = 0; i < n; i++) {
/* ... */
}
write(out, buff, n);
Write a function that reads a line, up to some maximum size; separate the logic of reading the file from other processing,
int readline(int fh, char* buff, int maxsize) {
int rc = read(fh,buff,maxsize);
if( rc < 0 ) {
printf("read error, %d\n",rc);
return rc;
}
return rc;
}
Write a function that writes the converted buffer, separate the logic of writing the file and other processing,
int writeline(int fh, char* buff, int len) {
int wc = write(fh, buff, len);
return wc;
}
Write a function that flips the case; separate the logic from reading and writing the file,
char* flipcase(char* buff, int len) {
if(!buff || len<1) return buff;
char* cp = buff;
for (int ix = 0; ix<len; ix++, cp++ ) {
if( isupper(*cp) { // in [A-Z]
// printf("%d", *cp-32); // not portable
*cp = tolower(*cp); // modify buff[ix]
}
else if( islower(*cp) ) { // in [a-z]
// printf("%d", *cp+32); // not portable
*cp = toupper(*cp); // modify buff[ix]
}
// else {
// unchanged
// }
// printf("%d", *cp);
}
return buff;
}
Build a function that handles each line separately,
# define MAXLINE (256) // named 'constant'
int doline(int fin, int fout) {
char buff[MAXLINE+1] = { 0 };
int rc = readline(fin, buff, MAXLINE);
// check results of readline here
flipcase(buff, rc);
int wc = writeline(fout, buff, rc);
// check results of writeline here
return rc;
}
Here you would handle your (argc, argv) and open your files,
if(argc < 3) {
printf("Not enough arguments");
return 1;
}
if(argv[1] == 0 || argv[2] == 0) {
printf("No file");
return 1;
}
int fin = open(argv[1], O_RDONLY);
if( !fin ) {
printf("open %s failed\n",argv[1]);
return 2;
}
int fout = open(argv[2], O_CREAT | O_WRONLY, 0624);
if( !fout ) {
printf("open %s failed\n",argv[2]);
close(fout);
return 2;
}
int rc = 0;
// process one line
rc = doline(fin,fout);
// or, process every line in file
for( ; rc = doline(fin,fout) >= 0; ) {
}
close(fin);
close(fh);
Trying to give a Minimal Viable Example of the problem. Basically the method send_chars_to_reducers sends a character to the proper reducer_pipe. The fork_reducers function remains in its while loop until it receives EOF but it never does even though I close all reducer pipes in send_chars_to_reducers. I know it doesnt exit the while loop because it never prints exiting reducers.
C Code
void send_chars_to_reducers(char * line) {
printf("SEND_CHARS_TO_REDUCERS read: %s\n\n", line);
int i;
int ob_size = 1;
int wlen = 0;
for (i = 0; i < strlen(line); i++) {
if (line[i] >= ALPHA_OFFSET && line[i] < ALPHA_OFFSET + LETTERS) {
int pipe_num = line[i] - ALPHA_OFFSET;
printf("SENDING %c TO REDUCER PIPE %d\n", line[i], pipe_num);
wlen = print_if_err(write(reducer_pipes[pipe_num][PIPE_WRITE_END], &line[i], ob_size), "write");
printf("WROTE %s to REDUCER %d\n", line[i], i);
}
}
close_reducer_pipes();
}
void close_reducer_pipes(void) {
int i;
for (i = 0; i < NUM_OF_REDUCERS; i++) {
close(reducer_pipes[i][PIPE_WRITE_END]);
close(reducer_pipes[i][PIPE_READ_END]);
}
}
void fork_mappers(void) {
/* Constants useful to all children */
char ibuf[PIPE_BUFFER_SIZE]; // input pipe buffer
int rlen = 0;
int i;
for (i=0; i<NUM_OF_MAPPERS; i++) {
pid_t mapper_pid = print_if_err(fork(), "fork");
if (mapper_pid == 0) {
int j;
for (j=0; j < NUM_OF_MAPPERS; j++) {
close(mapper_pipes[i][PIPE_WRITE_END]);
if (j != i) {
close(mapper_pipes[j][PIPE_READ_END]);
}
}
rlen = print_if_err(read(mapper_pipes[i][PIPE_READ_END], ibuf, 1000), "read");
send_chars_to_reducers(ibuf);
close_reducer_pipes();
//printf("forked mapper%d read: %s\n\n", i, ibuf);
close(mapper_pipes[i][PIPE_READ_END]);
_exit(0);
}
}
}
void fork_reducers(void) {
printf("HELLLOOOO FROM REDUCER\n");
char ibuf[PIPE_BUFFER_SIZE]; // input pipe buffer
int rlen = 0;
int i;
for (i = 0; i < NUM_OF_REDUCERS; i++) {
pid_t reducer_pid = print_if_err(fork(), "fork");
if (reducer_pid == 0) {
while (1) {
rlen = print_if_err(read(reducer_pipes[i][PIPE_READ_END], ibuf, 1), "read");
if (rlen > 0) {
printf("REDUCER #%d, read %s\n", i, ibuf);
} else {
break;
}
}
printf("exiting reducer\n");
_exit(0);
}
}
}
Entire C Code
#include <sys/wait.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#define BUFFER_SIZE 1024
#define ALPHA_OFFSET 97
#define LETTERS 26
const int NUM_OF_MAPPERS = 4;
const int NUM_OF_REDUCERS = 26;
const int PIPE_READ_END = 0;
const int PIPE_WRITE_END = 1;
const int PIPE_BUFFER_SIZE = 1000;
int mapper_pipes[4][2];
int reducer_pipes[26][2];
int letter_count[26];
void init_letter_count(void) {
int i;
for (i =0; i < 26; i++) {
letter_count[i] = 0;
}
}
void pipe_wrapper(int pipefd[]) {
int ret = pipe(pipefd);
if (ret == -1) {
perror("Error. Failed when trying to create pipes.");
exit(EXIT_FAILURE);
}
}
void create_mapper_pipes(void) {
int i;
for (i = 0; i < NUM_OF_MAPPERS; i++) {
pipe_wrapper(mapper_pipes[i]);
}
}
void create_reducer_pipes(void) {
int i;
for (i=0; i < NUM_OF_REDUCERS; i++) {
pipe_wrapper(reducer_pipes[i]);
}
}
// Prints an error msg and exits if one occurs. Else, returns the system call value.
int print_if_err(int syscall_val, const char* syscall_name) {
if (syscall_val < 0) {
perror(syscall_name);
exit(errno);
} else {
//No syscall error we can return
return syscall_val;
}
}
void send_chars_to_reducers(char * line) {
printf("SEND_CHARS_TO_REDUCERS read: %s\n\n", line);
int i;
int ob_size = 1;
int wlen = 0;
for (i = 0; i < strlen(line); i++) {
if (line[i] >= ALPHA_OFFSET && line[i] < ALPHA_OFFSET + LETTERS) {
int pipe_num = line[i] - ALPHA_OFFSET;
printf("SENDING %c TO REDUCER PIPE %d\n", line[i], pipe_num);
wlen = print_if_err(write(reducer_pipes[pipe_num][PIPE_WRITE_END], &line[i], ob_size), "write");
printf("WROTE %c to REDUCER %d\n", line[i], pipe_num);
}
}
printf("END OF SEND CHAR FOR LOOP");
close_reducer_pipes();
}
void close_reducer_pipes(void) {
int i;
for (i = 0; i < NUM_OF_REDUCERS; i++) {
print_if_err(close(reducer_pipes[i][PIPE_WRITE_END]), "close");
print_if_err(close(reducer_pipes[i][PIPE_READ_END]), "close");
}
}
void fork_mappers(void) {
/* Constants useful to all children */
char ibuf[PIPE_BUFFER_SIZE]; // input pipe buffer
int rlen = 0;
int i;
for (i=0; i<NUM_OF_MAPPERS; i++) {
pid_t mapper_pid = print_if_err(fork(), "fork");
if (mapper_pid == 0) {
int j;
for (j=0; j < NUM_OF_MAPPERS; j++) {
close(mapper_pipes[i][PIPE_WRITE_END]);
if (j != i) {
close(mapper_pipes[j][PIPE_READ_END]);
}
}
rlen = print_if_err(read(mapper_pipes[i][PIPE_READ_END], ibuf, 1000), "read");
send_chars_to_reducers(ibuf);
//printf("forked mapper%d read: %s\n\n", i, ibuf);
close(mapper_pipes[i][PIPE_READ_END]);
_exit(0);
}
}
}
void fork_reducers(void) {
printf("HELLLOOOO FROM REDUCER\n");
char ibuf[PIPE_BUFFER_SIZE]; // input pipe buffer
int rlen = 0;
int i;
for (i = 0; i < NUM_OF_REDUCERS; i++) {
pid_t reducer_pid = print_if_err(fork(), "fork");
if (reducer_pid == 0) {
while (1) {
rlen = print_if_err(read(reducer_pipes[i][PIPE_READ_END], ibuf, 1), "read");
printf("RLEN = %d\n", rlen);
if (rlen > 0) {
int letter_count_i = ibuf[0] - ALPHA_OFFSET;
printf("REDUCER #%d, read %s, letter_count_i = %d\n", i, ibuf, letter_count_i);
letter_count[letter_count_i]++;
} else {
break;
}
}
printf("REDUCER EXITING\n");
_exit(0);
}
}
}
void send_lines_to_mappers(void) {
int wlen = 0;
char obuf[PIPE_BUFFER_SIZE];
int ob_size;
int count = 0;
char buff[BUFFER_SIZE]; // a buffer for each line of the file
FILE *input_file = fopen("input.txt", "r");
// read the input file line by line
while(fgets(buff, BUFFER_SIZE, input_file) > 0) {
//printf("send_lines_to_mappers read: %s\n\n", buff);
ob_size = sizeof buff;
switch(count) {
case 0 :
write(mapper_pipes[0][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[0][PIPE_WRITE_END]);
close(mapper_pipes[0][PIPE_READ_END]);
break;
case 1 :
write(mapper_pipes[1][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[1][PIPE_WRITE_END]);
close(mapper_pipes[1][PIPE_READ_END]);
break;
case 2 :
write(mapper_pipes[2][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[2][PIPE_WRITE_END]);
close(mapper_pipes[2][PIPE_READ_END]);
break;
case 3 :
write(mapper_pipes[3][PIPE_WRITE_END], buff, ob_size);
close(mapper_pipes[3][PIPE_WRITE_END]);
close(mapper_pipes[3][PIPE_READ_END]);
break;
default :
printf("you did something wrong in send_lines_to_mappers loop");
}
count++;
}
fclose(input_file);
}
int main(void) {
init_letter_count();
// Setup the mapper pipes
create_mapper_pipes();
create_reducer_pipes();
fork_reducers();
fork_mappers();
send_lines_to_mappers();
return 0;
}
When you create a pipe, there are two ends ("file descriptors"), the reading end and the writing end.
When you fork(), the child process inherits ALL the open file descriptors, including both ends of ANY open pipe.
So, if you want:
[child1] >===pipe1===> [parent] >===pipe2===> [child2]
for example, then you have a several file descriptors to close.
In child1, you need to close the READ end of pipe1 AND BOTH ends of
pipe2.
In child2, you need to close the WRITE end of pipe2 and BOTH ends of
pipe1.
In the parent, after you are done forking, you need to close the
WRITE end of pipe1 and the READ end of pipe2.
As you have lots of pipes, you will have even more closing to do.
If you are not sure you have everything closed, the program lsof ("list open file-descriptors") can be helpful.
i've created a program which is a re-make of the wc program in BASH. For some reason my check doesn't work as it should. Word count and Line count (which are handled by my child processes, using fork) still display when they should not. if i type './test -n' it is only meant to display the current user. however it seems to display that followed by word and line count, even though i didn't ask for it. the if statement which doesn't seem to work is near the bottom of the code. here is my code:
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <sys/wait.h>
/* Size of character buffer to read in a file. */
#define BUFFSIZE 1000000
/* Read file 'filename' into character buffer 'text'.
*
* #param filename file to read from
* #param text character buffer to read into
*
* #return the number of bytes read.
*/
long read_file(char *filename, char *buff)
{
FILE *fp = fopen(filename, "r");
long size = 0; // Number of characters read.
int len = 0;
if (fp == NULL)
{
fprintf(stderr,"1 Error could not open file: %s\n",strerror(errno));
return -1;
}
/* Go to the end of the file. */
if (fseek(fp, 0L, SEEK_END) == 0)
{
/* Get the size of the file. */
size = ftell(fp);
if (size == -1)
{
fprintf(stderr,"2 Error could not open file: %s\n",strerror(errno));
return -1;
}
/* Go back to the start of the file. */
if (fseek(fp, 0L, SEEK_SET) != 0)
{
fprintf(stderr,"3 Error rewinding to start of file: %s\n",strerror(errno));
return -1;
}
/* Read the entire file into memory. */
len = fread(buff, sizeof(char), (size_t)size, fp);
if (len == 0)
{
fprintf(stderr,"4 Error reading file into memory: %s\n",strerror(errno));
return -1;
}
else
{
buff[++len] = '\0'; /* Add a null-terminator. */
}
}
(void)fclose(fp);
return size;
}
int compute_words(char* fileloc)
{
int wordcount = 0;
int check = 1;
char file;
FILE *f = fopen(fileloc, "r");
while((file=getc(f)) != EOF)
{
if(isspace(file) || file == '\t' || file == '\n')
{
if (check == 0)
{
check++;
wordcount++;
}
}
else
{
check = 0;
}
}
fclose(f);
return wordcount;
}
int compute_lines(char* fileloc)
{
int linecount = 0;
char file;
FILE *f = fopen(fileloc, "r");
while((file=getc(f)) != EOF)
{
if(file == '\n')
linecount++;
}
fclose(f);
return linecount;
}
/* The name of this program. */
const char* program_name;
/* Prints usage information for this program to STREAM (typically
stdout or stderr), and exit the program with EXIT_CODE. Does not
return. */
void print_usage (FILE* stream, int exit_code)
{
fprintf (stream, "Usage: %s options [ inputfile .... ]\n", program_name);
fprintf (stream,
" -h --help Display this usage information.\n"
" -n --num Display my student number.\n"
" -c --chars Print number of characters in FILENAME.\n"
" -w --words Print number of words in FILENAME.\n"
" -l --lines Print number of lines in FILENAME.\n"
" -f --file FILENAME Read from file.\n");
exit (exit_code);
}
/* Main program entry point. ARGC contains number of argument list
elements; ARGV is an array of pointers to them. */
int main (int argc, char* argv[])
{
int pipes[2][2];
pid_t child[2];
int status = 0;
int i;
//printf("\nParents Pro ID is %d\n\n", getpid());
char* fileloc = "/usr/share/dict/words";
char buffer[BUFFSIZE];
char* buff = &buffer[0];
int num = 0, chars = 0, words = 0, lines = 0;
int wordcount = 0;
int linecount = 0;
int next_option;
/* A string listing valid short options letters. */
const char* const short_options = "hncwlf:";
/* An array describing valid long options. */
const struct option long_options[] = {
{ "help", 0, NULL, 'h' },
{ "num", 0, NULL, 'n' },
{ "chars", 0, NULL, 'c' },
{ "words", 0, NULL, 'w' },
{ "lines", 0, NULL, 'l' },
{ "file", 1, NULL, 'f' },
{ NULL, 0, NULL, 0 } /* Required at end of array. */};
/* The name of the file to receive program output, or NULL for
standard output. */
const char* output_filename = NULL;
/* Remember the name of the program, to incorporate in messages.
The name is stored in argv[0]. */
program_name = argv[0];
do
{
next_option = getopt_long (argc, argv, short_options,long_options, NULL);
switch (next_option)
{
case 'h': /* -h or --help */
/* User has requested usage information. Print it to standard
output, and exit with exit code zero (normal termination). */
print_usage (stdout, 0);
case 'n':
num=1;
break;
case 'c':
chars=1;
break;
case 'w':
words=1;
break;
case 'l':
lines=1;
break;
case 'f':
fileloc = optarg;
break;
case '?': /* The user specified an invalid option. */
/* Print usage information to standard error, and exit with exit
code one (indicating abnormal termination). */
print_usage (stderr, 1);
case -1: /* Done with options. */
if(!num && !chars && !words && !lines)
chars=1;words=1;lines=1;
break;
default: /* Something else: unexpected. */
abort ();
}
}
while (next_option != -1);
for(i = 0; i < 3; i++)
{
if (pipe(pipes[i]) != 0)
{
printf("Error pipe %d could not be created\n", i);
exit(1);
}
if ((child[i] = fork()) == -1)//create fork
{
printf("Error fork %d could not be created\n", i);
exit(1);
}
else if (child[i] == 0) //fork successful
{
close(pipes[i][0]);
if(words && child[0]) //child 1
{
int computewords = compute_words(fileloc);
write(pipes[0][1], &computewords, sizeof(computewords));
}
if(lines && child[1]) //child 2
{
int computelines = compute_lines(fileloc);
write(pipes[1][1], &computelines, sizeof(computelines));
}
exit(0);
}
}
for (i = 0; i < 2; i++)
{
wait(&status);
}
if(num)
{
char *z=getenv("USER");
if(z == NULL) return EXIT_FAILURE;
printf("\nStudent number: 12345 and logged in as %s\n", z);
}
if(chars)
printf("\nNumber of Characters in the file:%s:\t%ld\n", fileloc, read_file(fileloc, buff));
if(words)
{
close(pipes[0][1]);
read(pipes[0][0], &wordcount, 50);
close(pipes[0][0]);
printf("\nNumber of Words in the file:%s:\t%d\n", fileloc, wordcount);
}
if(lines)
{
close(pipes[1][1]);
read(pipes[1][0], &linecount, 50);
close(pipes[1][0]);
printf("\nNumber of Lines in the file:%s:\t%d\n", fileloc, linecount);
}
close(pipes[0][0]);
close(pipes[1][0]);
close(pipes[0][1]);
close(pipes[1][1]);
return 0;
}
There's something else going on here - an if statement will work if you're getting the expected arguments. Try debugging the main program as it appears you have an error in your option parsing.
Consider the following case statement:
case -1: /* Done with options. */
if(!num && !chars && !words && !lines)
chars=1;words=1;lines=1;
break;
You have an if without braces around the assignments. Just because the statements are on the same line, doesn't mean the parser understands your intent. Instead it'll be parsed as such:
case -1: /* Done with options. */
if(!num && !chars && !words && !lines)
chars=1;
words=1;
lines=1;
break;
Which certainly will lead to unexpected behavior.
if(!num && !chars && !words && !lines)
chars=1;words=1;lines=1;
is equivalent to
if(!num && !chars && !words && !lines)
chars=1;
words=1;
lines=1;
You need some braces, or to put everything in a single statement like this:
if(!num && !chars && !words && !lines)
chars=words=lines=1;
I'm trying to get 2 way communication between a main file and a helper file.
The main file forks, and the child does some pipe work and then runs an exec.
My problem is that I can send information from the child exec to the parent exec, but not the other way around.
Below Is my entire code from the two files, so you should be able to run it.
Any help in getting the 2 way communication working will be extremely helpful. i'm been at this for almost 8 hours straight now.
When you run it, you'll see it print out "yo 0". This was me testing that it takes an integer from the main file, sends it to the helper, adds yo in front of it and sends it back. The first slab of code is the main file, second is the helper, third is the map file needed to run it. make sure there isn't a blank line underneath the last line, and the fourth is the agent file needed to run it.
the running is [./handler mapfile 20 agentfile.]
the int 20 doesn't do anything yet, but you need it in there to run the file.
If anyone actually goes to the effort to do all this and help me, i am eternally grateful
main file (handler.c)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/wait.h>
enum ErrorCode {
SHOW_USAGE = 1, BAD_STEPS, OPEN_MAP_ERROR, CORRUPT_MAP,
OPEN_AGENT_ERROR, CORRUPT_AGENTS, AGENT_ERROR,
AGENT_WALLED, AGENT_COLLIDED, TOO_MANY_STEPS, INVALID_AGENT_RESPONSE,
AGENT_CLOSED, AGENT_DIED, SIGINT_REC
};
typedef struct {
int valid;
int row, col;
} Point;
typedef struct {
Point point;
int number;
char name;
char param[20];
char type[20];
} Agent;
typedef struct {
int rows, cols;
char **grid;
} Map;
Map map;
Map agentMap;
int listSize = 0;
void error(enum ErrorCode e) {
switch(e) {
case SHOW_USAGE:
fprintf(stderr, "Usage: handler mapfile maxsteps agentfile\n");
break;
case BAD_STEPS:
fprintf(stderr, "Invalid maxsteps.\n");
break;
case OPEN_MAP_ERROR:
fprintf(stderr, "Unable to open map file.\n");
break;
case CORRUPT_MAP:
fprintf(stderr, "Corrupt map.\n");
break;
case OPEN_AGENT_ERROR:
fprintf(stderr, "Unable to open agent file.\n");
break;
case CORRUPT_AGENTS:
fprintf(stderr, "Corrupt agents.\n");
break;
case AGENT_ERROR:
fprintf(stderr, "Error running agent.\n");
break;
case AGENT_WALLED:
fprintf(stderr, "Agent walled.\n"); // needs fixing, check spec sheet
break;
case AGENT_COLLIDED:
fprintf(stderr, "Agent collided.\n"); // same as AGENT_WALLED
break;
case TOO_MANY_STEPS:
fprintf(stderr, "Too many steps.\n");
break;
case INVALID_AGENT_RESPONSE:
fprintf(stderr, "Agent sent invalid response.\n"); // fixiing
break;
case AGENT_CLOSED:
fprintf(stderr, "Agent exited with status.\n"); // fixiing
break;
case AGENT_DIED:
fprintf(stderr, "Agent exited due to signal.\n"); // fixing
break;
case SIGINT_REC:
fprintf(stderr, "Exiting due to INT signal.\n");
break;
}
exit(e);
}
void print_map(Map map)
{
int r;
for (r = 0; r < map.rows; ++r) {
printf("%s", map.grid[r]);
}
puts("");
}
void print_agents(Agent *agents, int size)
{
int i;
for (i = 0; i < size; i++) {
Agent temp = agents[i];
printf("%d %d %c %d %s %s %i\n", temp.point.row, temp.point.col, temp.name, temp.number, temp.type, temp.param, i);
}
puts("");
}
void readMap(char *file)
{
int r;
FILE *fd = fopen(file, "r");
char buffer[20];
char d;
if (!fd) {
error(OPEN_MAP_ERROR);
}
if (fgets(buffer, 20, fd) == NULL) {
error(CORRUPT_MAP);
}
if (sscanf(buffer, "%d %d%1[^\n]\n", &map.rows, &map.cols, &d) != 2 ||
map.rows < 1 || map.rows > 999 || map.cols < 1 || map.cols > 999) {
error(CORRUPT_MAP);
}
map.grid = malloc(map.rows * sizeof(char *));
for (r = 0; r < map.rows; ++r) {
map.grid[r] = calloc(map.cols + 2, sizeof(char));
if (fgets(map.grid[r], map.cols + 2, fd) == NULL ||
map.grid[r][map.cols] != '\n') {
error(CORRUPT_MAP);
}
}
fclose(fd);
}
void checkAgent(char *file)
{
FILE *fd = fopen(file, "r");
if (!fd) {
error(AGENT_ERROR);
}
fclose(fd);
}
int growList (Agent **agentList, int curSize, int increaseNum)
{
const int newSize = curSize + increaseNum;
Agent *temp = (Agent*) realloc(*agentList, (newSize * sizeof(Agent)));
if (temp == NULL) {
exit(20);
}
else {
*agentList = temp;
return newSize;
}
}
Agent* readAgentFile(char *file, Agent *agentList)
{
int readCount = 0;
FILE *fp = fopen(file, "r");
char buffer[80];
listSize = 0;
if (!fp) {
error(OPEN_AGENT_ERROR);
}
if (fgets(buffer, 80, fp) == NULL) {
error(CORRUPT_AGENTS);
}
rewind(fp);
while (fgets(buffer, 80, fp) != NULL) {
if (buffer[0] != '#') {
Agent agent;
sscanf( buffer, "%d %d %c %s %s" ,&agent.point.row, &agent.point.col, &agent.name, agent.type, agent.param);
checkAgent(agent.type);
agent.number = readCount+1;
listSize = growList(&agentList, listSize, 1);
agentList[readCount] = agent;
readCount++;
}
}
if (readCount == 0) {
error(CORRUPT_AGENTS);
}
fclose(fp);
return agentList;
}
void createAgentMap()
{
int i,j;
agentMap = map;
for (i=0; i < map.rows; i++) {
for (j=0; j < map.cols; j++) {
char c = map.grid[i][j];
if (c == '.') {
agentMap.grid[i][j] = ' ';
}
}
}
}
int main(int argc, char **argv)
{
int steps;
int pid;
int returnStatus;
int i;
int out_pipe[2];
int in_pipe[2];
char ch[20];
Agent firstAgent;
Agent *agentList =(Agent *) calloc(1, sizeof(Agent));
if (argc != 4) {
error(SHOW_USAGE);
}
sscanf(argv[2], "%d", &steps);
if ((steps < 1)) {
error(BAD_STEPS);
}
readMap(argv[1]);
agentList = readAgentFile(argv[3], agentList);
firstAgent = agentList[0];
createAgentMap();
for (i=0; i < listSize; i++) {
if (pipe(out_pipe) < 0) {
perror("Pipe Error");
}
if (pipe(in_pipe) < 0) {
perror("Child pipe error");
}
Agent temp;
temp = agentList[i];
switch ( pid = fork() )
{
case -1:
perror("Can't fork.\n");
exit(20);
case 0:
/* Child */
/*close(1);
dup(in_pipe[1]);
close(0);
dup(out_pipe[0]);
close(in_pipe[0]);
close(out_pipe[1]);*/
dup2(out_pipe[0], 0);
dup2(in_pipe[1], 1);
execlp(temp.type, temp.type, temp.param, (char *)0);
perror("No exec");
default:
//close(1);
//dup(handlerChild[1]);
//fprintf(stdout, "%d", listSize);
write(out_pipe[1], "%d", listSize);
close(in_pipe[1]);
close(0);
dup(in_pipe[0]);
if (fgets(ch, 20, stdin) == NULL) {
break;
}
printf("%s\n", ch);
}
}
while (steps > 0) {
steps -= 1;
}
return 0;
}
helper file (simple.c)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
typedef struct {
int valid;
int row, col;
} Point;
typedef struct {
int numAgents;
char agentNames[80];
int agentNumber;
} Info;
typedef struct {
int rows, cols;
char **grid;
} Map;
Map agent_map;
int main(int argc, char **argv)
{
int steps = 10;
int simple_pipe[2];
int dir;
char inputDir;
char input_stream[20];
int in = dup(0);
Info info;
if (argc == 2) {
sscanf(argv[1], "%c1", &inputDir);
switch (inputDir) {
case 'N': dir = 0; break;
case 'E': dir = 1; break;
case 'S': dir = 2; break;
case 'W': dir = 3; break;
default : fprintf(stdout, "Invalid params.\n"); exit(2);
}
}
else {
fprintf(stdout, "Incorrect number of params.\n");
exit(1);
}
close(0);
dup(simple_pipe[0]);
fgets(input_stream, 20, stdin);
sscanf(input_stream, "%d", &info.numAgents);
//printf("%d", info.numAgents);
//printf("this is the input: %s\n", input_stream); // This is successfully printing to stdout in the pipe
fprintf(stderr, "yo %d \n", info.numAgents);
while (steps > 0) {
steps -= 1;
}
exit(0);
}
map file
6 6
##..##
#....#
#.##.#
#....#
##....
######
agent file
1 1 A ./simple E
2 2 B ./simple N
5 2 C ./simple S
A pipe is a unidrectional connection across processes. Before you fork, you open the pipe and it will reserve two file descriptors, where fd[0] can be read from and fd[1] can be written to.
So when you want to have a two way commumincation you need to create two pipes, and then use one for reading in the parent writing in the child and the second pipe the other way around.
A more detailed explanation along with some sample code can be foun dhere: http://linux.die.net/man/2/pipe
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
int c, n, E, b, s, v, t, opt, valid = 0;
char current = '\0';
char previous = '\0';
FILE *fp;
/* -n numbers lines
* -E appends a dollar sign to line ends
* -b numbers only non-blank lines
* -s squeezes multiple blank lines down to 1
* -v displays control chars, excluding tab
* -t includes tab in the above
* -e is the same as -E and -v
*/
int setFlags(int argc, char *argv[]) {
int op;
while ((op = getopt(argc, argv, "nEbsvte")) != -1) {
switch (op) {
case 'n': {
n = 1;
break;
} case 'E': {
E = 1;
break;
} case 'b': {
b = 1;
break;
} case 's': {
s = 1;
break;
} case 'v': {
v = 1;
break;
} case 't': {
t = 1;
break;
} case 'e': {
E = 1;
v = 1;
break;
} case '?': {
//fprintf(stderr, "Option `-%c` is not valid.\n", optopt);
return EXIT_FAILURE;
} default: {
abort();
}
}
}
opt = optind;
if(n == 1) {
b = 0;
}
return EXIT_SUCCESS;
}
int checkFile(char *path) {
if (access(path, R_OK) == 0) {
return EXIT_SUCCESS;
} else {
fprintf(stderr, "cat: %s: %s\n", argv[i], strerror(errno));
errno = 0;
return EXIT_FAILURE;
}
}
int doPrint(char *path) {
if (strcmp(path, "stdin") == 0) {
fp = stdin;
} else {
if (checkFile(path) == 1) {
return EXIT_FAILURE;
} else {
fp = fopen(path, "r");
}
}
while ((c = fgetc(fp)) != EOF) {
putchar(c);
}
fclose(fp);
return EXIT_SUCCESS;
}
int main (int argc, char *argv[]) {
if (setFlags(argc, argv) == 1) {
fprintf(stderr, "The program has terminated with an error.\n"
"An invalid option was specified.\n");
return EXIT_FAILURE;
} else {
if ((argc - opt) == 0) {
doPrint("stdin");
} else {
for(int i = opt; i < argc; i++) {
doPrint(argv[i]);
}
}
}
}
I'm getting a really crazy bug, where my program outputs the error line in checkFile, before it finishes writing the contents of the file (always one chat before the end).
It's driving me insane, and no matter where I move that piece of code, it doesn't work as intended.
I'm sure the answer is probably trivial, but it has me stumped. I'd even thrown in sleeps and various other things just before output finished, and it would throw the error, THEN sleep, THEN print the final character.
Any help?
When using printf, stdout output is buffered by default. This means it can be interleaved with other output, often from stderr. stderr is unbuffered by default so that it's output is printed immediately as would normally be desired when an error occurs.
Interleaving can be fixed with judicious use of fflush or by turning off file buffering of stdout using setbuf. Be sure to read the man pages for setbuf as there are some caveats.
In this case, adding fflush(stdout) at the end of the doPrint function should fix the "problem".