-1

Here is a shell program I wrote for honing my understanding on C pointer and array. This shell program has the functionalities of reading in commands (execute them in execvp()) and the history feature can store 10 commands (similar to Linux terminal) where "!!" returns latest command and execute, !Nth returns Nth command and execute, otherwise read command and store them in history array. When requested history command doesn't exist, relevant error message printed. For full code reference, see Shell_Program_C.

initialize() method initialize each history character string array with letter 'e' ('e' means empty or string hasn't been assigned) so that I could later check that history string has value or not. readHist() method assign each individual command to the history string array, char** hist which has a size of 10.

My problem is:strcpy(hist[histC], tokens[count])in readHist() is returning 'bad access' here.

Expected behavior: each command should be copied to char** hist as a string of char so that later requested command can be retrieved and executed.

char** initialize() {
char** hist = malloc(10 * sizeof(char *));
for (int i = 0; i < 10; ++i) {
        hist[i] = (char *)malloc((MAX_LINE / 2) + 1);
    for(int j = 0; j < (MAX_LINE / 2); j++) {
        hist[i][j] = 'e';
    }
}
return hist;
}


void readHist(char**hist, int histC ,char** tokens, int count) {
histC = (histC - 1) % 10;
for(int i = 0; i < count; i++) {
    size_t strlenth = strlen(tokens[i]);
    //strcat(hist[histC], tokens[count]);
    if(count > 1) {
       strcat(hist[histC], tokens[count]);
       hist[histC][strlenth] = ' ';
    } else {
        printf("histC%d", histC);
       strcpy(hist[histC], tokens[count]); // bad access or segmentation fault
       hist[histC][strlenth] = '\0';
    }
}
}
Nate Lee
  • 17
  • 7
  • 1
    Standard error. Function parameters are pass by value in C. `hist = malloc(10 * sizeof(char *));` That only sets the *local* `hist` variable. The caller does not see that. One way is to pass in a pointer to the pointer. That is `char ***hist` and then in function do `*hist = ...` – kaylum May 20 '17 at 22:51
  • Or `char **initialize(char** hist)` and `return` (and assign) the pointer. (e.g. `return hist` and then in the caller `char **yourpointer = initialize (yourpointer)`;) You can omit the parameter from the declaration if you like and simply assign the return. – David C. Rankin May 20 '17 at 22:56
  • @DavidC.Rankin, your suggestion does fix the problem where `char** hist` is no longer null after `initialize()` is called and assigned to `char**hist` in main() method. Can you explain why we are able to access local `char** hist` from `initialize()`? I thought `char**hist` is initialize() would be only accessible within the function. Also, strcpy() still resulted in "Bad Access" in xcode after I modified `initialize()` as suggested. – Nate Lee May 20 '17 at 23:10
  • You are confusing returning a *local* variable from a function (which you cannot do since the function stack frame is destroyed on return) and allocating a block of memory and returning a pointer to that block (which does survive the function return since the block of memory is created in memory on the heap which is outside the function stack frame). So `char **foo = malloc (sizeof *foo);` creates a block of memory that is not effected by the return, allowing you to assign the return to your variable in the calling function. `:)` – David C. Rankin May 20 '17 at 23:29
  • Your bad access isn't related to the **allocation** of `hist` if you have modified your code as suggested. I suspect you have not stored a pointer to a valid string as `hist[histC]` or `tokens[count]` that is causing the problem. You will need to post a [**Minimal, Complete, and Verifiable example**](http://stackoverflow.com/help/mcve) for further help, we have know way of knowing what values/pointers you are trying to access. (I'll look at your link, but your code should be part of the question -- which may be part 2 to this question) – David C. Rankin May 20 '17 at 23:33
  • Why would you `initialize(hist);` within a `do` loop? Unless you `realloc` you create a memory leak each time you reassign the value of `hist`. I haven't stepped completely through all the logic. Since your initialization has changed, you may need to post an updated MCVE. – David C. Rankin May 21 '17 at 00:01
  • @DavidC.Rankin, I just realized I did not mean to initialize `char**hist` every time I iterate in the Do-While loop. I edited the post and 'bad access' is still the problem that crash my program when I try to assign each individual array of command to my `char** hist` array of string of history. – Nate Lee May 21 '17 at 01:37
  • That looks better, I'll look though your revised [**Shell_Program_C**](https://pastebin.com/6N9gShnJ) and see if anything pops out. – David C. Rankin May 21 '17 at 03:58
  • You have a number of problems to fix. How is the update to `count` in `checkAmp` ever seen in `stringTok`? Perhaps you meant `void checkAmp (int *count, ...`? You have bigger problems with the way you call `strcat` after passing duplicates arguments to `histCheck`. – David C. Rankin May 21 '17 at 07:09
  • @alk, the question you marked this being a duplicate of has absolutely nothing to do with the source or origin of the `SegFault`. (there are other problems with the question -- granted) But look though the answer and compare to the Question you linked to. I don't see a resemblance. This is a debugging and parameter passing error, not an allocation error. – David C. Rankin May 21 '17 at 18:49
  • @DavidC.Rankin: This looks like the common issues of a question being edited significantly after having been posed (please see it's history) ... *sigh* – alk May 22 '17 at 05:12
  • Fair enough. Agreed, it is time for any subsequent issues to be posted as a new question. Thanks for looking at it. – David C. Rankin May 22 '17 at 07:44

1 Answers1

0

Your SegFault is due to violating the strcat requirement that "The strings may not overlap" This looks like a careless oversight that has big consequences. Specifically, look at cmdargv below:

histCheck(cmdargv, &histCount, cmdargv, count, &isAmp);

You are passing the same array of pointers cmdargv as the parameters that are passed to hist and tokens in your call to:

void histCheck(char**hist, int* histCount, char**tokens, int tcount, int* amp)

In histCheck both hist and tokens point to the same memory that can be seen in a debugger, e.g.

histCheck (hist=0x7fffffffd9b0, histCount=0x7fffffffd9a8, tokens=0x7fffffffd9b0

note the address of both hist and tokens.

This same error is then passed to readHist, e.g.

readHist (hist=0x7fffffffd9b0, histC=1, tokens=0x7fffffffd9b0

which results in your call to strcat attempting to concatenate overlapping strings (the same string), e.g.:

strcat (hist[histC], tokens[count]);

(note: are you sure you want count above?)

which results in your familiar:

Program received signal SIGSEGV, Segmentation fault.
__strcat_sse2_unaligned () at ../sysdeps/x86_64/multiarch/strcpy-sse2-unaligned.S:296
296     ../sysdeps/x86_64/multiarch/strcpy-sse2-unaligned.S: No such file or directory.

Now I'm not saying your problems are limited to that (note my comment about he update to count in checkAmp), so you have more work to do. But your SegFault is no longer a mystery.

It appears you intended:

histCheck (hist, &histCount, cmdargv, count, &isAmp);

You will also want to revisit:

strcat (hist[histC], tokens[count]);

which will lead to your very next SegFault. Here it appears you want:

strcat (hist[histC], tokens[i]);

Also, in your rework of your code, pay careful attention to resetting all values with each iteration around your do loop in main to prevent leaving stray values in cmdargv, etc... You will also need some way to free the memory you allocate to prevent leaking memory on subsequent calls to malloc in your various functions.

You will want to make friends with a debugger. There is no way around it, especially when you are chopping the flow up into a number of small inter-related functions to compartmentalize the code. Nothing wrong with that, it just make the debugger all the more important to figure out where the wheels are coming off -- and why...

Rework your code and post any further problems you have. To bandaid the count problem in checkAmp, you may consider something like:

void checkAmp (int *count, char** tokens, int *Amp) {
        size_t strlenth = strlen(tokens[*count - 1]);
    if((strlenth == 1) && tokens[*count - 1][0] == '&') 
    {    
        *Amp = 1;     
        tokens[*count - 1] = NULL; 
        *count = *count -1;
    } else {
        tokens[*count] = NULL;
    } 
}

(and updating the call to checkAmp as well)

Lastly, while not an error, the standard coding style for C avoids the use of caMelCase or MixedCase variable names in favor of all lower-case while reserving upper-case names for use with macros and constants. It is a matter of style -- so it is completely up to you...

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Thank you for the detailed response. You are right on using `i` instead of `count` in the `strcat (hist[histC], tokens[i])' in `readHist`. Also, I change `void histCheck` parameter as suggested (that was really a silly overlook on my part passing the same variable, cmdargv). Of course, segmentation is only one error I fixed, there are other ones that I am trying to fix such as `fork()` creating too many child processes that exceed the limit kernel allows for my user process. – Nate Lee May 21 '17 at 22:11
  • Here is the changed version of my code: [Shell_Program_C](https://pastebin.com/zEbXUijS). I need some time to inspect my code and try to figure out what problems to ask since I saw a number of unexpected behaviors which do not behave as what a simple shell program should. – Nate Lee May 21 '17 at 22:15
  • Nate, I'll take a look, but while I do, let me leave you with this. One big reason you are having so much difficulty is that you have overly-complicated the code by chopping it up into many many small inter-related functions. This make "thinking though" the code very difficult and the constant parameter passing masks problems like which values need to be reset and where. I noticed the behavior related to fork. Go re-read about what parts of environment are shared from the parent and those that remain distinct. I didn't chase those problems down for you, but it was clear values were not reset. – David C. Rankin May 22 '17 at 02:33
  • You need to enable warnings when you compile. At minimum include `-Wall -Wextra` in your compile string, I also recommend `-pedantic` and **do not** accept code until it compiles without warning. You created a built in `SegFault` in the new code with `free(cmdargv);` Your compiler will warn `shell_program2.c:203:17: warning: attempt to free a non-heap object 'cmdargv'` (it even tells you where). A cast for `for(int i = 1; i < (int)strlen...` will correct the `signed/unsigned comparison` warnings. You need to use `hist` in `findCmd`. `if (hist) {}` will do until you complete the function. – David C. Rankin May 22 '17 at 02:44
  • Here is an example [**C - minimal shell using fork and execvp**](https://pastebin.com/KRxEACG2) that may help you get the `fork` and `execv` calls sorted out. After you get the core functionality of your shell working, then you can work on picking off the history, etc.. Since you have so many functions, always ask, for each one, "*What do I need to provide?*", "*What do I need back?*", and "*How am I going to accomplish that?*". (from a *parameter* and *return type* standpoint). Add a *comment block* above each function documenting your logic -- it helps... – David C. Rankin May 22 '17 at 03:38