PS1 variable support, ~ expansion and pretty pwd
- if arsh has a PS1 env var, it will use it to display prompt. It supports esc sequences, \u, \w - if '~' is used, it will be expanded to $HOME env var value - if PS1 env var uses \w, and $PWD is somewhere inside $HOME, then it will show '~' in prompt instead of a long path - arsh sets $SHELL env var on startup
This commit is contained in:
parent
3e1f2a2340
commit
db5aef12c2
1 changed files with 177 additions and 5 deletions
182
main.c
182
main.c
|
|
@ -9,13 +9,21 @@
|
|||
#include <ctype.h>
|
||||
|
||||
constexpr size_t max_length = 1024;
|
||||
constexpr size_t max_env_var_lenght = 256;
|
||||
constexpr size_t max_env_var_length = 256;
|
||||
const char* token_separators = " \t\n\r";
|
||||
char* esc = "\x1B";
|
||||
|
||||
char* default_prompt = "arsh> ";
|
||||
|
||||
static sigjmp_buf restart_jmp_buf;
|
||||
static constexpr int restart_value = 69;
|
||||
static volatile sig_atomic_t jump_active = 0;
|
||||
|
||||
size_t min_size_t(size_t a, size_t b)
|
||||
{
|
||||
return a > b ? b : a;
|
||||
}
|
||||
|
||||
void sigint_handler(int signo)
|
||||
{
|
||||
if(!jump_active)
|
||||
|
|
@ -168,6 +176,7 @@ static void append_str(char** buf, size_t* len, size_t* cap, const char* s)
|
|||
|
||||
struct pipeline* parse_pipeline(char *str);
|
||||
int execute_pipeline(struct pipeline* pl, int capture, char** out);
|
||||
char* get_home();
|
||||
|
||||
static char* run_subcommand(const char* cmd)
|
||||
{
|
||||
|
|
@ -223,6 +232,13 @@ char* parse_token(char** input)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!in_single && *p == '~')
|
||||
{
|
||||
p++;
|
||||
append_str(&buf, &len, &cap, get_home());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!in_single && *p == '\\')
|
||||
{
|
||||
p++;
|
||||
|
|
@ -288,7 +304,7 @@ char* parse_token(char** input)
|
|||
if (*p == '{')
|
||||
{
|
||||
p++;
|
||||
char name[max_env_var_lenght];
|
||||
char name[max_env_var_length];
|
||||
int i = 0;
|
||||
while (*p && *p != '}')
|
||||
name[i++] = *p++;
|
||||
|
|
@ -299,7 +315,7 @@ char* parse_token(char** input)
|
|||
continue;
|
||||
}
|
||||
|
||||
char name[max_env_var_lenght];
|
||||
char name[max_env_var_length];
|
||||
int i = 0;
|
||||
while (*p && (isalnum(*p) || *p == '_'))
|
||||
name[i++] = *p++;
|
||||
|
|
@ -519,7 +535,13 @@ pid_t run_with_redir(struct command* command, int n_pipes, int (*pipes)[2])
|
|||
|
||||
int cd(char* path)
|
||||
{
|
||||
return chdir(path);
|
||||
int result = chdir(path);
|
||||
char cwd[max_length];
|
||||
if (getcwd(cwd, sizeof(cwd)) != NULL)
|
||||
{
|
||||
setenv("PWD", cwd, 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int execute_pipeline(struct pipeline* pl, int capture, char** out)
|
||||
|
|
@ -618,10 +640,155 @@ int execute_pipeline(struct pipeline* pl, int capture, char** out)
|
|||
return WEXITSTATUS(status);
|
||||
}
|
||||
|
||||
char username_buf[max_env_var_length] = {0};
|
||||
char* get_user()
|
||||
{
|
||||
char* result = getenv("USER");
|
||||
if(!result)
|
||||
{
|
||||
return "UNKNOWN";
|
||||
}
|
||||
size_t length = min_size_t(sizeof(username_buf), strlen(result) + 1);
|
||||
memcpy(username_buf, result, length);
|
||||
return username_buf;
|
||||
}
|
||||
|
||||
char home_buf[max_length] = {0};
|
||||
char* get_home()
|
||||
{
|
||||
char* result = getenv("HOME");
|
||||
if(!result)
|
||||
{
|
||||
return "UNKNOWN_HOME_PATH";
|
||||
}
|
||||
size_t length = min_size_t(sizeof(home_buf), strlen(result) + 1);
|
||||
memcpy(home_buf, result, length);
|
||||
return home_buf;
|
||||
}
|
||||
|
||||
char pwd_buf[max_length] = {0};
|
||||
char* get_pwd()
|
||||
{
|
||||
char* result = getenv("PWD");
|
||||
if(!result)
|
||||
{
|
||||
return "UNKNOWN";
|
||||
}
|
||||
size_t length = min_size_t(sizeof(pwd_buf), strlen(result) + 1);
|
||||
memcpy(pwd_buf, result, length);
|
||||
return pwd_buf;
|
||||
}
|
||||
|
||||
char* prettify_pwd(char* pwd)
|
||||
{
|
||||
if (!pwd)
|
||||
{
|
||||
fprintf(stderr, "Internal Error: pwd can't be null");
|
||||
exit(1);
|
||||
}
|
||||
char* home = get_home();
|
||||
size_t home_len = strlen(home);
|
||||
size_t pwd_len = strlen(pwd);
|
||||
if(pwd_len < home_len)
|
||||
{
|
||||
return pwd;
|
||||
}
|
||||
if(memcmp(home, pwd, home_len) == 0)
|
||||
{
|
||||
char* ret = ccalloc(sizeof(char), pwd_len - home_len + 1 + 1);
|
||||
parser_allocated(ret);
|
||||
ret[0] = '~';
|
||||
memcpy(ret + 1, pwd + home_len, pwd_len - home_len);
|
||||
ret[pwd_len - home_len + 1] = '\0';
|
||||
return ret;
|
||||
}
|
||||
return pwd;
|
||||
}
|
||||
|
||||
char prompt_buf[max_length] = {0};
|
||||
char* generate_ps1_prompt()
|
||||
{
|
||||
char env_buf[max_env_var_length] = {0};
|
||||
char* ps1 = getenv("PS1");
|
||||
if(!ps1)
|
||||
{
|
||||
return default_prompt;
|
||||
}
|
||||
size_t ps1_len = min_size_t(sizeof(env_buf), strlen(ps1) + 1) - 1;
|
||||
if(ps1_len == 0)
|
||||
{
|
||||
return default_prompt;
|
||||
}
|
||||
memcpy(env_buf, ps1, ps1_len);
|
||||
env_buf[ps1_len + 1] = '\0';
|
||||
if(ps1_len < 2)
|
||||
{
|
||||
memcpy(prompt_buf, env_buf, ps1_len + 1);
|
||||
return prompt_buf;
|
||||
}
|
||||
size_t i = 1, j = 0;
|
||||
size_t start = 0;
|
||||
while(env_buf[i] != '\0' && j < sizeof(prompt_buf))
|
||||
{
|
||||
if(env_buf[i - 1] != '\\')
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
char* data = 0;
|
||||
switch (env_buf[i])
|
||||
{
|
||||
case 'u':
|
||||
data = get_user();
|
||||
break;
|
||||
case 'w':
|
||||
data = prettify_pwd(get_pwd());
|
||||
break;
|
||||
case 'e':
|
||||
data = esc;
|
||||
break;
|
||||
case ']':
|
||||
case '[':
|
||||
data = "";
|
||||
break;
|
||||
default:
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if(!data)
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
size_t len = strlen(data);
|
||||
if(j + i - 1 - start + len >= sizeof(prompt_buf))
|
||||
{
|
||||
fprintf(stderr, "Out of memory(1) for prompt: %d >= %d", j + i - start + len, sizeof(prompt_buf));
|
||||
break;
|
||||
}
|
||||
size_t start_len = min_size_t(sizeof(prompt_buf) - j, i - start - 1);
|
||||
memcpy(prompt_buf + j, env_buf + start, start_len);
|
||||
j += start_len;
|
||||
if(j + len >= sizeof(prompt_buf))
|
||||
{
|
||||
fprintf(stderr, "Out of memory(2) for prompt: %d >= %d", j + len, sizeof(prompt_buf));
|
||||
break;
|
||||
}
|
||||
memcpy(prompt_buf + j, data, len);
|
||||
i += 1; start = i; j += len;
|
||||
}
|
||||
size_t start_len = min_size_t(sizeof(prompt_buf) - j, i - start);
|
||||
memcpy(prompt_buf + j, env_buf + start, start_len);
|
||||
j += start_len;
|
||||
prompt_buf[j] = '\0';
|
||||
return prompt_buf;
|
||||
}
|
||||
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
setenv("SHELL", argv[0], 1);
|
||||
FILE* stream = stdin;
|
||||
char* prompt = "arsh> ";
|
||||
char* prompt = generate_ps1_prompt();
|
||||
const bool spawn_command = argc >= 2 && strcmp(argv[1], "-c") == 0;
|
||||
if(spawn_command)
|
||||
{
|
||||
|
|
@ -674,5 +841,10 @@ int main(int argc, const char* argv[])
|
|||
execute_pipeline(pipeline, 0, NULL);
|
||||
|
||||
parser_free();
|
||||
|
||||
if(prompt)
|
||||
{
|
||||
prompt = generate_ps1_prompt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue