This question already has answers here:
C 2D array Memory allocation
(2 answers)
Closed 1 year ago.
I am writing a basic shell program in C for an assignment. I am currently having troubles allocating the memory that will be used to store the command line arguments that will be passed into the program to be parsed.
I am getting a segmentation fault if the input size is greater than or equal to 4 (i.e. "this is a test" will produce a segmentation fault upon program terminations whereas "this is a" will not).
I imagine my issue lies within how I was allocating the memory but the program is capturing each token that I enter and printing them to the screen.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{
char cap[] = "";
char* cmd;
const int MAX_ARGS = 16;
const int MAX_ARG_SIZE = 256;
char** arglst = (char**)malloc(MAX_ARGS * sizeof(char*));
for(int i = 0; i < MAX_ARGS; i++)
{
arglst[i] = (char*)malloc(MAX_ARG_SIZE * sizeof(char));
}
pid_t cpid;
//implement ls, cd, mkdir, chdir, rm, rmdir
while(1)
{
printf("Enter a command: ");
scanf("%[^\n]s", cap);
int index = 0;
cmd = strtok(cap, " ");
while( cmd != NULL )
{
strcpy(arglst[index], cmd);
cmd = strtok(NULL, " ");
index++;
}
for(int i = 0; i < index; i++)
{
printf("%s\n", arglst[i]);
}
printf("%d\n", index);
/*
if(strcmp(cap, "quit") == 0) exit(EXIT_SUCCESS);
if( (cpid = fork()) == -1) perror("fork()");
else if(cpid == 0)
{
if( execvp(cmd, arglst) == -1 )
{
errorp("cmd error");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
else
{
cpid = wait(NULL);
strcpy(cmd, "/bin/");
}
*/
for(int i = 0; i < index; i++)
{
free(arglst[i]);
}
free(arglst);
return 0;
}
}
There are a number of bugs.
This won't compile (e.g. errorp instead of perror).
cap is too small to contain a line. Better to use (e.g.) char cap[1000];
Doing a preallocate of each arglst[i] once before the main loop is problematic. One of the cells has to get a NULL value so it works with execvp. However, doing so would cause a memory leak. The solution is to use strdup inside the strtok loop so that cells are only allocated when needed.
Also, because arglst[i] is set only once during initialization, doing a loop with free near the bottom causes UB [accessing the buffer after being freed]. This is fixed with the use of strdup below.
The commented out code references variables (e.g. cmd and cap) that should not be relied upon. At that point, cmd will be NULL, causing a segfault.
The return 0; is placed incorrectly. Only one iteration (and thus only one command) will be executed.
The final free of arglst (e.g. free(arglst)) is done inside the outer loop, so referencing it on the second iteration is UB.
There are a few more issues [annotated below]
Here's a refactored version. It fixes the bugs and is heavily annotated.
I've used the preprocessor to show old/original code vs new/fixed code:
#if 0
// old code
#else
// new code
#endif
Likewise, using #if 1 for purely new code.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#if 1
#include <sys/wait.h>
#endif
int
main(void)
{
// NOTE/BUG: this must be large enough to contain a command line
#if 0
char cap[] = "";
#else
char cap[1000];
#endif
char *cmd;
const int MAX_ARGS = 16;
char **arglst = malloc(MAX_ARGS * sizeof(*arglst));
// NOTE/BUG: because we need to add a NULL terminator, don't preallocate the
// elements -- we'll leak memory
#if 0
const int MAX_ARG_SIZE = 256;
for (int i = 0; i < MAX_ARGS; i++) {
arglst[i] = (char *) malloc(MAX_ARG_SIZE * sizeof(char));
}
#endif
pid_t cpid;
// implement ls, cd, mkdir, chdir, rm, rmdir
while (1) {
printf("Enter a command: ");
// NOTE/BUG: this didn't work too well
#if 0
scanf("%[^\n]s", cap);
#else
fgets(cap,sizeof(cap),stdin);
cap[strcspn(cap,"\n")] = 0;
#endif
int index = 0;
cmd = strtok(cap, " ");
while (cmd != NULL) {
// NOTE/BUG: we should strdup dynamically rather than preallocate -- otherwise,
// we leak memory when we set the necessary NULL pointer below
#if 0
strcpy(arglst[index], cmd);
#else
arglst[index] = strdup(cmd);
#endif
cmd = strtok(NULL, " ");
index++;
}
// NOTE/FIX: we have to add a NULL terminator before passing to execvp
#if 1
arglst[index] = NULL;
#endif
for (int i = 0; i < index; i++) {
printf("%s\n", arglst[i]);
}
printf("%d\n", index);
// NOTE/BUG: we can't [shouldn't] rely on cap here
#if 0
if (strcmp(cap, "quit") == 0)
exit(EXIT_SUCCESS);
#else
if (strcmp(arglst[0], "quit") == 0)
exit(EXIT_SUCCESS);
#endif
if ((cpid = fork()) == -1)
perror("fork()");
else if (cpid == 0) {
// NOTE/BUG: cmd will be NULL here
#if 0
if (execvp(cmd, arglst) == -1) {
errorp("cmd error");
exit(EXIT_FAILURE);
}
#else
if (execvp(arglst[0], arglst) == -1) {
perror("cmd error");
exit(EXIT_FAILURE);
}
#endif
// NOTE/BUG: this will never be executed
#if 0
exit(EXIT_SUCCESS);
#endif
}
else {
cpid = wait(NULL);
// NOTE/BUG -- cmd is NULL and this serves no purpose
#if 0
strcpy(cmd, "/bin/");
#endif
}
// NOTE/BUG: in the _old_ code that did a single preallocate of these cells
// _before_ the loop, freeing them here is wrong -- they would never be
// reallocated because -- the fix using strdup alleviates the issue
for (int i = 0; i < index; i++) {
free(arglst[i]);
}
// NOTE/BUG: freeing this is wrong because we do the allocation only _once_
// above the outer loop
#if 0
free(arglst);
#endif
// NOTE/BUG -- this should be placed at the end to allow multiple commands --
// here it stops after the first command is input
#if 0
return 0;
#endif
}
// NOTE/FIX: correct placement for the above
#if 1
free(arglst);
return 0;
#endif
}
Here's that version cleaned up so that only the fixed code remains:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
int
main(void)
{
char cap[1000];
char *cmd;
const int MAX_ARGS = 16;
char **arglst = malloc(MAX_ARGS * sizeof(*arglst));
pid_t cpid;
// implement ls, cd, mkdir, chdir, rm, rmdir
while (1) {
printf("Enter a command: ");
fgets(cap,sizeof(cap),stdin);
cap[strcspn(cap,"\n")] = 0;
int index = 0;
cmd = strtok(cap, " ");
while (cmd != NULL) {
arglst[index] = strdup(cmd);
cmd = strtok(NULL, " ");
index++;
}
arglst[index] = NULL;
for (int i = 0; i < index; i++) {
printf("%s\n", arglst[i]);
}
printf("%d\n", index);
if (strcmp(arglst[0], "quit") == 0)
exit(EXIT_SUCCESS);
if ((cpid = fork()) == -1)
perror("fork()");
else if (cpid == 0) {
if (execvp(arglst[0], arglst) == -1) {
perror("cmd error");
exit(EXIT_FAILURE);
}
}
else {
cpid = wait(NULL);
}
for (int i = 0; i < index; i++) {
free(arglst[i]);
}
}
free(arglst);
return 0;
}
Note that the above does not check for the number of actual arguments exceeding MAX_ARGS.
While we could add that check, a better way is to use realloc on arglst to dynamically increase it, so an arbitrary limit on the number of arguments isn't needed
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
int
main(void)
{
char cap[1000];
char *cmd;
char **arglst = NULL;
int argmax = 0;
pid_t cpid;
// implement ls, cd, mkdir, chdir, rm, rmdir
while (1) {
printf("Enter a command: ");
fgets(cap,sizeof(cap),stdin);
cap[strcspn(cap,"\n")] = 0;
int index = 0;
cmd = strtok(cap, " ");
while (cmd != NULL) {
if (index >= argmax) {
argmax += 10;
arglst = realloc(arglst,sizeof(*arglst) * (argmax + 1));
}
arglst[index] = strdup(cmd);
cmd = strtok(NULL, " ");
index++;
}
arglst[index] = NULL;
for (int i = 0; i < index; i++) {
printf("%s\n", arglst[i]);
}
printf("%d\n", index);
if (strcmp(arglst[0], "quit") == 0)
exit(EXIT_SUCCESS);
if ((cpid = fork()) == -1)
perror("fork()");
else if (cpid == 0) {
if (execvp(arglst[0], arglst) == -1) {
perror("cmd error");
exit(EXIT_FAILURE);
}
}
else {
cpid = wait(NULL);
}
for (int i = 0; i < index; i++) {
free(arglst[i]);
}
}
free(arglst);
return 0;
}
The original code used malloc and/or strdup on the individual elements of arglst (e.g. arglst[i]).
This makes the code general enough to be used in more complex scenarios. But, as the code is written, the malloc/strdup for the individual elements really isn't necessary.
This is because the cells are fully used [up] at the bottom of the main loop, so we don't need to save them.
We can reuse the cap buffer space on each loop iteration because we do not need any tokens to live on iteration to iteration.
We can simply store the return value from strtok and simplify the code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
int
main(void)
{
char cap[1000];
char *cmd;
char **arglst = NULL;
int argmax = 0;
pid_t cpid;
// implement ls, cd, mkdir, chdir, rm, rmdir
while (1) {
printf("Enter a command: ");
fgets(cap,sizeof(cap),stdin);
cap[strcspn(cap,"\n")] = 0;
int index = 0;
cmd = strtok(cap, " ");
while (cmd != NULL) {
if (index >= argmax) {
argmax += 10;
arglst = realloc(arglst,sizeof(*arglst) * (argmax + 1));
}
arglst[index] = cmd;
cmd = strtok(NULL, " ");
index++;
}
arglst[index] = NULL;
for (int i = 0; i < index; i++) {
printf("%s\n", arglst[i]);
}
printf("%d\n", index);
if (strcmp(arglst[0], "quit") == 0)
exit(EXIT_SUCCESS);
if ((cpid = fork()) == -1)
perror("fork()");
else if (cpid == 0) {
if (execvp(arglst[0], arglst) == -1) {
perror("cmd error");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
else {
cpid = wait(NULL);
}
}
free(arglst);
return 0;
}
Related
I have a large file (around 1,000,000 characters) in the format "AATACGTAGCTA" and a subsequent file, such as "CGTATC" (10,240 characters). I want to find the largest match of the subsequence within the main sequence. A full, 100% subsequence match may not exist, this is not guaranteed. For the sake of a smaller example, the above would output: Longest match is 4/6 starting at position 5.
I'm working on my C basics, and would like to implement it like so:
The user chooses how many processes they would like to split the work
into.
Each process does 1/nth of the work and updates the shared memory
values located in the struct.
The longest match (it may not be all characters) is reflected in the
struct, as well as it's starting position, and how many
characters were matched. See output below.
Code
#define _GNU_SOURCE
#include <limits.h>
#include <stdio.h>
#include <errno.h>
#include <semaphore.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/shm.h>
typedef struct memoryNeeded {
int start_pos, total_correct;
char sequence[1038336];
char subsequence[10240];
sem_t *sem;
} memoryNeeded;
// Used to check all arguments for validity
int checkArguments(char* p, int argc) {
char *prcs;
errno = 0;
int num;
long conv = strtol(p, &prcs, 10);
if (errno!= 0 || *prcs != '\0' || conv > INT_MAX || conv > 50) {
puts("Please input a valid integer for number of processes. (1-50)");
exit(1);
} else {
num = conv;
if (argc != 4) {
puts("\nPlease input the correct amount of command line arguments (4) in"
"the format: \n./DNA (processes) (sequence) (subsequence)\n");
exit(1);
} else
printf("Looking for string using %d processes...\n", num);
return(num);
}
}
int main (int argc, char* argv[]) {
int processes = checkArguments(argv[1], argc);
key_t shmkey;
int procNumber, shmid, pid;
FILE *sequence;
FILE *subsequence;
char *buf1, *buf2;
// Create shared memory
size_t region_size = sizeof(memoryNeeded);
shmkey = ftok("ckozeny", 5);
shmid = shmget(shmkey, region_size, 0644 | IPC_CREAT);
if (shmid < 0) {
perror("shmget\n");
exit(1);
}
// Create structure in shared memory, attach memory and open semaphore
memoryNeeded *mn;
mn = (memoryNeeded *)shmat(shmid, NULL, 0);
mn->sem = sem_open("sem", O_CREAT | O_EXCL, 0644, 1);
sequence = fopen(argv[2], "r");
subsequence = fopen(argv[3], "r");
// Get file sizes
fseek(sequence, 0L, SEEK_END);
int sz1 = ftell(sequence);
rewind(sequence);
fseek(subsequence, 0L, SEEK_END);
int sz2 = ftell(subsequence);
rewind(subsequence);
// Read files into 2 buffers, which are put into struct mn
buf1 = malloc(sz1);
buf2 = malloc(sz2);
if (sz1 != fread(buf1, 1, sz1, sequence)) {
free(buf1);
}
if (sz2 != fread(buf2, 1, sz2, subsequence)) {
free(buf2);
}
// Initialize struct with necessary values
mn->start_pos = 0;
mn->total_correct = 0;
strncpy(mn->sequence, buf1, sz1);
strncpy(mn->subsequence, buf2, sz2);
fclose(sequence);
fclose(subsequence);
// Begin n forks
for (procNumber = 0; procNumber < processes; procNumber++) {
pid = fork();
if (pid < 0) {
sem_unlink("sem");
sem_close(mn->sem);
printf ("Fork error.\n");
} else if (pid == 0)
break;
}
if (pid != 0) {
while ((pid = waitpid (-1, NULL, 0))){
if (errno == ECHILD)
break;
}
printf("Best match is at position %d with %d/10240 correct.", mn->start_pos, mn->total_correct);
printf ("\nParent: All children have exited.\n");
sem_unlink("sem");
sem_close(mn->sem);
shmdt(mn);
shmctl(shmid, IPC_RMID, 0);
exit(0);
} else {
// this child process will do its 1/nth of the work
sem_wait(mn->sem);
printf ("Child(%d) is in critical section.\n", procNumber);
sleep(1);
int i = 0;
int longest, count = 0;
for (i = 0; i < sz1; i += processes) {
for (int j = 0; j < sz2; j += processes) {
count = 0;
while (mn->sequence[i+j] == mn->subsequence[j]) {
count++;
j++;
}
if (count > longest) {
longest = count;
}
}
}
// If local match is longer than that of the struct, update and unlock
if (longest > mn->total_correct) {
mn->total_correct = count;
mn->start_pos = (i - count);
sem_post(mn->sem);
} else
// If not - unlock and let next process go
sem_post(mn->sem);
exit(0);
}
return 1;
}
The current child code is more or less "pseudocode". I've put it together how it makes sense in my head. (I'm aware this may not be correct or function as intended.) My question is in regard to the child code algorithm near the bottom.
How do I implement this so each child does 1/nth of the work, and finds the longest match, even though it may not match 100%?
Final output would be:
./DNA 6 sequence1 subsequence1
Looking for string using 6 processes...
Best match is at position 123456 with 9876/10240 correct.
Thanks.
I am working on pipeline system with string in C. For example I write this line to console ./pipeline LOWERCASE REVWORD SQUASHWS < stringFile.txt. It should be any like this P0 -> P1 -> P2 -> ...-> Pn. P0 load string from file stringFile.txt and send it to P1.. I can work with pipe(send and read), but I dont know how to work with N processes. It should be any like this. Can you give me any advice or example? Thank you
while(is_something_to_read) {
load_from_previous_process();
do_process(); // for example LOWERCASE
send_to_next_process();
}
Patrik,
I wrote a program that simulate a shell that will spawn a given amount of children and each process will then communicate with another process. Since the number of processes can vary I decided to use a 2D array for the pipes. In the below code NUM_PROCS refers to the amount of processes that will be running (including the parent).
I declare it
int pipes[NUM_PROCS][2];
After this, I create the pipes
for(i = 0; i < NUM_PROCS; i++)
{
if((pipe(pipes[i])) < 0)
{
perror("Failed to open pipe");
}
}
This is a shell program that i wrote for practise.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
#define MAXARGS 256
char ** getargs(char * cmd) {
char** argsarray;
int nargs = 0;
int nlen = strlen(cmd);
int i = 0;
argsarray = (char**) malloc(sizeof(char*) * MAXARGS);
argsarray[0] = strtok(cmd," ");
i = 0;
while (argsarray[i] != NULL){
i++;
argsarray[i] = strtok(NULL," ");
}
return argsarray;
}
int main(void){
pid_t childpid;
int fd[256][2];
char cmd[256];
char * sepCmd[256];
char * pch;
printf("Please enter a command sequence: \n");
gets(cmd);
printf("You have entered: %s ....%d\n", cmd,strlen(cmd));
printf("Attempting to split up command: \n");
pch = strtok (cmd, "|");
int count = 0;
while (pch != NULL && count < 256) {
printf("%s\n", pch);
sepCmd[count] = pch;
printf("The value in this array value is: %s\n", sepCmd[count]);
pch = strtok (NULL, "|");
count++;
}
char ** argue;
int k;
/* Block that deals with the first command given by the user */
k = 0;
pipe(fd[k]);
if(!fork()) {
dup2(fd[k][1], STDOUT_FILENO);
close(fd[k][0]);
argue = getargs(sepCmd[k]);
execvp(argue[0], argue);
perror(argue[0]);
exit(0);
}
/*Loop that will control all other comands except the last*/
for(k = 1; k <= count - 2; k++) {
close(fd[k-1][1]);
pipe(fd[k]);
if(!fork()) {
close(fd[k][0]);
dup2(fd[k-1][0], STDIN_FILENO);
dup2(fd[k][1], STDOUT_FILENO);
argue = getargs(sepCmd[k]);
execvp(argue[0], argue);
perror(argue[0]);
exit(0);
}
}
/*Block that will take care of the last command in the sequence*/
k = count - 1;
close(fd[k-1][1]);
if(!fork()) {
dup2(fd[k-1][0], STDIN_FILENO);
argue = getargs(sepCmd[k]);
execvp(argue[0], argue);
perror(argue[0]);
exit(0);
}
while(waitpid(-1, NULL, 0) != -1);
}
I am trying to create a basic shell for Linux using C. I have gotten it to work until I try to do output redirection and it just destroys everything. When I run this code, it goes straight to the default case of the fork(). I have no idea why. If I get rid of the for loop in the child process it works, but even with the for loop I don't understand why the child process is never even entered. If I put a print statement at the top of the child process it doesn't get printed.
When I run this in command line, I get the prompt and type something like "ls", which worked before I added the for loop, but now I just get "% am i here" and if I press enter it just keeps giving me that same line. My goal is to be able to type "ls > output" and have it work. I think the input redirection works, but honestly I haven't even played with it much because I am so utterly confused as to what is going on with the output redirection. Any help would be greatly appreciated, I've spent 4 hours on the same like 15 lines trying to get this to work.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#define HISTORY_COUNT 20
char *prompt = "% ";
int
main()
{
int pid;
//int child_pid;
char line[81];
char *token;
char *separator = " \t\n";
char **args;
char **args2;
char **args3;
char cmd[100];
char *hp;
char *cp;
char *ifile;
char *ofile;
int check;
int pfds[2];
int i;
int j;
int current = 0;
int p = 0;
//int check;
char *hist[HISTORY_COUNT];
//char history[90];
//typedef void (*sighandler_t) (int);
args = malloc(80 * sizeof(char *));
args2 = malloc(80 * sizeof(char *));
signal(SIGINT, SIG_IGN);
while (1) {
fprintf(stderr, "%s", prompt);
fflush(stderr);
if (fgets(line, 80, stdin) == NULL)
break;
// split up the line
i = 0;
while (1) {
token = strtok((i == 0) ? line : NULL, separator);
if (token == NULL)
break;
args[i++] = token;
/* build command array */
}
args[i] = NULL;
if (i == 0){
continue;
}
// assume no redirections
ofile = NULL;
ifile = NULL;
// split off the redirections
j = 0;
i = 0;
while (1) { //stackoverflow.com/questions/35569673
cp = args[i++];
if (cp == NULL)
break;
switch (*cp) {
case '<':
if (cp[1] == 0)
cp = args[i++];
else
++cp;
ifile = cp;
break;
case '>':
if (cp[1] == 0)
cp = args[i++];
else
++cp;
ofile = cp;
break;
case '|':
if(cp[1] ==0){
cp = args[i++];
if(pipe(pfds) == -1){
perror("Broken Pipe");
exit(1);
}
p = 1;
}
else{
++cp;
}
break;
default:
args2[j++] = cp;
args3[cp++] = cp
break;
}
}
args2[j] = NULL;
if (j == 0)
continue;
switch (pid = fork()) {
case 0:
// open stdin
if (ifile != NULL) {
int fd = open(ifile, O_RDONLY);
if (dup2(fd, STDIN_FILENO) == -1) {
fprintf(stderr, "dup2 failed");
}
close(fd);
}
// open stdout
if (ofile != NULL) {
// args[1] = NULL;
int fd2;
if ((fd2 = open(ofile, O_WRONLY | O_CREAT, 0644)) < 0) {
perror("couldn't open output file.");
exit(0);
}
// args+=2;
printf("okay");
dup2(fd2, STDOUT_FILENO);
close(fd2);
}
if(p == 1){ //from stackoverflow.com/questions/2784500
close(1);
dup(pfds[1]);
close(pfds[0]);
execvp(args2[0], args2);
break;
}
if(strcmp(args2[0], "cd") == 0){ //cd command
if(args2[1] == NULL){
fprintf(stderr, "Expected argument");
}
else{
check = chdir(args2[1]);
if(check != 0){
fprintf(stderr,"%s",prompt);
}
}
break;
}
execvp(args2[0], args2); /* child */
signal(SIGINT, SIG_DFL);
fprintf(stderr, "ERROR %s no such program\n", line);
exit(1);
break;
case -1:
/* unlikely but possible if hit a limit */
fprintf(stderr, "ERROR can't create child process!\n");
break;
default:
//printf("am I here");
if(p==1){
close(0);
dup(pfds[0]);
close(pfds[1]);
//execvp();
}
wait(NULL);
//waitpid(pid, 0, 0);
}
}
exit(0);
}
I added a separate argument pass to capture and remember the I/O redirections and remove them from the arg list passed to the child.
Here's the corrected code [please pardon the gratuitous style cleanup]:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
char *prompt = "% ";
int
main()
{
int pid;
//int child_pid;
char line[81];
char *token;
char *separator = " \t\n";
char **args;
char **args2;
char *cp;
char *ifile;
char *ofile;
int i;
int j;
int err;
//int check;
//char history[90];
//typedef void (*sighandler_t) (int);
args = malloc(80 * sizeof(char *));
args2 = malloc(80 * sizeof(char *));
//signal(SIGINT, SIG_IGN);
while (1) {
fprintf(stderr, "%s", prompt);
fflush(stderr);
if (fgets(line, 80, stdin) == NULL)
break;
// split up the line
i = 0;
while (1) {
token = strtok((i == 0) ? line : NULL, separator);
if (token == NULL)
break;
args[i++] = token; /* build command array */
}
args[i] = NULL;
if (i == 0)
continue;
// assume no redirections
ofile = NULL;
ifile = NULL;
// split off the redirections
j = 0;
i = 0;
err = 0;
while (1) {
cp = args[i++];
if (cp == NULL)
break;
switch (*cp) {
case '<':
if (cp[1] == 0)
cp = args[i++];
else
++cp;
ifile = cp;
if (cp == NULL)
err = 1;
else
if (cp[0] == 0)
err = 1;
break;
case '>':
if (cp[1] == 0)
cp = args[i++];
else
++cp;
ofile = cp;
if (cp == NULL)
err = 1;
else
if (cp[0] == 0)
err = 1;
break;
default:
args2[j++] = cp;
break;
}
}
args2[j] = NULL;
// we got something like "cat <"
if (err)
continue;
// no child arguments
if (j == 0)
continue;
switch (pid = fork()) {
case 0:
// open stdin
if (ifile != NULL) {
int fd = open(ifile, O_RDONLY);
if (dup2(fd, STDIN_FILENO) == -1) {
fprintf(stderr, "dup2 failed");
}
close(fd);
}
// trying to get this to work
// NOTE: now it works :-)
// open stdout
if (ofile != NULL) {
// args[1] = NULL;
int fd2;
//printf("PLEASE WORK");
if ((fd2 = open(ofile, O_WRONLY | O_CREAT, 0644)) < 0) {
perror("couldn't open output file.");
exit(0);
}
// args+=2;
printf("okay");
dup2(fd2, STDOUT_FILENO);
close(fd2);
}
execvp(args2[0], args2); /* child */
signal(SIGINT, SIG_DFL);
fprintf(stderr, "ERROR %s no such program\n", line);
exit(1);
break;
case -1:
/* unlikely but possible if hit a limit */
fprintf(stderr, "ERROR can't create child process!\n");
break;
default:
//printf("am I here");
wait(NULL);
//waitpid(pid, 0, 0);
}
}
exit(0);
}
UPDATE:
If you're still around do you think you could help me with creating a pipe?
Sure. It's too big to post here. See: http://pastebin.com/Ny1w6pUh
Wow did you create all 3300 lines?
Yes.
I borrowed xstr from another SO answer of mine [with bugfix and enhancement]. The dlk was new, but I do many of those, so was easy. Most of it was new code.
But ... It was composed of fragments/concepts I've done before: tgb, FWD, BTV, sysmagic. Notice all struct members for struct foo are prefixed with foo_ [standard for me]. The macro "trickery" with DLHDEF and DLKDEF to simulate inheritance/templates is also something I do [when necessary].
Many function vars reuse my style: idx for index var [I would never use i/j, but rather xidx/yidx], cp for char pointer, cnt for count, len for byte length, etc. Thus, I don't have to "think" about small stuff [tactics] and can focus on strategy.
The above idx et. al. is a "signature style" for me. It's not necessarily better [or worse] than others. It comes from the fact that I started using C when the linker/loader could only handle 8 character symbols, so brevity was key. But, I got used to using the shorter names. Consider which is clearer/better:
for (fooidx = 0; fooidx <= 10; ++fooidx)
Or:
for (indexForFooArray = 0; indexForFooArray <= 10; ++indexForFooArray)
I use the do { ... } while (0) to avoid if/else ladders a lot. This is called a "once through" loop. This is considered "controversial", but, in my experience it keeps the code cleaner. Personally, I've never found a [more standard] use of a do/while loop that can't be done more easily/better with a while or for loop--YMMV. In fact, a number of languages don't even have do/while at all.
Also, I use lower case unless it's a #define [or enum] which is always upper. That is, I use "snake case" (e.g. fooidx) and not "camel hump case" (e.g. indexForFooArray).
The .proto file containing function prototypes is auto-generated. This is a huge time saver. Side note: Be sure you have at least v2 from the external link as there was a bug in the Makefile. A make clean would erase the .proto. v2 won't do that
I've developed my own style over the years. Turns out that the linux kernel style was "borrowed from mine". Not actually :-) Mine came first. But ... They, in parallel, came up with something that is a 99% match to mine: /usr/src/kernels/whatever_version/Documentation/CodingStyle.
Consistency to a given style [one's own] is key. For a given function, you don't have to worry about what you'll name the variables, what indent or blank lines you'll use.
This helps a reader/new developer. They can read a few functions, see the style in play, and then go faster because all functions have similar style.
All this allows you to "go faster" and still get high quality code on the first try. I'm also quite experienced.
Also, my code comments focus on "intent". That is, what do you want the code to do in real world terms. They should answer the "what/why" and the code is the "how".
I am trying to make a simple shell program with the C language which have the options of redirecting stdin and stdout and making a pipe but it's giving me a segmentation fault error. Maybe the problem is in the getline but I'm not sure. Here is the code:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#define R 0
#define W 1
#define LINE_LEN 25
struct Job {
char* command;
char** argv;
int stdin;
int stdout;
} typedef Job;
int tokens_number = 0;
int sign_place = 0;
int contain_left = 0;
int contain_right = 0;
int contain_line = 0;
char** parse_cmdline (char * cmdline ){
char** arg = calloc(15, sizeof(char*));
char temp_cmd[LINE_LEN*10];
strcpy(temp_cmd, cmdline);
char * tmp;
tmp = strtok(temp_cmd, " ");
while(tmp != NULL) {
arg[tokens_number] = (char*) malloc(LINE_LEN * sizeof(char*));
strcpy(arg[tokens_number],tmp);
tmp = strtok(NULL, " ");
tokens_number++;
}
//LAST ELEMENT IS NULL
arg[tokens_number+1] = NULL;
return arg;
}
void check_for_special_signs(char** argv){
int i;
for(i=0; i<tokens_number; i++){
if(strcmp(argv[i], "<") == 0){
contain_left = 1;
sign_place = i;
return;
}else if(strcmp(argv[i], ">") == 0){
contain_right = 1;
sign_place = i;
return;
}else if(strcmp(argv[i], "|") == 0){
contain_line = 1;
sign_place = i;
return;
}
}
}
void fork_child(Job* my_job) {
pid_t pid = fork();
if (pid == 0) {
execv(my_job -> command, my_job -> argv);
perror(my_job -> command);
} else if (pid > 0) {
int status;
wait(&status);
} else
perror("fork");
}
char** create_argv(char** argv){
int i;
int j = 0;
char** argvs = calloc(sign_place,sizeof(char*));
if(sign_place!=0){
for(i=0; i < sign_place ; i++){
argvs[i] = (char*) malloc(sizeof(char*));
strcpy(argvs[i],argv[i]);
}
return argvs;
}else{
return argv;
}
}
void close_job(Job* my_job) {
if (my_job -> stdin != STDIN_FILENO)
close(my_job -> stdin);
if (my_job -> stdout != STDOUT_FILENO)
close(my_job -> stdout);
free(my_job);
}
int main() {
size_t s = 512;
char* buffer = malloc(s * sizeof(char));
char** sep_cmd = malloc(s * sizeof(char));
while (getline(&buffer, &s, stdin) != EOF) {
Job* my_job;
int my_pipe[2];
int in = 0;
int out = 1;
sep_cmd = parse_cmdline(buffer);
my_job->command = sep_cmd[0];
my_job->argv = sep_cmd;
my_job->stdin = in;
my_job->stdout = out;
check_for_special_signs(my_job->argv);
pid_t pid = fork();
if (pid == 0) {
if(contain_left == 1){
in = open(my_job->argv[sign_place + 1], O_RDONLY);
if(in < 0){
perror("open()");
}
my_job->argv = create_argv(my_job->argv);
my_job->stdin = in;
}else if(contain_right == 1){
out = open(my_job->argv[sign_place + 1], O_WRONLY | O_CREAT,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (out < 0)
perror("open()");
my_job->argv = create_argv(my_job->argv);
my_job->stdout = out;
}else if(contain_line == 1){
pipe(my_pipe);
if (my_job -> stdin == my_pipe[R])
close(my_pipe[W]);
else
close(my_pipe[R]);
}
execv(my_job -> command, my_job -> argv);
perror(my_job -> command);
} else if (pid > 0) {
int status;
wait(&status);
} else{
perror("fork");
}
close_job(my_job);
free(buffer);
buffer = (char*) malloc(s * sizeof(char));
}
free(buffer);
return 0;
}
That way I can't see if there are more mistakes in the code. Please if you see more mistakes list them too.
Thank you.
You forgot to allocate memory for my_job in main function
a kind user here gave me some code to work with for a command line shell, but I want it to output to stdout and stderr instead of using a screen or whatever it is doing right now. I am new to C so I don't know anything about converting it. I also need its ability to detect arrow keys preserved... I'm trying to make a simplistic bash clone. This is what I have right now, it's about 50% my code and 50% others'... yes, it is buggy. There are large sections commented out because they were no longer being used or because they were broken. Ignore them. :)
The particular difficulty is in the use of draw_frame() in main().
#include "os1shell.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h> /* standard unix functions, like getpid() */
#include <sys/types.h> /* various type definitions, like pid_t */
#include <signal.h> /* signal name macros, and the kill() prototype */
#include <ncurses/curses.h> /* a library for cursor-based programs */
#include <poll.h>
#include <termios.h>
#include <time.h>
/** VT100 command to clear the screen. Use puts(VT100_CLEAR_SCREEN) to clear
* the screen. */
#define VT100_CLEAR_SCREEN "\033[2J"
/** VT100 command to reset the cursor to the top left hand corner of the
* screen. */
#define VT100_CURSOR_TO_ORIGIN "\033[H"
struct frame_s {
int x;
int y;
char *data;
};
char* inputBuffer; /* the command input buffer, will be length 65 and null
* terminated. */
char** cmdHistory; /* the command history, will be no longer than 20
* elements and null terminated. */
int historySize = 0;
void addToHistory(char* newItem) {
char** h;
int historySize = 0;
for (historySize; historySize < 21; ++historySize) {
if (cmdHistory[historySize] == NULL) break;
}
if (historySize == 20) {
char** newPtr = cmdHistory + sizeof(char *);
free(cmdHistory[0]);
cmdHistory = newPtr;
h = (char**)realloc(cmdHistory,21*sizeof(char *));
cmdHistory = h;
cmdHistory[19] = newItem;
cmdHistory[20] = NULL;
} else {
h = (char**)realloc(cmdHistory,(historySize+2)*sizeof(char *));
cmdHistory = h;
cmdHistory[historySize] = newItem;
cmdHistory[historySize+1] = NULL;
}
}
/* Some help from http://stackoverflow.com/users/1491/judge-maygarden*/
char** getArguments(char* input) {
char** arguments;
int k = 0;
char* tokenized;
arguments = calloc(1, sizeof (char *));
tokenized = strtok(input, " &");
while (tokenized != NULL) {
arguments[k] = tokenized;
++k;
arguments = realloc(arguments, sizeof (char *) * (k + 1));
tokenized = strtok(NULL, " &");
}
// an extra NULL is required to terminate the array for execvp()
arguments[k] = NULL;
return arguments;
}
void printHistory(struct frame_s *frame) {
snprintf(frame->data, frame->x, "\n\n");
char** currCmd = cmdHistory;
while (*currCmd != NULL) {
snprintf(frame->data[(2*frame->x)], frame->x, "%s\n", *currCmd);
currCmd++;
}
snprintf(frame->data, frame->x, "\n\n");
}
/* Some help from http://stackoverflow.com/users/659981/ben*/
static int draw_frame(struct frame_s *frame) {
int row;
char *data;
int attrib;
puts(VT100_CLEAR_SCREEN);
puts(VT100_CURSOR_TO_ORIGIN);
for ( row = 0, data = frame->data;
row < frame->y;
row++, data += frame->x ) {
// 0 for normal, 1 for bold, 7 for reverse.
attrib = 0;
// The VT100 commands to move the cursor, set the attribute,
// and the actual frame line.
fprintf( stdout,
"\033[%d;%dH\033[0m\033[%dm%.*s",
row + 1,
0,
attrib, frame->x, data);
fflush(stdout);
}
return (0);
}
/* Some help from http://stackoverflow.com/users/659981/ben*/
int main(void) {
const struct timespec timeout = { .tv_sec = 1, .tv_nsec = 0 };
struct frame_s frame;
struct termios tty_old;
struct termios tty_new;
unsigned char line[65]; // the input buffer
unsigned int count = 0; // the count of characters in the buff
int ret;
struct pollfd fds[1];
sigset_t sigmask;
struct tm *tp;
time_t current_time;
cmdHistory = (char**)calloc(21,sizeof(char *)); // initialize the
// command history
cmdHistory[20] = NULL; // null terminate the history
int histInd = 0; // an index for the history for arrows
int t;
int r;
char** downTemp;
char** enterTemp;
// Set up a little frame.
frame.x = 80;
frame.y = 32;
frame.data = malloc(frame.x * frame.y);
if (frame.data == NULL) {
fprintf(stderr, "No memory\n");
exit (1);
}
memset(frame.data, ' ', frame.x * frame.y);
// Get the terminal state.
tcgetattr(STDIN_FILENO, &tty_old);
tty_new = tty_old;
// Turn off "cooked" mode (line buffering) and set minimum characters
// to zero (i.e. non-blocking).
tty_new.c_lflag &= ~ICANON;
tty_new.c_cc[VMIN] = 0;
// Set the terminal attributes.
tcsetattr(STDIN_FILENO, TCSANOW, &tty_new);
// Un-mask all signals while in ppoll() so any signal will cause
// ppoll() to return prematurely.
sigemptyset(&sigmask);
fds[0].events = POLLIN;
fds[0].fd = STDIN_FILENO;
// Loop forever waiting for key presses. Update the output on every key
// press and every 1.0s (when ppoll() times out).
do {
fd_set rdset;
int nfds = STDIN_FILENO + 1;
FD_ZERO(&rdset);
FD_SET(STDIN_FILENO, &rdset);
ret = pselect(nfds, &rdset, NULL, NULL, &timeout, &sigmask);
if (ret < 0) { // check for pselect() error.
if (errno == EINTR) {
continue;
} else {
break;
}
}
if (FD_ISSET(STDIN_FILENO, &rdset)) {
ret = read(STDIN_FILENO,&line[count],sizeof(line)-count);
// do {
// fds[0].revents = 0;
// ret = poll(fds, sizeof(fds) / sizeof(struct pollfd), 1000);
//
// if (fds[0].revents & POLLIN) {
// ret = read(STDIN_FILENO,&line[count],sizeof(line)-count);
if (ret > 0) {
line[count + ret] = '\0';
if (strcmp(&line[count], "\033[A") == 0) {
if (histInd > 0) {
--histInd;
}
count = 0;
if(cmdHistory[histInd]!=NULL) {
snprintf(&frame.data[(2*frame.x)],
frame.x,
"hist: %s",
cmdHistory[histInd]);
strcpy(line, cmdHistory[histInd]);
}
} else if (strcmp(&line[count],"\033[B")==0) {
char** downTemp = cmdHistory;
r = 0;
while (*downTemp != NULL) {
++downTemp;
++r;
}
if (histInd < r-1 && r!= 0) {
++histInd;
}
count = 0;
if(cmdHistory[histInd]!=NULL) {
snprintf(&frame.data[(2*frame.x)],
frame.x,
"hist: %s",
cmdHistory[histInd]);
strcpy(line, cmdHistory[histInd]);
}
} else if (line[count] == 127) {
if (count != 0) {
line[count] = '\0';
count -= ret;
}
snprintf(&frame.data[(2*frame.x)], frame.x, "backspace");
} else if (line[count] == '\n') {
char** arguments = getArguments(line);
snprintf( &frame.data[(2*frame.x)],
frame.x,
"entered: %s",
line);
if (count > 0) {
int hasAmpersand = 0;
char* cmd = (char*)
malloc(65*sizeof(char));
strcpy(cmd, line);
addToHistory(cmd);
/*
char* temp = cmd;
while (*temp != '\0') {
if (*temp == '&') {
hasAmpersand = 1;
}
++temp;
}
pid_t pid;
pid = fork();
if (pid == 0) {
int exeret;
exeret = execvp(*arguments,
arguments);
if (exeret < 0) {
snprintf(
&frame.data[
(2*frame.x)],
frame.x,
"Exec failed.\n\n");
exit(1);
}
} else if (pid < 0) {
snprintf(
&frame.data[
(2*frame.x)],
frame.x,
"Fork failed.\n\n");
exit(1);
} else if (pid > 0) {
if (!hasAmpersand) {
wait(NULL);
}
free(arguments);
snprintf(frame.data,
frame.x,
"\n\n");
}*/
} else {
free(arguments);
}
enterTemp = cmdHistory;
t = 0;
while (*enterTemp != NULL) {
++enterTemp;
++t;
}
if (t > histInd) histInd = t;
count = 0;
} else {
//snprintf( frame.data,
// frame.x,
// "char: %c",
// line[count]);
count += ret;
}
}
}
// Print the current time to the output buffer.
//current_time = time(NULL);
//tp = localtime(¤t_time);
//strftime( &frame.data[1 * frame.x],
// frame.x,
// "%Y/%m/%d %H:%M:%S",
// tp);
// Print the command line.
line[count] = '\0';
snprintf( frame.data,
frame.x,
"OS1Shell -> %s",
line);
draw_frame(&frame);
} while (1);
// Restore terminal and free resources.
tcsetattr(STDIN_FILENO, TCSANOW, &tty_old);
free(frame.data);
int n = 0;
while (n < 21) {
free(cmdHistory[n]);
++n;
}
free(cmdHistory);
return (0);
}
Any help getting it to act more like bash would be highly appreciated! Part of the credit is for using stderr correctly anyways, so it would definitely help to take the stdin/stdout/stderr approach.
It looks to me it already is going to STDOUT
fprintf( stdout,
"\033[%d;%dH\033[0m\033[%dm%.*s",
row + 1,
0,
attrib, frame->x, data);
fflush(stdout);