I am writing a mini-shell program. It gives me seg fault in the line "args = my_str2vect(line);". The "my_str2vect" function works fine though, as well as the my_strncpy function and my_strlen function(returns the length of the char array). So what's wrong with that line?
#define _POSIX_SOURCE
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>
#include "../../include/my.h"
pid_t pid;
int childrunning = 0;
void minishell_loop();
/*
Your shell must also support CTRL+C by using UNIX signals.
If you type CTRL+C your prompt must not exit
and instead kill the currently running process (or do nothing if there is none).
*/
void int_handler(int sig){
if(childrunning == 1){
kill(pid, SIGUSR2);
}else{
my_str("\n");
minishell_loop();
}
}
void killkillkill(int sig){
exit(0);
}
/*
Function Declarations for builtin shell commands:
*/
int minishell_cd(char **args);
int minishell_help(char **args);
int minishell_exit(char **args);
/*
List of builtin commands, followed by their corresponding functions.
*/
char *builtin_str[] = {
"cd",
"help",
"exit"
};
int (*builtin_func[]) (char **) = {
&minishell_cd,
&minishell_help,
&minishell_exit
};
int minishell_mum_bultins() {
return sizeof(builtin_str) / sizeof(char *);
}
/*
Builtin function implementations.
*/
/**
#brief Bultin command: change directory.
#param args List of args. args[0] is "cd". args[1] is the directory.
#return Always returns 1, to continue executing.
*/
int minishell_cd(char **args)
{
if (args[1] == NULL) {
fprintf(stderr, "minishell: expected argument to \"cd\"\n");
} else {
if (chdir(args[1]) != 0) {
perror("minishell");
}
}
return 1;
}
/**
#brief Builtin command: print help.
#param args List of args. Not examined.
#return Always returns 1, to continue executing.
*/
int minishell_help(char **args)
{
int i;
printf("Tianwei's minishell, version 1.01\n");
printf("These shell commands are defined internally.\n");
for (i = 0; i < minishell_mum_bultins(); i++) {
printf(" %s\n", builtin_str[i]);
}
return 1;
}
/**
#brief Builtin command: exit.
#param args List of args. Not examined.
#return Always returns 0, to terminate execution.
*/
int minishell_exit(char **args)
{
return 0;
}
/**
#brief Launch a program and wait for it to terminate.
#param args Null terminated list of arguments (including program).
#return Always returns 1, to continue execution.
*/
int minishell_launch(char **args)
{
signal(SIGUSR2, killkillkill);
int status;
pid = fork();
if (pid == 0) {
// Child process
childrunning = 1;
if (execvp(args[0], args) == -1) {
perror("minishell");
}
exit(EXIT_FAILURE);
} else if (pid < 0) {
// Error forking
perror("minishell");
} else {
// Parent process
do {
waitpid(pid, &status, WUNTRACED);
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
return 1;
}
/**
#brief Execute shell built-in or launch program.
#param args Null terminated list of arguments.
#return 1 if the shell should continue running, 0 if it should terminate
*/
int minishell_execute(char **args)
{
int i;
my_str(args[0]);
if (args[0] == NULL) {
my_str("args[0] is NULL\n");
// An empty command was entered.
return 1;
}
for (i = 0; i < minishell_mum_bultins(); i++) {
if (strcmp(args[0], builtin_str[i]) == 0) {
my_str("1\n");
return (*builtin_func[i])(args);
}
}
my_str("0\n");
return minishell_launch(args);
}
#define MINISHELL_RL_BUFSIZE 1024
/**
#brief Read a line of input from stdin.
#return The line from stdin.
*/
char *minishell_readln(void)
{
int bufsize = MINISHELL_RL_BUFSIZE;
int position = 0;
char* buffer = malloc(1024 * sizeof(char));
int c;
while (1) {
// Read a character
c = getchar();
// If we hit EOF, replace it with a null character and return.
if (c == EOF || c == '\n') {
buffer[position] = '\0';
return buffer;
} else {
buffer[position] = c;
}
position++;
// If we have exceeded the buffer, reallocate.
if (position >= bufsize) {
fprintf(stderr, "minishell: Command line too long!\n");
exit(EXIT_FAILURE);
}
}
}
/**
#brief Loop getting input and executing it.
*/
void minishell_loop(void)
{
signal(SIGINT, int_handler);
char* line;
char** args;
int status;
do {
printf("MINISHELL: /home/tianwei/$ ");
line = minishell_readln();
my_str("0\n");
my_str(line);
args = my_str2vect(line);
my_str(args[0]);
my_str("0\n");
status = minishell_execute(args);
my_str("1\n");
free(line);
free(*args);
free(args);
} while (status);
}
/**
#brief Main entry point.
#param argc Argument count.
#param argv Argument vector.
#return status code
*/
int main(int argc, char **argv)
{
// Load config files, if any.
// Run command loop.
minishell_loop();
// Perform any shutdown/cleanup.
return EXIT_SUCCESS;
}
This is the code for my_str2vect function
#include "../../include/my.h"
char** my_str2vect(char* str){
// Takes a string
// Allocates a new vector (array of string ended by a NULL),
// Splits apart the input string x at each space character
// Returns the newly allocated array of strings
// Any number of ' ','\t', and '\n's can separate words.
// I.e. "hello \t\t\n class,\nhow are you?" -> {"hello", "class,", "how", "are","you?", NULL}
int max_num_words = 0;
int a = 0;
int b = 0;
int max_num_char = 0;
while(str[a] != '\0'){ // find the number of words and the length of the longest word
if(str[a] != ' ' && str[a] != '\t' && str[a] != '\n'){
++max_num_words;
++a;
++b;
while((str[a] != ' ' && str[a] != '\t' && str[a] != '\n') && str[a] != '\0'){
++a;
++b;
}
if(b > max_num_char){
max_num_char = b;
}
b = 0;
while((str[a] == ' ' || str[a] == '\t' || str[a] == '\n') && str[a] != '\0'){
++a;
}
}
}
char** output = (char **)malloc(sizeof(char *) * (max_num_words + 1)); // Allocate a 2D array first.
for(int c = 0; c < max_num_words + 1; ++c){
output[c] = (char *)malloc(sizeof(char) * (max_num_char + 1));
}
int i = 0;
int j = 0;
while(i < my_strlen(str) && j < max_num_words){ // Put the characters into the 2D array.
int k = 0;
while(i < my_strlen(str) && (str[i] == ' ' || str[i] == '\t' || str[i] == '\n')){
++i;
}
while(i < my_strlen(str) && (!(str[i] == ' ' || str[i] == '\t' || str[i] == '\n'))){
++i;
++k;
}
if(i-k < my_strlen(str) && j < max_num_words){
my_strncpy(output[j], &str[i-k], k);
}
++j;
}
output[j] = NULL;
return output;
}
This is my_strncpy function
#include "../../include/my.h"
char *my_strncpy(char *dst, char *src, int n)
{
/* Same as my_strcpy except:
* Only copies n chars or until the end of src*/
if(src != NULL && dst != NULL){
int i = 0;
while(i < n && src[i] != '\0'){
dst[i] = src[i];
++i;
}
dst[i] = '\0';
}
return dst;
}
I didn't try to execute your code but here is what strikes me at first sight in the my_str2vect function:
in the first while loop, a is only incremented in the if. You are likely to meet a forever loop if your line begins with a space
in the second while loop, you don't check for the end of string '\0'. This may well be the reason for your crash, but there may be others.
PS: since you are parsing a string, you should take a look at strtok and sscanf, they would ease your life
Related
When I run my code the first printParams() call works perfectly. But every call after fork() the struct loses all of its char array values. I'm not that knowledgeable of pointers, but I can tell that the root of this problem is probably pointer-based. For example, the first printParams() will print out all of the values assigned in the Parse() function. But after the fork(), all of the integer values such as background and argumentCount are displayed but none of the string values associated with inputRedirect or the string values held in the vectorArguments array.
![Here is a photo of my output]]1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include "parse.h"
void printParams(Param_t * param);
struct PARAM
{
char *inputRedirect; /* file name or NULL */
char *outputRedirect; /* file name or NULL */
int background; /* ethier 0 (false) or 1 (true) */
int argumentCount; /* number of tokens in argument vector
*/
char *argumentVector[MAXARGS]; /* array of String */
};
typedef struct PARAM Param_t;
int main(int argc, char *argv[]){
int i;
int debug;
pid_t pid;
if(argc>1){
if(!strcmp(argv[1], "-debug"))
debug = 1;
}
Param_t * testParam = Parse();
if(debug == 1){
printParams(testParam);
}
pid = fork();
printParams(testParam);
if(pid == 0){
exit(1);
}
return 0;
}
void printParams(Param_t *param)
{
int i;
printf("InputRedirect: [%s]\n", (param->inputRedirect != NULL) ? param-
>inputRedirect: "NULL");
printf("OutputRedirect: [%s]\n", (param->outputRedirect != NULL) ?
param->outputRedirect: "NULL");
printf ("Background: [%d]\n", param->background);
printf ("ArgumentCount: [%d]\n", param->argumentCount);
for (i = 0; i < param->argumentCount; i++)
printf("ArgumentVector[%2d]: [%s]\n", i, param->argumentVector[i]);
}
Param_t* Parse(){
char *toke[MAXARGS];
int i = 0;
char str[MAXSTRLENGTH];
int j;
int k=0;
Param_t* testParam = malloc(sizeof(Param_t));
testParam->argumentCount = 0;
printf("Enter your commands:\n");
fgets(str, MAXSTRLENGTH, stdin);
toke[i] = strtok(str, " ");
//Tokenizes the user input into the toke array
while(toke[i] != NULL){
//printf("%s\n", toke[i]);
++i;
toke[i] = strtok(NULL, " ");
}
i=0;
char c;
while(toke[i] != NULL){
c = toke[i][0];
if(c == '<')
{
for(j=0; j<strlen(toke[i]); ++j ){
toke[i][j] = toke[i][j+1];
}
testParam->inputRedirect = toke[i];
}
else if(c == '>')
{
for(j=0; j<strlen(toke[i]); ++j ){
toke[i][j] = toke[i][j+1];
}
testParam->outputRedirect = toke[i];
}
else if(c == '&')
{
testParam->background = 1;
//background
}
else
{
testParam->argumentVector[k] = toke[i];
k++;
//save as cmd vector
}
++i;
}
testParam->argumentCount = k;
return testParam;
}
The reason you loose all char * values is because strtok() function doesn't create buffer. Basically all your char* consist of an address pointing into str variable you read with fgets(). The str variable has scope only to end of Parse() function.
Solution for this:
Replace:
testParam->inputRedirect = toke[i];
with:
testParam->inputRedirect = malloc(MAXSTRLENGTH);
memset( testParam->inputRedirect, 0, MAXSTRLENGTH);
memcpy( testParam->inputRedirect, toke[i], strlen(toke[i]) );
But please note, that this leads to memory leak since there is no free().
Sugestion:
Create static instance of structure in main and give its pointer to Parse function.
Param_t testParam;
Parse( &testParam );
Let the Parse function fill it. And at the end of main call free for all char * buffers inside testParam
My situation is the output print "p2: " each time the child processes.
When I type a command line "echo NULL; echo void". My parse() will split each word and assign to *newargv[] by using getword() function.
The getword(char *w) is basically split each word in command line. In my situation, the ";" is treat as a newline character. Getword(char * w) will return '\0' when it encounters the semi-colon.
The output when I run the program.
p2: echo NULL;echo void
NULL
void
p2: p2:
I thought it will need to flush output buffer and tried fflush(stdout). But it doesn't work.
Here is my p2.c and p2.h
#include <stdio.h>
#include "p2.h"
#define MAXITEM 100 /* max number of words per line */
int BGFLAG = 0, INFLAG = 0, OUTFLAG = 0, AMFLAG = 0, SEMIFLAG = 0, PIPEFLAG = 0;
char argv[255]; //this array get a string from getword function
char *newargv[MAXITEM]; //this pointer array will keep the first character of argv array
char *in, *out; //pointers point to file in argv
int sizeC = 0; //check if it is EOF or not
int parse();
int main()
{
int argc;
signal(SIGTERM,myhandler);
for (;;)
{
printf("p2: ");
pid_t pid, child_pid;
int status;
//call parse function
argc = parse();
if (sizeC == -1)
break;
if (argc == 0) //reissue prompt if line is empty
continue;
if (newargv[0] == NULL){
fprintf(stderr, "Invalid command");
continue;
}
child_pid = fork();
if (child_pid < 0){
printf("ERROR! can't fork\n");
exit(1);
}
else if(child_pid == 0){ //return a new child process
execvp(*newargv,newargv);
printf("ERROR exec failed\n");
exit(1);
}
else {
pid = wait(NULL);
if (pid == child_pid)
continue;
}
}//end for
killpg(getpgrp(),SIGTERM);
printf("p2 terminated. \n");
exit(0);
}// main
int parse()
{
int p = 0;
//this pointer will keep track the argv array for each of loops
//the getword function will not overwrite the argv array
int ptr = 0;
int count = 0;
SEMIFLAG = 0;
int wCount = 0; //return the number of argc
int semiColon = 0;
/* Read from stdin to argv array. The ptr will keep track the argv array
If it is meta character, set a flag appropriately. Otherwise,
set the address of first char of argv to the newargv*/
while ((sizeC = getword(argv + ptr)) > 0)
{
if(sizeC == 0){
semiColon++;
continue;
}else{
//Put the address of first char of each argv to the pointer array
newargv[p] = argv + ptr;
p++;
}
argv[ptr + sizeC] = '\0';
//point to the next address of next word in argv, the getword will not overwrite the argv array
ptr = ptr + sizeC + 1;
wCount++;
}//end while
newargv[p] = NULL;
return wCount;
}//end parse
void myhandler(){
}//end myhandler
Here p2.h
#include <stdio.h>
#include "getword.h"
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <dirent.h>
#include <signal.h>
#define MAXITEM 100
int parse();
void myhandler();
Here getword.c and getword.h
#include <stdio.h>
#include "getword.h"
int getword(char *w)
{
int count = 0; //represent how many characters are read
char iochar;
int flag = 0; // identify a pair of single quote
//set an infinite loop
while (1)
{
iochar = getchar(); //This variable stores a character from stdin
/* If the array is full, the character will put back for next call.
Return (STORAGE -1) characters*/
if (count >= (STORAGE -1))
{
ungetc(iochar,stdin);
w[count] = '\0';
return count;
}
/* This block code will eleminate the leading tab */
if (iochar == '\t') //ignore the tabs if it counters
continue;
/* Identify if the function hit EOF */
if (iochar == EOF)
{
/* Return an empty string and -1 for size of string array
Because the EOF put back when count > 0 and the getword() encounters right away at next call
Therefore, the count is 0.*/
if (count == 0)
{
w[count] = '\0';
return -1;
}
/* The getword() read some characters before hitting EOF
Set a null terminator to finish a string array.
Return the size of string array.
Put the EOF back to stdin for next call
to print the EOF*/
if (count > 0)
{
w[count] = '\0';
ungetc(iochar,stdin);
return count;
}
}
/* For backslash case, treat all metacharacter
and space character as a normal character
The ";" and newline char will not effect meaning of that newline
*/
if (iochar == '\\')
{
//identify next character is meta-char,
//or normal char or a single quote
char nextC = getchar();
/* Only put a meta-character or space into the array
the blackslash is ignored
flag = 0 means the SINGLE QUOTE MOD is OFF*/
if ((nextC == '\'' || nextC == '!' || nextC == '>' ||
nextC == '&' || nextC == ' ' || nextC == '\\' || nextC == '\t')
&& flag == 0)
{
w[count++] = nextC;
continue;
}
/* As in a pair of single quote
slash and meta char both put in the array
flag = 1 means the SINGLE QUOTE MOD is ON.
The metacharacter and backslash are treat as normal char*/
else if ( (nextC == '!' || nextC == '>' ||
nextC == '&' || nextC == ' ' || nextC == '\\' || nextC ==';')
&& flag == 1)
{
w[count++] = iochar;
w[count++] = nextC;
continue;
}
//the single quote character in a pair of single quote
//treat as a normal character
else if (nextC == '\'' && flag == 1)
{
w[count++] = nextC;
continue;
}
//return the string if it encounters new line
//put nextC back to stdin for next call
//because we need to print out to determine the new line exist
else if (nextC == '\n' || nextC == ';')
{
w[count] = '\0';
ungetc(nextC,stdin);
return count;
}
else
//the normal character is put into the string array
{
w[count++] = nextC;
continue;
}
}// end if blacknextC
/* Identify Space case
Treat a space char as a normal char if it's in a pair of single quotes.
Treat a space char as a delimeter if it's not in a pair of single quotes*/
if (iochar == ' ')
{
if (flag == 1) //SINGLE QUOTE MOD is ON
{
w[count++] = iochar; //space is treat as normal character instead of a delimeter
continue;
}
else if (count == 0)//ignore if it is leading char or space char
continue;
else
{
/*Set a null delimeter and return the size of the array
This case space is a delimeter*/
w[count] = '\0';
return count;
}
}// end if space
/* This block of codes will identify the single quote case*/
if (iochar == '\'')
{
//read the character after single quote to determine
//it is a newline or normal character or metacharacter
char c = getchar();
/* Detect the open quote
If it is not newline or delimeter char, put it back to stdin for next call
and move on*/
if (flag == 0 && (c != '>' || c != '<' || c != '!' || c != '&'
|| c != '|' || c != ' ' || c != ';' || c !='\n'))
{
flag = 1;
ungetc(c,stdin);
continue;
}
/* Detect the closed quote. Set flag on.
Put the character back to stdin and move on*/
else if (flag == 1 && (c != '>' || c != '<' || c != '!' || c != '&' || c != '|' || c != ' ' || c != ';' || c !='\n'))
{
//Set single quote mod back to normal(OFF) (flag = 0)
//get the character back to stdin for next call to read as normal character
flag = 0;
ungetc(c,stdin);
continue;
}
/* if it hit a new line, set a null delimeter to terminate the array
return size of the string array.
the newline char is put back to stdin to print out nextcall*/
else
{
w[count] = '\0';
ungetc(c,stdin);
return count;
}
}//end if single quote
/* This code handle when the character is meta-character
It is considered as a delimeter character */
if (iochar == '>' || iochar == '<' || iochar == '!'
|| iochar == ';' || iochar == '\n' || iochar == '&' || iochar == '|')
{
/* Special character ">!"
Need to read next character to identify "!" */
if (iochar == '>')
{
char c = getchar();
/* return the string if is "!". It becomes a delimeter
put two characters back to stdin for next getword() call.
Return the size of string array before the special char*/
if ( c == '!' && count > 0)
{
w[count] = '\0';
ungetc(c,stdin);
ungetc(iochar,stdin);
return count;
}
//Return size and the special character ">!"
if (c == '!' && count == 0)
{
w[count++] = iochar; //iochar = ">"
w[count++] = c; //c = "!"
w[count] = '\0';
return count;
}
/* Put c and iochar back to stdin for next call
make sure in order, ">" need to be read first to print out the ">" char
The delimeter is only ">". Return the size of string array before ">"*/
if ( c != '!' && count > 0)
{
ungetc(c,stdin);
ungetc(iochar,stdin);
w[count] = '\0';
return count;
}
//put the ">" in to the array
//make sure put the non-"!" back to stdin for next call
//Return the meta-character ">" and size = 1
if ( c != '!' && count == 0)
{
ungetc(c,stdin);
w[count++] = iochar;
w[count] = '\0';
return count;
}
}
/* This code identify when the character is a new line "\n" or ";" */
if (iochar == '\n' || iochar == ';')
{
if (count == 0) // return an empty string
{
w[count] = '\0';
return 0;
}
/* Return a string array after newline
Put newline back to stdin for next call
to print out*/
if (count > 0)
{
w[count] = '\0';
ungetc(iochar,stdin);
return count;
}
}
/* This code block handle the rest of the metacharacter.
Return the size of 1 and that metacharacter if count = 0.
Set null delimeter and return string array before the metacharacter.
Put the metacharacter back to stdin for next call to print out.*/
if (iochar == '<' || iochar == '!' || iochar == '&' || iochar == '|')
{
//return the delimeter and size of 1.
if (count == 0)
{
w[count++] = iochar;
w[count] = '\0';
return count;
}
/* Set null delimiter to and return size of 1 and string array.
put the meta-character back to stdin for next call to get the meta-character */
if (count > 0)
{
w[count] = '\0';
ungetc(iochar,stdin);
return count;
}
}
}//end if meta case
/* After handling all situation, this character is normal.
Put the normal character to the string array */
w[count++] = iochar;
}//end while
}// getword
getword.h
#include <stdio.h>
#include <string.h>
#include <strings.h>
#define STORAGE 255
/* This is one more than the max wordsize that getword() can handle */
int getword(char *w);
the following code:
corrects the problems listed in the comments and exposed by the compiler.
cleanly compiles
documents why each header file is being included
keeps data declarations local to where they are used
is not a complete answer because you have not indicated what you want to trigger the code to exit.
And now the code:
#include <stdio.h> // perror(), printf()
#include <stdlib.h> // exit(), EXIT_FAILURE
#include <unistd.h> // execvp(), fork(), pid_t
#include <sys/types.h>
#include <sys/wait.h> // waitpid()
char **parse( void );
int main( void )
{
for (;;)
{
printf("p2: ");
//pid_t pid;
pid_t child_pid;
//int status;
//call parse function
char **argv = parse();
child_pid = fork(); //fork a child process
if(child_pid < 0)
{ // then error occurred
perror( "fork failed" );
//printf("ERROR: can't fork! exec failed\n");
//exit(1);
exit( EXIT_FAILURE );
}
else if (child_pid == 0)
{ // then child process
execvp( argv[0], argv );
perror( "execvp failed" );
exit(1);
}
else
{ // parent process
int status;
waitpid( child_pid, &status, 0);
}
}//end for
}// main
I need to get input from user and deal with variables. I need to have next features:
set varname = somevalue: set the value of the environment variable named varname to the value specified by somevalue.
delete varname: remove the named environment variable.
print varname: prints out the current value of the named environment variable.
What I have till now is this:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <readline/readline.h>
#include <readline/history.h>
int main(int argc, char **argv) {
char * s;
char * command;
char * varName;
char * varValue;
while (s = readline("prompt> ")) {
/* Initialise char* variables */
command = NULL;
varName = NULL;
varValue = NULL;
add_history(s); /* adds the line to the readline history buffer */
printf("> %s\n", s); //print > sign
int cmdNo = 1;
int i;
// parse through entire string
for (i = 0; i < strlen(s); i++)
{
// check for space or = and jump over it
while ((isspace(s[i]) || s[i] == '=') && (i < strlen(s)))
{
i++;
}
// check if i is greater than string size
if (i >= strlen(s))
{
printf("Bad command format!\n");
break;
}
// if cmdNo == 1, get the command
if (cmdNo == 1)
{
int commandSize = 0;
int index = i;
// point index to space
while (!isspace(s[index]))
{
commandSize++;
index++;
}
// get command
command = (char*)malloc(commandSize + 1);
int destIndex = 0;
// copy command into command array
while (i<index)
{
command[destIndex] = s[i];
destIndex++;
i++;
}
// adding terminate character
command[destIndex] = '\0';
// increase command number by 1
cmdNo++;
}
// if cmdNo == 2 we deal with variable name
else if (cmdNo == 2)
{
// variable name size
int varNameSize = 0;
int index = i;
// point index to space
while (!isspace(s[index]))
{
varNameSize++;
index++;
}
// get var name
varName = (char*)malloc(varNameSize + 1);
int index2 = 0;
while (i<index)
{
varName[index2] = s[i];
index2++;
i++;
}
// add terminate char
varName[index2] = '\0';
// increment cmdNo by 1
cmdNo++;
}
// if cmdNo == 3 we deal with variable value
else if (cmdNo == 3)
{
int valueSize = 0;
int index = i;
// point index to space
while (!isspace(s[index]) && s[index] != '\0')
{
valueSize++;
index++;
}
// get variable value
varValue = (char*)malloc(valueSize + 1);
int index2 = 0;
while (i<index)
{
varValue[index2] = s[i];
index2++;
i++;
}
// add terminate char
varValue[index2] = '\0';
}
}
// print command, variable name and value
if (command != NULL)
{
printf("%s", command);
}
if (varName != NULL)
{
printf(" %s", varName);
}
if (varValue != NULL)
{
printf(" %s\n", varValue);
}
/* clean up! */
free(s);
free(command);
free(varName);
free(varValue);
}
return(0);
}
Obviously, I had to put somewhere putenv(), setenv() or clearenv(). I don't have much experience with these commands.
Also, there is some error (Segmentation fault). This is response of system
The crash is caused by your loops while (!isspace(s[index])) for cmdNo 1 and 2 -- if there is no third (or second) word on the input line, they'll run past the NUL terminator in the string and (probably) crash. You need a check for NUL in these loops as you check in cmdNo 3 case.
You also have a problem if you have more than 3 words on the input line -- you'll go into an infinite loop on the 4th word.
Rather than duplicating the code for the word in if/else if/else if as you have done, its much better to put the words in an array. You could even use strtok_r or strsep rather than manually parsing out the characters.
I'm able to reverse the array fine, but I can't get the program to terminate when I do CTRL+D(EOF) in terminal.
The only way I can get the program to terminate is if the very first thing I do after compiling is doing CTRL+D. But if I type in one string, then CTRL+D will not work after that.
I'm not quite sure where my error is.
#include <stdio.h>
#define MAXLINE 1000 // Maximum input.
// ----------------- reverseLine -----------------
// This method reads in chars to be put into an
// array to make a string. EOF and \n are the
// delimiters on the chars, then \0 is the
// delimiter for the string itself. Then the
// array is swapped in place to give the reverse
// of the string.
//------------------------------------------------
int reverseLine(char s[], int lim)
{
int c, i, newL;
// c is the individual chars, and i is for indices of the array.
for (i = 0; i < lim - 1 && (c=getchar()) != EOF && c != '\n'; ++i)
{
s[i] = c;
}
if (c == '\n') // This lets me know if the text ended in a new line.
{
newL = 1;
}
// REVERSE
int toSwap;
int end = i-1;
int begin = 0;
while(begin <= end) // Swap the array in place starting from both ends.
{
toSwap = s[begin];
s[begin] = s[end];
s[end] = toSwap;
--end;
++begin;
}
if (newL == 1) // Add the new line if it's there.
{
s[i] = '\n';
++i;
}
s[i] = '\0'; // Terminate the string.
return i;
}
int main()
{
int len;
char line[MAXLINE];
while ((len = reverseLine(line, MAXLINE)) > 0) // If len is zero, then there is no line to recored.
{
printf("%s", line);
}
return 0;
}
The only thing I can think of is the while loop in main checks if len > 0, so if I type EOF, maybe it can't make a valid comparison? But that wouldn't make sense as to why it works when that's the first and only thing I type.
Your program will never read the EOF because of this condition:
(c=getchar()) != EOF && c != '\n';
As soon as c is equal to '\n' the loop terminates and all the following characters are ignored. I think you should separate input from line reversing and make the usual checks on the reverse function parameters.
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SIZE_MAX (256U)
static bool linereverse(char *line);
static bool deletenewline(char *s);
int main(void)
{
char buff[SIZE_MAX];
bool success;
(void) fputs("Enter a string: ", stdout);
if( NULL == fgets(buff,(size_t) SIZE_MAX, stdin))
{
(void) fputs("Error: invalid input!\n",stderr);
return EXIT_FAILURE;
}
success = deletenewline(buff);
if(false == success)
{
(void) fputs("Error: cannot remove newline\n",stderr);
return EXIT_FAILURE;
}
success = linereverse(buff);
if(false == success)
{
(void) fputs("Error: cannot reverse the line");
return EXIT_FAILURE;
}
(void) fputs("The line reversed is: ", stdout);
(void) fputs(buff, stdout);
(void) puchar('\n');
return EXIT_SUCCESS;
}
static bool linereverse(char *line)
{
size_t i;
size_t j;
char tmp;
if(NULL == line)
{
return false;
}
i = 0;
j = strlen(line) - 1;
while(i < j)
{
tmp = line[i];
line[i] = line[j];
line[j] tmp;
++i;
--j;
}
return true;
}
static bool deletenewline(char *s)
{
char *p;
if(NULL == s)
{
return false;
}
p = strrchr(s,'\n');
if(NULL != p)
{
*p = '\0';
}
return true;
}
I'm trying to write a VERY basic shell program in C. The problem I am facing is trying to fill my argv array of character pointers with the words taken from input. When I attempt to print out the contents of the argv array after attempting to fill it using the parse() function below I get a segmentation fault. I know this means that I am probably trying to access part of the argv array that is out of bounds. However, even when supplying only one argument to fill the array, I still get the segfault. The printf call used to print argc returns the correct value for argc based on input, but the second printf call with *argv[0] is the one causing the segfault. I am wondering if my error is in the way I am attempting to print the contents of argv, or if the error is because I am attempting to fill argv incorrectly.
EDIT: I should add that the getword() function takes in a line of text and returns the first word delimited by spaces, and a number of other delimiters. I can post all the delimiters it breaks the words up by if necessary, but I do not think the problem is because of getword().
EDIT 2: Added in the header file and included the #include statement in main.
EDIT 3: Added the getword function under main(), and getword.h below p2.h
Here is p2.h, the header file included in main:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "getword.h"
#include <signal.h>
#define MAXITEM 100
getword.h:
#include <stdio.h>
#include <string.h>
#include <strings.h>
#define STORAGE 255
int getword(char *w);
int parse(char *, char *[]);
Here is the main function :
#include "p2.h"
int main() {
pid_t pid, child_pid;
int argc, inputRedirect;
char *devNull;
devNull = (char *) malloc(10);
strcpy(devNull, "/dev/null");
char *argv[MAXITEM];
char commandLine[STORAGE];
for (;;) {
printf("p2: ");
scanf("%s", commandLine);
argc = parse(commandLine, argv);
printf("argc = %d\n", argc);
if(argc == 0)
continue;
printf("*argv = %s\n", *argv[0]);
child_pid = fork();
if (child_pid < 0) {
printf("Cannot fork! Terminating...");
exit(1);
} else if (child_pid == 0) {
inputRedirect = open(devNull, O_RDONLY);
dup2(inputRedirect, STDIN_FILENO);
close(inputRedirect);
execvp(*argv, argv);
}
else {
for(;;) {
pid = wait(NULL);
if(pid == child_pid)
break;
}
printf("Child's pid is %d\n", child_pid);
}
}
killpg(getpid(), SIGTERM);
printf("p2 Terminated.\n");
exit(0);
}
int parse(char *commandLine, char *argv[]) {
int i, argc = 0;
char *commandPointer = commandLine;
while (*commandPointer != '\0') {
*argv = commandPointer;
argc++;
getword(commandPointer);
}
*commandPointer = '\0';
*argv = '\0';
return argc;
}
getword.c:
#include "getword.h"
#include <stdlib.h>
/*Function Prototypes*/
int tilde(char *p, int i);
int BSFollowedByMetaCharacter(int c, char *w);
int getword(char *w) {
int c;
int index = 0;
/*This while loop removes all leading blanks and whitespace characters
* The if statement then tests if the first character is a new line or
* semicolon metacharacter*/
while ((c = getchar()) == ' ' || c == '\t' || c == '\n' || c == ';') {
if (c == '\n' || c == ';') {
w[index] = '\0';
return 0;
}
}
/*This if statement calls ungetc() to push whatever character was taken
* from the input stream in the previous while loop back to the input
* stream. If EOF was taken from the input stream, ungetc() will return EOF,
* which will then cause getword() to return -1, signalling that it reached
* the End Of File. */
if (ungetc(c, stdin) == EOF)
return -1;
/*This if statement deals with some of the "non-special" metacharacters.
* If one of these metacharacters is pulled from the input stream by getchar(),
* it is stored in w and null-terminated. getword() then returns the length of
* the current string stored in w. If getchar() pulls anything besides one of the
* specified metacharacters from the input stream, it is then returned using ungetc() after
* the if statement.*/
if ((c = getchar()) == '<' || c == '>' || c == '|' || c == '&') {
w[index++] = c;
int d = getchar();
if (c == '>' && d == '>')
w[index++] = d;
else {
ungetc(d, stdin);
}
w[index] = '\0';
return index;
}
ungetc(c, stdin);
/*This while statement handles plain text from the input stream, as well as a few 'special'
* metacharacters. It also ensures that the word scanned is shorter than STORAGE-1 bytes.*/
while ((c = getchar()) != ' ' && c != '<' && c != '>' && c != '|'
&& c != ';' && c != '&' && c != '\t' && c != '\n' && c != '\0'
&& index <= STORAGE - 1) {
if (c == '~') {
int *ip = &index;
index = tilde(&w[index], *ip);
continue;
}/*END IF*/
else if (c == '\\') {
int d = c;
c = getchar();
if (BSFollowedByMetaCharacter(c, w)) {
w[index++] = c;
continue;
} else {
w[index++] = d;
}
}/*END ELSE IF*/
w[index] = c;
index++;
}/*END WHILE*/
ungetc(c, stdin);/*This final ungetc() call is used to push any meta characters*/
w[index] = '\0'; /*used as delimiters back to the input stream, to be retrieved*/
return index; /*at the next call of getword(). */
}/*END getword()*/
int tilde(char *cp, int i) {
int *ip;
ip = &i;
char *p = cp;
char *o;
o = (strcpy(p, getenv("HOME")));
int offset = strlen(o);
*ip = *ip + offset;
return i;
}
int BSFollowedByMetaCharacter(int c, char *w) {
if (c == '~' || c == '<' || c == '>' || c == '|' || c == ';' || c == '&'
|| c == ' ' || c == '\t' || c == '\\') {
return 1;
} else {
return 0;
}
}
The functions in getword.c seems correct. Your problem is in function parse.
To use execvp, contents of argv should be following (input:"hello world"):
argv[0] -> "hello"
argv[1] -> "world"
argv[2] -> NULL
Here, argv is an array of character pointers. But, in parse function, you are treating argv like simple character pointers in here:
*argv = commandPointer;
and here:
*argv = '\0';
Change your parse function into something like this:
int parse(char *commandLine, char *argv[]) {
int argc = 0;
char *commandPointer;
argv[argc++] = commandLine;
do{
commandPointer = (char*)malloc(sizeof(char) * STORAGE);
argv[argc++] = commandPointer;
getword(commandPointer);
}while(*commandPointer != '\0');
argv[argc] = NULL;
return argc;
}
Now, you should free allocated memory after if-else tree like:
for(int i = 0; i < argc; i++) free(argv[i]);