Matteo Del Seppia Matteo Del Seppia - 2 months ago 9
Bash Question

Issue with libreadline when completing an upper case directory

this is a piece of a shell that I'm creating. I'm having some trouble using libreadline, because when the shell gets loaded and I try to cd in a directory using autocompletion (so pressing TAB), after I press enter I access the directory but I get some strange output before another prompt is printed. I noticed that this happens only when the name of the directory starts with an upper case letter.

Example: "user:: ~ % cd Github" <-- written pressing tab to autocomplete Github

Next prompt is: "8b�/�user :: Github %"
I really cannot understand why, this is something really strange for me.

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/wait.h>
#include <signal.h>
#include <readline/history.h>
#include <readline/readline.h>
#include "flush.h"
#include "env.h"
#include "fget.h"

char * flush_builtins[] =
{
"cd",
"help",
"exit",
"ver",
"fget"
};

int flush_num_builtins() {
return sizeof(flush_builtins) / sizeof(char *);
}

int (*flush_func[]) (char **) =
{
&flush_cd,
&help,
&exit_flush,
&ver,
&fget
};

static int flush_startp(char **args)
{
pid_t pid;
int status;
pid = fork();
if (pid == 0)
{
if (execvp(args[0], args) == -1)
{
fprintf(stderr, "flush: command not found\n");
}
exit(1);
}
else if (pid < 0)
{
fprintf(stderr, "flush: command not found\n");
}
else
{
do
{
waitpid(pid, &status, WUNTRACED);
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}

return 1;
}

static int flush_exec(char **args)
{
int i;

if (args[0] == NULL)
{
return 1;
}

for (i = 0; i < flush_num_builtins(); i++)
{
if (strcmp(args[0], flush_builtins[i]) == 0) {
return (*flush_func[i])(args);
}
}

return flush_startp(args);
}

static char * flush_read(void)
{
fflush(stdout);
char *line_read = malloc(sizeof(char) * LINE_BUF);
char *prompt = malloc(sizeof(char) * LINE_BUF);
char *current, buffer[TOK_BUF];
current = getcwd(buffer, TOK_BUF);

strcat(prompt, get_user());
strcat(prompt, " :: ");

if (strcmp(current, get_home()) == 0)
{
strcat(prompt, "~");
}

else
{
strcat(prompt, get_cwd());
}

strcat(prompt, " % ");
line_read = readline(prompt);

if (line_read && *line_read)
{
add_history(line_read);
}

return line_read;
free(prompt);
free(line_read);
free(current);
}

static char **flush_getargs(char * line)
{
int bufsize = TOK_BUF;
int i = 0;
char **tokens = malloc(bufsize * sizeof(char *));
char **token;

if (!tokens)
{
fprintf(stderr, "allocation error\n");
exit(1);
}

token = strtok(line, DELIM);
while (token != NULL)
{
tokens[i] = token;
i++;
token = strtok(NULL, DELIM);
}

tokens[i] = NULL;
return tokens;
}


static void flush_loop(void)
{
char *line;
char **args;
int status;

do
{
line = flush_read();
args = flush_getargs(line);
status = flush_exec(args);
free(line);
free(args);
} while (status);
}

static void handler(int num)
{
signal(SIGINT, handler);
flush_loop();
fflush(stdout);
}

int main()
{
init();
signal(SIGINT, handler);
flush_loop();
return 0;
}

Answer

You can not use strcat with a non \0 terminated string:

char *prompt = malloc(sizeof(char) * LINE_BUF);
char *current, buffer[TOK_BUF];
current = getcwd(buffer, TOK_BUF);

strcat(prompt, get_user());

Use strcpy instead of strcat, or calloc instead of malloc