I'm learning UNIX system programming. I'm writing a simple shell application for UNIX (I'm on OS X Yosemite ver 10.10.5 and I use Xcode). I had some experience with C but not much.
Utility programs work fine and will print unicode characters (though ls prints '????' instead of it in Xcode console, but it seems to be the problem of the debugger itself).
I've made a little research and found out that strcmp() should work fine with it too, as far as it just compares bytes and looks for a zero byte in the end. Reading input should be ok too, as you just read bytes.
I've also read that unicode string shouldn't contain null bytes. However, some input will cause EXC_BAD_ACCESS when doing `strcmp().
Code:
Reading user input:
char* readCommand(void) {
int buffer_size = LINE_BUFFER_SIZE;
char *buffer = malloc(sizeof(char) * buffer_size);
int position = 0;
int character;
if(!buffer)
{
fprintf(stderr, "readCommand failed: memory allocation error");
exit(ALLOCATION_ERROR);
}
while (1) {
character = getchar();
if(character == EOF || character == '\n')
{
buffer[position] = '\0';
char* cmd = buffer;
free(buffer);
return cmd;
}
else {
buffer[position] = character;
}
if(++position >= sizeof(buffer))
{
buffer_size += LINE_BUFFER_SIZE;
buffer = realloc(buffer, sizeof(char) * buffer_size);
if(!buffer) {
fprintf(stderr, "readCommand failed: memory reallocation error");
free(buffer);
exit(ALLOCATION_ERROR);
}
}
}
return NULL;
}
Split args:
int split_string_quotes(char* source, char** argv, size_t arg_count)
{
enum split_states state = DULL;
char* p, *word_start = NULL;
int character;
int argc = 0;
for(p = source; argc < arg_count && *p != '\0'; p++)
{
character = (unsigned char) *p;
switch (state) {
case DULL:
if(isspace(character))
{
continue;
}
if(character == '"')
{
state = IN_STRING;
word_start = p+1;
continue;
}
state = IN_WORD;
word_start = p;
continue;
case IN_WORD:
if(isspace(character))
{
state = DULL;
*p = 0;
argv[argc++] = word_start;
}
continue;
case IN_STRING:
if(character == '"')
{
state = DULL;
*p = 0;
argv[argc++] = word_start;
}
continue;
}
}
if(state != DULL && argc < arg_count)
{
argv[argc++] = word_start;
}
argv[argc] = NULL;
return argc;
}
That's where strcmp is:
int shell_execute(char **args)
{
for(int i = 0; i < 3; i++)
{
if(strcmp(args[0], commands[i]) == 0)
{
return (*standardFuncs[i])(args);
}
}
shell_launch(args);
return 0;
}
And the main loop
char* current_dir = malloc(sizeof(char)*PATH_MAX);
char* args[MAX_ARGS];
char* command;
printf("dolphinShell (c) Alex Kale 2016\n");
while (1)
{
getwd(current_dir);
printf("dsh: %s-> ", current_dir);
command = readCommand();
printf("%s\n", command);
split_string_quotes(command, args, MAX_ARGS);
if(shell_execute(args) == -1) break;
}
free(current_dir);
return 0;
So, the problem is that some unicode strings I type work fine and never cause EXC_BAD_ACCESS, but when I type фывпфвыапы, for example, it breaks. I think the problem is with accessing args[0], but here's the debugger's output:
Printing description of args:
(char **) args = 0x00007fff5fbff900
*args char * 0x101800a00 0x0000000101800a00
Printing description of *(*(args)):
(char) **args = '\xd1'
So it thinks that args[0] is empty, but is it empty? Or is it confused by all the zeroes?
I'm really confused, I've spent a lot of time researching and seem to be stuck here.
I have also tried using wchar_t and wcscmp(), but it doesn't work good with execvp() and doesn't solve the problem.
I have also tried gcc -Wall -Wextra and here's the output:
main.c:53:26: warning: comparison of integers of different signs: 'int' and
'size_t' (aka 'unsigned long') [-Wsign-compare]
for(p = source; argc < arg_count && *p != '\0'; p++)
~~~~ ^ ~~~~~~~~~
main.c:92:30: warning: comparison of integers of different signs: 'int' and
'size_t' (aka 'unsigned long') [-Wsign-compare]
if(state != DULL && argc < arg_count)
~~~~ ^ ~~~~~~~~~
main.c:124:23: warning: comparison of integers of different signs: 'int' and
'unsigned long' [-Wsign-compare]
if(++position >= sizeof(buffer))
~~~~~~~~~~ ^ ~~~~~~~~~~~~~~
main.c:180:18: warning: unused parameter 'args' [-Wunused-parameter]
int dHelp(char **args)
^
main.c:203:18: warning: unused parameter 'args' [-Wunused-parameter]
int dExit(char **args)
^
main.c:210:14: warning: unused parameter 'argc' [-Wunused-parameter]
int main(int argc, const char** argv)
^
main.c:210:33: warning: unused parameter 'argv' [-Wunused-parameter]
int main(int argc, const char** argv)
^
7 warnings generated.
But I don't think that's the case (correct me if I'm wrong).
There are multiple bugs in the shown code.
char* cmd = buffer;
free(buffer);
return cmd;
This returns a pointer to a deleted char buffer. Continuing use of this pointer results in undefined behavior.
if(++position >= sizeof(buffer))
buffer is a char *. This is equivalent to:
if(++position >= sizeof(char *))
Which would be either 4 or 8 bytes, depending on your hardware platform. This needlessly resizes the buffer, every time it grows larger than 4 or 8 bytes.
You seem to believe that sizeof() gives the size of the malloc-ed buffer. It does not.
In conclusion: your overall approach here is to write a big pile of code, and then try to see if it works correctly. This is the wrong way to do it. You need to write one, small function. Such as the one that reads a line into a buffer. Test it. Verify that it works. Now that you know it works, move on and write the next small part of your overall program.
One huge bug in your code is the way you read the input. Look at this part:
if(character == EOF || character == '\n')
{
buffer[position] = '\0';
char* cmd = buffer;
free(buffer);
return cmd;
}
Here you are terminating buffer with nil, as you should. Then you assign cmd to point to the same memory as buffer, free the buffer and return the pointer to the already freed memory. If after this you use the returned pointer for anything it is not allowed and anything can happen. The memory could be reused somewhere, you'd get access violations or maybe a volcano erupts near you.
Assigning a pointer to another variable doesn't make a copy of the memory, it just makes them point at the same place. You cannot free the memory before you stop using the contents. This most likely is causing your issues.
Related
I am having a problem with my program in which I am getting a segmentation fault from strsep() which was gotten from GDB and has the error message
Program received signal SIGSEGV, Segmentation fault.
0x00002aaaaad64550 in strsep () from /lib64/libc.so.6
My code is as follows:
int split(char *string, char *commands, char *character) {
char **sp = &string;
char *temp;
temp = strdup(string);
sp = &temp;
for (int i = 0; i < 100; i++) {
commands[i] = strsep(sp, character);
if (commands[i] == '\0') {
return 0;
}
if (strcasecmp(commands[i], "") == 0) {
i--;
}
printf("%d", i);
}
return 0;
}
Any help would be greatly appreciated as I have spent hours trying to solve this problem
The arguments for the function are ("Hello World", "#", "&")
EDIT
So I have managed to get rid of the segment fault by changing the code to
int split(char* string, char* commands, char* character) {
for(int i = 0; i < 100; i++) {
commands[i] = strsep(&string, character);
if(commands[i] == '\0') {
return 0;
}
if(strcasecmp(&commands[i], "") == 0) {
i--;
}
}
return 0;
}
However now I have a new problem with commands returning a null array where every index is out of bounds.
EDIT 2
I should also clarify on what I am trying to do a bit so essentially commands is of type char* commands[100] and I want to pass it into the function when then modifies the original pointer array and store say `"Hello World"' into commands[0] then I want to modify this value outside the function.
Your usage of commands is inconsistent with the function prototype: the caller passes an array of 100 char*, commands should be a pointer to an array of char *, hence a type char **commands or char *commands[]. For the caller to determine the number of tokens stored into the array, you should either store a NULL pointer at the end or return this number or both.
Storing commands[i] = strsep(...) is incorrect as commands is defined as a char *, not a char **.
It is surprising you get a segmentation fault in strsep() because the arguments seem correct, unless character happens to be an invalid pointer.
Conversely you have undefined behavior most likely resulting in a segmentation fault in strcasecmp(commands[i], "") as commands[i] is a char value, not a valid pointer.
Here is a modified version:
// commands is assumed to point to an array of at least 100 pointers
// return the number of tokens or -1 is case of allocation failure
int split(const char *string, char *commands[], const char *separators) {
char *dup = strdup(string + strcspn(string, separators));
if (temp == NULL)
return -1;
char *temp = dup;
char **sp = &temp;
int i = 0;
while (i < 99) {
char *token = strsep(sp, separators);
if (token == NULL) // no more tokens
break;
if (*token == '\0') // ignore empty tokens
continue;
commands[i++] = token;
}
commands[i] = NULL;
if (i == 0) {
free(dup);
}
return i;
}
The memory allocated for the tokens can be freed by freeing the first pointer in the commands array. It might be simpler to duplicate these tokens so they an be freed in a more generic way.
My str_split function returns (or at least I think it does) a char** - so a list of strings essentially. It takes a string parameter, a char delimiter to split the string on, and a pointer to an int to place the number of strings detected.
The way I did it, which may be highly inefficient, is to make a buffer of x length (x = length of string), then copy element of string until we reach delimiter, or '\0' character. Then it copies the buffer to the char**, which is what we are returning (and has been malloced earlier, and can be freed from main()), then clears the buffer and repeats.
Although the algorithm may be iffy, the logic is definitely sound as my debug code (the _D) shows it's being copied correctly. The part I'm stuck on is when I make a char** in main, set it equal to my function. It doesn't return null, crash the program, or throw any errors, but it doesn't quite seem to work either. I'm assuming this is what is meant be the term Undefined Behavior.
Anyhow, after a lot of thinking (I'm new to all this) I tried something else, which you will see in the code, currently commented out. When I use malloc to copy the buffer to a new string, and pass that copy to aforementioned char**, it seems to work perfectly. HOWEVER, this creates an obvious memory leak as I can't free it later... so I'm lost.
When I did some research I found this post, which follows the idea of my code almost exactly and works, meaning there isn't an inherent problem with the format (return value, parameters, etc) of my str_split function. YET his only has 1 malloc, for the char**, and works just fine.
Below is my code. I've been trying to figure this out and it's scrambling my brain, so I'd really appreciate help!! Sorry in advance for the 'i', 'b', 'c' it's a bit convoluted I know.
Edit: should mention that with the following code,
ret[c] = buffer;
printf("Content of ret[%i] = \"%s\" \n", c, ret[c]);
it does indeed print correctly. It's only when I call the function from main that it gets weird. I'm guessing it's because it's out of scope ?
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define DEBUG
#ifdef DEBUG
#define _D if (1)
#else
#define _D if (0)
#endif
char **str_split(char[], char, int*);
int count_char(char[], char);
int main(void) {
int num_strings = 0;
char **result = str_split("Helo_World_poopy_pants", '_', &num_strings);
if (result == NULL) {
printf("result is NULL\n");
return 0;
}
if (num_strings > 0) {
for (int i = 0; i < num_strings; i++) {
printf("\"%s\" \n", result[i]);
}
}
free(result);
return 0;
}
char **str_split(char string[], char delim, int *num_strings) {
int num_delim = count_char(string, delim);
*num_strings = num_delim + 1;
if (*num_strings < 2) {
return NULL;
}
//return value
char **ret = malloc((*num_strings) * sizeof(char*));
if (ret == NULL) {
_D printf("ret is null.\n");
return NULL;
}
int slen = strlen(string);
char buffer[slen];
/* b is the buffer index, c is the index for **ret */
int b = 0, c = 0;
for (int i = 0; i < slen + 1; i++) {
char cur = string[i];
if (cur == delim || cur == '\0') {
_D printf("Copying content of buffer to ret[%i]\n", c);
//char *tmp = malloc(sizeof(char) * slen + 1);
//strcpy(tmp, buffer);
//ret[c] = tmp;
ret[c] = buffer;
_D printf("Content of ret[%i] = \"%s\" \n", c, ret[c]);
//free(tmp);
c++;
b = 0;
continue;
}
//otherwise
_D printf("{%i} Copying char[%c] to index [%i] of buffer\n", c, cur, b);
buffer[b] = cur;
buffer[b+1] = '\0'; /* extend the null char */
b++;
_D printf("Buffer is now equal to: \"%s\"\n", buffer);
}
return ret;
}
int count_char(char base[], char c) {
int count = 0;
int i = 0;
while (base[i] != '\0') {
if (base[i++] == c) {
count++;
}
}
_D printf("Found %i occurence(s) of '%c'\n", count, c);
return count;
}
You are storing pointers to a buffer that exists on the stack. Using those pointers after returning from the function results in undefined behavior.
To get around this requires one of the following:
Allow the function to modify the input string (i.e. replace delimiters with null-terminator characters) and return pointers into it. The caller must be aware that this can happen. Note that supplying a string literal as you are doing here is illegal in C, so you would instead need to do:
char my_string[] = "Helo_World_poopy_pants";
char **result = str_split(my_string, '_', &num_strings);
In this case, the function should also make it clear that a string literal is not acceptable input, and define its first parameter as const char* string (instead of char string[]).
Allow the function to make a copy of the string and then modify the copy. You have expressed concerns about leaking this memory, but that concern is mostly to do with your program's design rather than a necessity.
It's perfectly valid to duplicate each string individually and then clean them all up later. The main issue is that it's inconvenient, and also slightly pointless.
Let's address the second point. You have several options, but if you insist that the result be easily cleaned-up with a call to free, then try this strategy:
When you allocate the pointer array, also make it large enough to hold a copy of the string:
// Allocate storage for `num_strings` pointers, plus a copy of the original string,
// then copy the string into memory immediately following the pointer storage.
char **ret = malloc((*num_strings) * sizeof(char*) + strlen(string) + 1);
char *buffer = (char*)&ret[*num_strings];
strcpy(buffer, string);
Now, do all your string operations on buffer. For example:
// Extract all delimited substrings. Here, buffer will always point at the
// current substring, and p will search for the delimiter. Once found,
// the substring is terminated, its pointer appended to the substring array,
// and then buffer is pointed at the next substring, if any.
int c = 0;
for(char *p = buffer; *buffer; ++p)
{
if (*p == delim || !*p) {
char *next = p;
if (*p) {
*p = '\0';
++next;
}
ret[c++] = buffer;
buffer = next;
}
}
When you need to clean up, it's just a single call to free, because everything was stored together.
The string pointers you store into the res with ret[c] = buffer; array point to an automatic array that goes out of scope when the function returns. The code subsequently has undefined behavior. You should allocate these strings with strdup().
Note also that it might not be appropriate to return NULL when the string does not contain a separator. Why not return an array with a single string?
Here is a simpler implementation:
#include <stdlib.h>
char **str_split(const char *string, char delim, int *num_strings) {
int i, n, from, to;
char **res;
for (n = 1, i = 0; string[i]; i++)
n += (string[i] == delim);
*num_strings = 0;
res = malloc(sizeof(*res) * n);
if (res == NULL)
return NULL;
for (i = from = to = 0;; from = to + 1) {
for (to = from; string[to] != delim && string[to] != '\0'; to++)
continue;
res[i] = malloc(to - from + 1);
if (res[i] == NULL) {
/* allocation failure: free memory allocated so far */
while (i > 0)
free(res[--i]);
free(res);
return NULL;
}
memcpy(res[i], string + from, to - from);
res[i][to - from] = '\0';
i++;
if (string[to] == '\0')
break;
}
*num_strings = n;
return res;
}
This question already has answers here:
Strcmp pointer from integer without cast
(4 answers)
Closed 7 years ago.
I'm writing a method that splits a given array into 2 parts. It keeps the first half in the original array and puts the other half into a temporary array using a symbol that marks where the split is. I'm getting erros as follows:
Warning: passing argument 2 of ‘strcmp’ makes pointer from integer without a cast
if(strcmp(input[i], symbol) == 0){
^
In file included from process.c:7:0:
/usr/include/string.h:144:12: note: expected ‘const char *’ but argument is of type ‘char’
extern int strcmp (const char *__s1, const char *__s2)
^
process.c:95:16: warning: assignment makes integer from pointer without a cast
input[i] = NULL;
^
process.c:96:26: warning: comparison between pointer and integer
while(input[i + 1] != NULL){
^
process.c:98:22: warning: assignment makes integer from pointer without a cast
input[i + 1] = NULL;
^
Here is my code:
char *splitCommands(char *input, char symbol){
int i = 0, j = 0;
char *temp;
int symbIndex; // where the symbol was
while(input[i] != NULL){
if(strcmp(input[i], symbol) == 0){
symbIndex = i;
input[i] = NULL;
while(input[i + 1] != NULL){
temp[j] = input[i + 1];
input[i + 1] = NULL;
i++;
j++;
}
break;
}
i++;
}
return temp;
}
You can directly do this comparison
if (input[i] == symbol)
because input[i] and symbol have type char and hence you compare their values, when the are strings you compare addresses which is why you need strcmp().
And
input[i + 1] = NULL;
is equivalent to
input[i + 1] = (void *) 0;
hence, you are trying to assign a pointer, to a char, which is also wrong, perhaps you mean
input[i + 1] = 0;
or in a more c-ish fashion
input[i + 1] = '\0';
Also, the while condition, the same problem, just
while (input[i])
would be enough, if you like your code to be more readable, I personally think that,
while (input[i] != '\0')
is more readable.
And you can't use temp like that because it's uninitialized, you need to allocate space for it, like this
temp = malloc(1 + estimatedSizeInBytes);
if (temp == NULL)
return NULL;
/* add the '\0' terminator */
temp[realSize - 1] = '\0';
where realSize <= estimatedSize.
You should also remember to free() the pointer returned by this function when you finish using it.
input[i] and symbol are char. So campare by imput[i] == symbol. And don't forget to allocate memory for char *tmp
There are several problems with your code but one of the primary problems and reason for your compiler errors is that strcmp() takes two pointers to c style strings as arguments. You are passing two char's to it, instead of pointers.
Another problem is that you're assigning a char to NULL, however NULL should only be used for pointers, not other types like char/int/float. If you're trying to check for the end of a c style string, check if the char is equal to 0 or '\0' instead. You'll probably have to spend more time learning about c style strings and pointers before you can finish this program.
I won't complete your assignment/work for you but I will give you an example of how to use strcmp():
#include <string.h>
#include <stdio.h>
int main()
{
char String1[4] = "abc";
char String2[4] = "xyz";
int Result = strcmp(String1, String2);
if (Result < 0)
printf("%s < %s\n", String1, String2);
if (Result == 0)
printf("%s = %s\n", String1, String2);
if (Result > 0)
printf("%s > %s\n", String1, String2);
return 0;
}
Output: abc < xyz
Here's an additional strcmp() reference from cplusplus.com.
Can someone tell me how to correct this warning/error. I am trying to get just the first character of a string to tell if it a "-".
Error:
grep-lite.c:15:13: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
if(strcmp((char *) pattern[0],"-") == 0)
^
grep-lite.c:29:16: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
while(strcmp((char *) argv[index][0],"-"))
^
Source with warnings/errors:
Line 15:
if (strcmp((char *) pattern[0],"-") == 0)
Line 29:
while (strcmp((char *) argv[index][0],"-"))
Complete Source:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "grep-lite.h"
int main(int argc, char * * argv)
{
//initailize variables
int index = 1, lineNumber = 1;
int oneMatchingLine = FALSE;
int invertOpt = FALSE, lineNumOpt = FALSE, quietOpt = FALSE;
char * pattern = argv[argc];
//check if last arguement is invalid by starting with a '-'
if(strcmp((char *) pattern[0],"-") == 0)
{
error(INVALID_PATTERN);
return EXIT_ERROR;
}
//check if they asked for help
if(strcmp(argv[index],"--help") == 0)
{
printHelpStatement();
return EXIT_SUCCESS;
}
//walk through all options
while(strcmp((char *) argv[index][0],"-"))
{
//find and set option
if(processOptions(argv[index], &invertOpt, &lineNumOpt, &quietOpt))
index++;
//if invalid option fai
else
{
error(INVALID_OPTION);
return EXIT_ERROR;
}
}
//walk through stdinput searching for pattern relationship
while(feof(stdin) == 0)
{
//initialize
char * thisLine = NULL;
// get read line with fgets
thisLine = fgets(thisLine, MAX_CHARACTERS, stdin);
//find pattern location in thisLine
char * patternLoc = strstr(thisLine, pattern);
//check if we should print this line based of patternLoc and invertOpt
if((!patternLoc != NULL && !invertOpt) || (pattenLoc == NULL && invertOpt))
{
//see if we should print this line
if(!quietOpt)
printLine(thisLine, lineNumOpt, lineNumber);
}
lineNumber++;
}
I will enumerate the problems I find in your code
The correct usage of strcmp() exists in your code, in this line
if (strcmp(argv[index],"--help") == 0)
strcmp() is intended for string comparison, not character comparison, this
if(strcmp((char *) pattern[0],"-") == 0)
should be
if (pattern[0] == '-')
do not cast the variable to force compilation, instead find the actual cause for the compiler error/warning.
You have a severe error, you don't allocate space for the thisLine char pointer, you must allocate memory via malloc() or just declare it as a char array like
char thisLine[SOME_CONSTANT_NUMBER];
Also, this
while(feof(stdin) == 0)
is never a good idea instead do it like this
char thisLine[100];
while (fgets(thisLine, sizeof(thisLine), stdin) != NULL)
You made another very common mistake, arrays in c are indexed from 0 to N - 1, so
char *pattern = argv[argc]
is wrong, because you are reading one element after the last, the correct code is
char *pattern = argv[argc - 1]
which will give you the last element.
I'm writing a shell program, and when passing values into execv() I need a char pointer to the name of the program (which is ls), and I need a pointer to an array of arrays of char pointers for the arguments.
I go through and parse the user's input, and if I type in ls, if I print my char *, printf() prints out "ls". So I am parsing the line correctly and storing the correct information. When I pass this to execv(), it says bad path name, but if I change my pointer manually to progs[0] = "ls", then it works. If I compare the two strings, strcmp(mypointer, "ls"), it shows that although mypointer prints out "ls", it is not equivalent to "ls".
Does anyone know why?
Here is my shell process code:
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
char **progs;
char ***arguments;
char **mode;
char pathname[] = "/bin/";
int main(int argc, char ** argv){
//printf("\n %s \n", progs);
//fflush(stdout);
char buff[100];
FILE *p;
p = fdopen(0, "r"); //opens FD 0 (Stdin) as a stream
char * pathname;
while(1){
//printf("I'm at the top");
if(isatty(1) == 1){ //check to see if stdout is going to the terminal
printstart(); //if so, print
}
fgets(buff, 100, p); // Gets the input from the stdin and puts it in buff
int processid = fork(); //fork into child to complete task
if(processid == 0){
//initialize all variables
int numcmd = countcmd(buff);
int pipes = countpipes(buff);
int i;
int j;
//allocate memory for tokenization
progs = malloc(numcmd * sizeof(char *));
arguments = malloc((numcmd) * sizeof(char *));
mode = malloc((numcmd*2) * sizeof(char *));
for(i = 0; i < numcmd; i++){
progs[i] = malloc(10* sizeof(char *));
mode[i] = malloc(10 * sizeof(char *));
mode[2*numcmd-1-i] = malloc(10 * sizeof(char*));
arguments[i] = malloc(15 * sizeof(char *));
for(j = 0; j < 15; j++){
arguments[i][j] = malloc(15 * sizeof(char*));
}
}
/////////////////////////////////////////////////////////////////////
parse(buff); //parses input and places it in the static progs
for(i = 0; i < 1; i++){
printf("\n This is progs %s", arguments[0][0]);
char temp[25];
//strcpy(temp, "/bin/");
strcpy(temp, progs[0]);
//strcat(temp, ' \0');
//*progs = "ls";
char * ptr = progs[0];
for(;*ptr != '\0';){
printf("This is what pointer poitns to %c \n", *ptr++);
}
printf("This is the program: <<%s>>", progs[0]);
fflush(stdout);
char * argument[2];
argument[0] = "ls";
argument[1] = '\0';
char * hell = "l\0";
printf("This is the value of comparison %d\n", strcmp(progs[0], hell));
char **temparg = arguments[0];
//char temp[20] = progs[0];
errno = 0;
execvp("ls", *argument);
char * error = strerror(errno);
printf("This is the error %s", error);
return;
}
}
else{
int status;
waitpid(processid, &status, WIFEXITED(status));
}
}
return 0;
}
Here is my parse() code:
#include <string.h>
#include <stdio.h>
#include "myshell.h"
int parse(char * buff){
//Initialize all variables and pointers
int cmd = 0;
int argument = 0;
int mod = 0;
int j = 0;
int hitargs = 0;
int gotcommand = 0;
int multiarg = 0;
char ** argptr = arguments[cmd];
char * ptr1 = progs[cmd];
char * argptr2 = argptr[argument];
char * ptr2 = mode[mod];
while(buff[j] != '\0'){
switch(buff[j]){
case ';':
cmd++;
argument = 0;
multiarg = 1;
*argptr2++ = '\0';
argptr = arguments[cmd];
argptr2 = argptr[argument];
ptr1 = progs[cmd];
*ptr2 = buff[j];
mod += 2;
ptr2 = mode[mod];
case ' ':
if(gotcommand == 0){
break;
}
else{
if(hitargs == 0){
hitargs = 1;
*ptr1++ = '\0';
argument++;
argptr2 = argptr[argument];
}
else{
argument++;
argptr2 = argptr[argument];
}
break;
}
default:
if(gotcommand == 0){
*ptr1++ = (char) buff[j];
*argptr2++ = buff[j];
gotcommand = 1;
}
else if(gotcommand == 1 && hitargs == 0){
*ptr1++ = (char) buff[j];
*argptr2++ = buff[j];
}
else if(gotcommand == 1 && hitargs == 1){
*argptr2++ = buff[j];
}
}
j++;
}
*argptr2++ = '\0';
*ptr1++ = '\0';
int cmdflag = 0;
int spaceflag = 0;
int argflag = 0;
int cmdct = 1; //account for null
int argumentct = 1; //account for null termination
return 1;
}
Sorry about the random printf statements.
Your main program should include myshell.h like your parsing code does.
It would be helpful to have the missing functions (such as countcmd() and countpipes() — it isn't clear precisely what those do, though we can guess a bit from their names).
You should have include <unistd.h> in your main program.
You should have been getting warnings about undeclared functions (such as fork()), and you should be heeding and fixing those warnings.
If you were not getting those warnings, you need to add more warning options to your compilation.
If you use gcc, using -Wall is a good starting point.
With the warnings enabled, you get to see:
shex.c:95: warning: passing argument 2 of ‘execvp’ from incompatible pointer type
shex.c:99: warning: ‘return’ with no value, in function returning non-void
The latter is best handled via return EXIT_FAILURE; or exit(EXIT_FAILURE);.
The former is triggered by execvp("ls", *argument);.
It might be better to use a plural word (arguments?) for something that contains many arguments.
...Oh, I see, there is a global variable arguments.
You declare char ***arguments;. Ouch! I have used a triple pointer occasionally, but only very occasionally. That's one too many levels of pointer most of the time, and especially for this exercise.
Then there's also a local variable char *argument[2];.
The correct call is, therefore, execvp("ls", argument);.
I would immediately expand on the waiting code to at least print the information:
else
{
int status;
int corpse = waitpid(processid, &status, WIFEXITED(status));
printf("Command exited: PID = %d; status = 0x%.4X\n", corpse, status);
}
In your parsing code, the case ';': drops through into the case ' ':. If that's intentional, document it (with a comment such as /* DROP THROUGH */); if not, insert the missing break. Your default: case should probably have a break after it too. It isn't quite crucial, but it is conventional and protects you from drop through if a new case needs to be handled.
The string of five variables declared at the end of parse() are superfluous; they're local variables that are never used (as the compiler tells you).
At the top of the main() you do:
FILE *p;
p = fdopen(0, "r"); //opens FD 0 (Stdin) as a stream
This is not necessary; stdin is already open as a stream.
You read from p (aka stdin) with fgets(). On the whole, this is good (better than gets()), but you need to be aware that fgets() includes a newline which gets() does not, and you need to check the return status (you might not read anything). You should also use sizeof():
if (fgets(buff, sizeof(buff), stdin) == 0)
...error - read failed...
The memory allocation is extraordinarily complex. For a fixed size input like char buff[100];, you can afford to use non-dynamic allocation. There can't be more than about 50 arguments total, since they're blank separated.
Etc.
not looked into the entire code so there may be other bugs but ith regard to the 'ls' issue you need to add the following to the parse code :
while (buff[j] != '\0' && buff[j] != '\n')
this ensures you don't add 'newline' to the command pointer at *ptr1++ = (char) buff[j];
Note fgets if it encounters new line would stop reading but would include it as part of the string.