Bash command helper in C
* or []
There isn't really a definitive time when you should and shouldn't use pointers or arrays  (at least not that I know of). However, I generally only use arrays for:

Large buffers. For example, if you were reading a file.
A definitive size array of a simple type, such as an int or a char.
A group of constants all sharing the same type (your commands array, for example).

The two things are very similar. However, pointers are more idiomatic and C and I find are actually a lot easier to use.

Here is a great example of using pointers or arrays:

for (c = 0; string[c] != '\0'; ++c)
    string[c] = tolower(string[c]);


If instead of receiving an array through an argument you received a char *, you could do this:
for(; *string != '\0'; string++) {
    *string = tolower(*string);
}

This completely eliminates the need for c, and is a lot cleaner. Here, in the above snippet, we are taking the pointer string and increment through it each iteration.
Note that you still might be able to use an array in the arguments, it would just look a lot cleaner if you wrote it like this:
char *string

instead. I think it would still work either way (I did not test it), which is one of those gray areas between arrays and pointers.

const vs #define
Your #defines as the top should not be #defines:
const int MAX_TEXT_LENGTH = 100
const int MAX_DESC_LENGTH = 100
const int MAX_KEYWORDS_LENGTH = 100
const int NUM_COMMANDS = 8
const int NUM_RESULTS = 3

This is better practice than using #defines as those do not specify a direct type for the value; the pre-processor simply substitutes that number as a string into your code and it is up to the compiler to determine a type.

sprintf

    strcpy(current, commands[c].text);
    strcat(current, commands[c].description);
    strcat(current, commands[c].keywords);


Three separate function calls to concatenate three strings? That's not very efficient; and it's quite ugly. It would be better to use sprintf:
sprintf(current, &quot;%s%s%s&quot;, commands[c].text, commands[c].description, commands[c].keywords);

Actually, this looks like a toString method for a command struct. Time for a refactor!
void buffer_tostring(char *buf, struct command);

Simply move that code (with slight adjustments) into a method like the one above; it is good to split your code into as many different (and logical) methods as you can so that everything follows the single responsibility principle, overall making your code more maintainable.
You could also do the same thing for your results struct regarding here:

    strcpy(results[c].text, commands[c].text);
    strcpy(results[c].description, commands[c].description);