"I think he is talking about piping input and output through a GUI."
I have code for this that might be applicable. It's not the best, but it works. This can actually be run as a shell, although functionality is limited.
Essentially you might be looking looking for execvp, or execlp if you want to hard-code parameters.
Code dump:
shell.c (main):
Code: Select all
/************************************************************************
* Created by Steven A. Wilson *
* CS 470-001 S'08 Assignment 4 *
* This is a basic command interpreter for Linux *
* Specifications: *
* + Supports background processing *
* + Supports MAX_INPUT input chars and MAX_ARGS process arguments *
* + Supports chained pipes up to MAX_PS *
* + Supports output to single file *
***********************************************************************/
#include "run.h"
#include "input.h"
#include "defs.h"
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
int main(int argc, char* argv[]) {
const char* EXIT_STR = "exit";
const char* QUIT_STR = "quit";
const char PROMPT[] = "shell> ";
const char DELIMS[] = ">|";
char actual_input[MAX_INPUT]; //Stores the user's input
char tokenize[MAX_INPUT]; //A copy of actual_input that we run strtok on
char buffer[MAX_INPUT]; //A modifiable copy of current input btwn DELIMs
char* pipe_input; //Pointer past the next DELIM
char* input; //The current input between DELIMs
size_t psi = 0; //process index
//Initialize list of processes
Process* ps_queue[MAX_PS];
for (int i = 0;i < MAX_PS;++i)
ps_queue[i] = NULL;
Process* cur_ps = ps_queue[psi];
Process* prev_ps = NULL;
//Get input from the user, tokenize it on pipe characters.
//If there are no pipes, parse the whole line.
//We have to copy the whole thing because split_str also calls strtok
get_input(PROMPT, actual_input, MAX_INPUT);
strncpy(tokenize, actual_input, MAX_INPUT);
pipe_input = strtok(tokenize, DELIMS);
input = (pipe_input) ? pipe_input : tokenize;
while ( strlen(input) == 0 ||
((strncmp(input, EXIT_STR, MAX_INPUT) != 0) &&
(strncmp(input, QUIT_STR, MAX_INPUT) != 0)) )
{
trim_str(input);
prev_ps = cur_ps;
cur_ps = ps_queue[psi] = init_ps(alloc_ps());
//Copy our working input to buffer so we can modify it w/o modifying
//the actual_input
strncpy(buffer, input, MAX_INPUT);
//trim_str(buffer);
cur_ps->background = (buffer[strlen(buffer)-1] == '&');
//Cut off the '&' if we're running in background.
//Not storing size_t strlen(input)-1 because bkgd is uncommon
if (cur_ps->background)
buffer[strlen(buffer)-1] = '\0';
if (input-1 > 0)
cur_ps->file = (*(input-1) == '>');
split_str(cur_ps->argv, buffer, " ", MAX_ARGS);
//Move our point of reference (re: the pipe & fd), if applicable
set_pipes(cur_ps, prev_ps);
//Since split_str also calls strtok, we need to redo this
//Must recopy because apparently strtok does something to the original
strncpy(tokenize, actual_input, MAX_INPUT);
pipe_input = strtok(tokenize, DELIMS);
//Advance one past our current token
for (int tok_track = 0;tok_track <= psi;++tok_track)
pipe_input = strtok(NULL, DELIMS);
if (prev_ps && prev_ps->file) {
printf("shell: Parse error: Cannot redirect after first '>>'\n");
free_all(ps_queue, MAX_PS);
pipe_input = NULL;
} else if ( prev_ps && (cur_ps->background || prev_ps->background) ) {
printf("shell: Parse error: Cannot use '&' with '|' or '>>'.\n");
free_all(ps_queue, MAX_PS);
pipe_input = NULL;
}
//This process has been totally initialized.
//Move on to the next.
if (!pipe_input) { //No more processes to move on to
run_all(ps_queue, MAX_PS);
free_all(ps_queue, MAX_PS);
cur_ps = NULL;
prev_ps = NULL;
psi = 0;
//Reset our user input buffer
memset(actual_input, '\0', sizeof(actual_input));
//Get new input and the initial pipe val
get_input(PROMPT, actual_input, MAX_INPUT);
strncpy(tokenize, actual_input, MAX_INPUT);
pipe_input = strtok(tokenize, DELIMS);
} else {
//Continue tokenization
psi++;
}
input = (pipe_input) ? pipe_input : tokenize;
}
return 0;
}
run.c
Code: Select all
/**********************
* run.c
* Steven A Wilson - CS 470
* Part of shell
* Contains function definitions for running a parsed command
*********************/
#include "run.h"
#include "process.h"
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h> //file i/o
#include <sys/fcntl.h> //file i/o
/*
* Handles the forking and does the exec call, etc
* argv follows standard argv practice (prog is argv[0])
* background determines whether or not parent waits for completion
*/
bool run(Process* ps, int ps_index) {
if (ps == NULL)
return false;
const char CHILD_ERROR_PREFIX[] = "shell: Execution error";
const char PARENT_ERROR_PREFIX[] = "shell: Wait error";
//Options to call a wait but not actually wait for it
//Used on background child processes
const int NOWAIT_OPTIONS = WNOHANG | WUNTRACED | WCONTINUED;
static int fds[MAX_PS][2];
const bool do_pipe = (ps->pipe_right || ps->pipe_left);
const int input_index = ps_index-1;
const int output_index = ps_index;
//Only open the pipe once.
//Every process opens its output pipe
//Input pipe is one less than the output pipe
if (ps->pipe_right && !ps->file) {
if (pipe(fds[output_index]) == -1) {
perror("pipe");
return false;
}
}
int pid = fork();
if (pid > 0) { //Parent
ps->pid = pid;
if (!do_pipe) {
if (!ps->background) {
if (waitpid(pid, NULL, 0) == -1) {
perror(PARENT_ERROR_PREFIX);
return false;
}
} else {
//If we don't do a wait on all child
//processes, they become zombies
if (waitpid(pid, NULL, NOWAIT_OPTIONS) == -1) {
perror(PARENT_ERROR_PREFIX);
return false;
}
}
} else {
for (int i = 0;i <= input_index;++i) {
close(fds[i][0]);
close(fds[i][1]);
}
//Wait for the last child to finish
if (ps->pipe_left && !ps->pipe_right)
waitpid(ps->pid, NULL, 0);
}
} else if (pid == 0) { //Child
//Get our input from the pipe
if (ps->pipe_left) {
dup2(fds[input_index][0], 0);
close(fds[input_index][1]);
/*
* Don't close unused output pipes here because
* it makes the program freak out.
* */
}
//Send our output to the pipe
if (ps->pipe_right) {
if (ps->file) {
printf("shell: Parse error: Cannot have '|' after '>>'\n");
close(fds[input_index][0]);
close(fds[input_index][1]);
getchar();
exit(EXIT_FAILURE);
}
dup2(fds[output_index][1], 1);
close(fds[output_index][0]);
if (!ps->pipe_left && input_index >= 0) {
close(fds[input_index][0]);
close(fds[input_index][1]);
}
}
if (ps->file)
fds[output_index][1] = creat(ps->argv[0], 0600);
if (!ps->file) {
execvp(ps->argv[0], ps->argv);
} else {
char buff;
if (fds[output_index][1] != -1) {
while (read(fds[input_index][0], &buff, 1) > 0)
write(fds[output_index][1], &buff, 1);
close(fds[output_index][1]);
exit(EXIT_SUCCESS); //Prevent fall-through to perror
}
}
printf("\n");
//This will only be hit if execvp() or creat() fails.
perror(CHILD_ERROR_PREFIX);
if (!ps->file) {
printf("%s (cont): while attempting to execute '%s'\n",
CHILD_ERROR_PREFIX, ps->argv[0]);
} else {
printf("%s (cont): while attempting to write to '%s'\n",
CHILD_ERROR_PREFIX, ps->argv[0]);
}
exit(EXIT_FAILURE); //Kill the child
} else { //Error
if (ps->pipe_left || ps->pipe_right) {
close(fds[input_index][0]);
close(fds[output_index][1]);
}
printf("%s: fork failed\n", CHILD_ERROR_PREFIX);
return false;
}
return true;
}
process.c
Code: Select all
#include "process.h"
#include "run.h"
#include <string.h> //memset
#include <stdlib.h> //malloc
#include <stdio.h> //printf debug
//Set initial values for a process, returns a pointer to it.
Process* init_ps(Process* ps) {
if (!ps)
return NULL;
for (int i = 0;i < MAX_ARGS;++i)
ps->argv[i] = NULL;
ps->background = false;
ps->pipe_right = false;
ps->pipe_left = false;
ps->file = false;
return ps;
}
//Allocate a new process, return a pointer to it
Process* alloc_ps() {
Process* ps = (Process*) malloc( sizeof(Process) );
if (ps == NULL) {
printf("Failure allocating memory for process.\n");
exit(EXIT_FAILURE);
}
return ps;
}
//Set cur_ps as being on RHS of a pipe and prev_ps as being on LHS of a pipe
//If either are null, abort
void set_pipes(Process* cur_ps, Process* prev_ps) {
if (!cur_ps || !prev_ps)
return;
cur_ps->pipe_left = true;
cur_ps->pipe_right = false;
prev_ps->pipe_right = true;
}
//Runs num_ps ps's processes in arr
void run_all(Process** arr, size_t num_ps) {
if (!arr || arr[0] == NULL)
return;
Process* ps;
for (int i = 0;i < num_ps; ++i) {
ps = arr[i];
if (!ps)
break;
run(ps, i);
}
}
//Frees num_ps ps's processes in arr
void free_all(Process** arr, size_t num_ps) {
if (!arr || arr[0] == NULL)
return;
Process* ps;
for (int i = 0;i < num_ps;++i) {
ps = arr[i];
if (!ps)
break;
for (int j = 0;j < MAX_ARGS && ps->argv[j];++j)
free(ps->argv[j]);
free(ps);
arr[i] = NULL;
}
}
input.c
Code: Select all
/**********************
* input.c
* Steven A Wilson - CS470
* Part of shell
* Contains function definitions for parsing user input
*********************/
#include "input.h"
#include "defs.h"
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <stdlib.h>
//Modifies str, replacing \r\n with \0\0
void strip_rn(char str[]) {
char* found_endline;
//Directly modify str
found_endline = strchr(str, '\n');
if (found_endline)
*found_endline = '\0';
found_endline = strchr(str, '\r');
if (found_endline)
*found_endline = '\0';
}
/*
* Splits a string on first occurence of 'on'
* Returning results to before and after, neither
* containing 'on'
*/
void split_str(char** result, char* input, const char* delims, size_t max_args)
{
if (!input) {
result[0][0] = '\0';
return;
}
size_t i;
char* cur_token = strtok(input, delims);
//Previous result wasn't NULL
for (i = 0;i < max_args && cur_token != NULL;++i) {
result[i] = (char*)malloc(MAX_INPUT);
strncpy(result[i], trim_str(cur_token), MAX_INPUT);
cur_token = strtok(NULL, delims);
}
//Set the NULL result or final result to \0
result[i] = NULL;
}
/*
* Trims spaces off of start and end of a string
*/
char* trim_str(char* str) {
char ptr[strlen(str)+1];
int i, j = 0;
bool lead = false;
//Remove leading spaces, copy everything else.
for (i = 0; str[i] != '\0';++i) {
if (lead) {
ptr[j++] = str[i];
} else if (str[i] != ' ' && !lead) {
ptr[j++] = str[i];
lead = true;
}
}
ptr[j] = '\0';
//Remove trailing spaces.
while (ptr[--j] == ' ')
ptr[j] = '\0';
strcpy(str, ptr);
str = ptr;
return str;
}
/*
* Prints 'prompt' followed by ": "
* Waits for and stores user input in 'buffer'
* Maximum characters sent to 'buffer' is 'max'
*/
void get_input(const char* prompt, char* buffer, size_t max) {
printf("%s", prompt);
fgets(buffer, max, stdin);
strip_rn(buffer);
}
/*********
* copy_str
* Copies to 'to' from 'from' not including ch
* If ch (+ ch2, if not 0) is not found, copies entire string
* NULL-terminates 'to' if required.
* Returns one past from's ch/ch2, or NULL if not found
********/
char* copy_str(char* to, const char* from, char ch = '|', char ch2 = '\0') {
const int len_to = strlen(to);
const int len_from = strlen(from);
// const int len_match = strlen(match);
char* until = strchr(from, ch);
size_t max;
//Couldn't match or 2nd char is not a match
if (until == NULL || (ch2 && *(until+1) != ch2) ) {
max = (len_to > len_from) ? len_to : len_from;
} else {
max = (size_t)(until - from - 1); //-1: Avoid ch
until++; //Increment for retval: advance past ch
if (ch2)
until++;
}
strncpy(to, from, max);
if (to[max-1] != '\0')
to[max-1] = '\0';
return until;
}
defs.h
Code: Select all
#ifndef DEFS_H
#define DEFS_H
#define MAX_INPUT 1028
#define MAX_ARGS 128
#define MAX_PS 128
#endif
input.h
Code: Select all
/***********************************
* Part of shell
* input.h
* Contains function prototypes for editing user input
***********************************/
#ifndef INPUT_H
#define INPUT_H
#include <sys/types.h>
void strip_rn(char str[]);
void split_str(char** result, char* input, const char* delims, size_t max_args);
void get_input(const char* prompt, char* buffer, size_t max);
char* trim_str(char* str);
#endif
process.h
Code: Select all
#ifndef PROCESS_H
#define PROCESS_H
#include "defs.h"
#include "process.h"
#include <sys/types.h>
#ifndef NULL
#define NULL 0
#endif
struct Process {
//int fds[2]; //0 == input, 1 == output
char* argv[MAX_ARGS]; //Standard argv
int pid;
bool background; //There was a & after argv
bool file; //I'm really a file that input is coming to
bool pipe_right; //There was a pipe on the RHS of this input
bool pipe_left; // ..LHS..
};
//Set initial values for a process, returns a pointer to it
Process* init_ps(Process* ps);
//Allocate a new process
Process* alloc_ps();
//If cur_ps and prev_ps, then set cur_ps as being on RHS of pipe and prev_ps
//as being on LHS of pipe.
void set_pipes(Process* cur_ps, Process* prev_ps);
//Runs all processes in arr
void run_all(Process** arr, size_t num_ps);
//Frees all processes in arr
void free_all(Process** arr, size_t num_ps);
#endif
run.h
Code: Select all
#ifndef RUN_H
#define RUN_H
#include "process.h"
/* Forks to execute process specified by ps
* Returns true to parent on success
* Returns false ot parent on parent fail
* Exits child on child fail
*/
bool run(Process* ps, int ps_index);
#endif