save commit
This commit is contained in:
221
minishell-codex/src/builtins/builtins.c
Normal file
221
minishell-codex/src/builtins/builtins.c
Normal file
@@ -0,0 +1,221 @@
|
||||
#include "minishell.h"
|
||||
|
||||
static int builtin_echo(t_command *cmd, t_shell *sh);
|
||||
static int builtin_cd(t_command *cmd, t_shell *sh);
|
||||
static int builtin_pwd(t_command *cmd, t_shell *sh);
|
||||
static int builtin_env(t_command *cmd, t_shell *sh);
|
||||
static int builtin_export(t_command *cmd, t_shell *sh);
|
||||
static int builtin_unset(t_command *cmd, t_shell *sh);
|
||||
static int builtin_exit(t_command *cmd, t_shell *sh);
|
||||
|
||||
int is_builtin(const char *name)
|
||||
{
|
||||
if (!name)
|
||||
return 0;
|
||||
return (strcmp(name, "echo") == 0 || strcmp(name, "cd") == 0
|
||||
|| strcmp(name, "pwd") == 0 || strcmp(name, "env") == 0
|
||||
|| strcmp(name, "export") == 0 || strcmp(name, "unset") == 0
|
||||
|| strcmp(name, "exit") == 0);
|
||||
}
|
||||
|
||||
int exec_builtin(t_command *cmd, t_shell *sh)
|
||||
{
|
||||
if (strcmp(cmd->argv[0], "echo") == 0)
|
||||
return builtin_echo(cmd, sh);
|
||||
if (strcmp(cmd->argv[0], "cd") == 0)
|
||||
return builtin_cd(cmd, sh);
|
||||
if (strcmp(cmd->argv[0], "pwd") == 0)
|
||||
return builtin_pwd(cmd, sh);
|
||||
if (strcmp(cmd->argv[0], "env") == 0)
|
||||
return builtin_env(cmd, sh);
|
||||
if (strcmp(cmd->argv[0], "export") == 0)
|
||||
return builtin_export(cmd, sh);
|
||||
if (strcmp(cmd->argv[0], "unset") == 0)
|
||||
return builtin_unset(cmd, sh);
|
||||
if (strcmp(cmd->argv[0], "exit") == 0)
|
||||
return builtin_exit(cmd, sh);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int builtin_echo(t_command *cmd, t_shell *sh)
|
||||
{
|
||||
int i = 1;
|
||||
int newline = 1;
|
||||
|
||||
(void)sh;
|
||||
while (cmd->argv[i] && cmd->argv[i][0] == '-' && cmd->argv[i][1] == 'n')
|
||||
{
|
||||
int j = 2;
|
||||
while (cmd->argv[i][j] == 'n')
|
||||
j++;
|
||||
if (cmd->argv[i][j] != '\0')
|
||||
break;
|
||||
newline = 0;
|
||||
i++;
|
||||
}
|
||||
while (cmd->argv[i])
|
||||
{
|
||||
printf("%s", cmd->argv[i]);
|
||||
if (cmd->argv[i + 1])
|
||||
printf(" ");
|
||||
i++;
|
||||
}
|
||||
if (newline)
|
||||
printf("\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int builtin_pwd(t_command *cmd, t_shell *sh)
|
||||
{
|
||||
char buf[4096];
|
||||
(void)cmd;
|
||||
(void)sh;
|
||||
if (getcwd(buf, sizeof(buf)))
|
||||
printf("%s\n", buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int builtin_cd(t_command *cmd, t_shell *sh)
|
||||
{
|
||||
char *path;
|
||||
char cwd[4096];
|
||||
|
||||
if (cmd->argc > 2)
|
||||
{
|
||||
fprintf(stderr, "minishell: cd: too many arguments\n");
|
||||
return 1;
|
||||
}
|
||||
if (cmd->argc == 1)
|
||||
path = env_get(sh, "HOME");
|
||||
else
|
||||
path = cmd->argv[1];
|
||||
if (!path)
|
||||
{
|
||||
fprintf(stderr, "minishell: cd: HOME not set\n");
|
||||
return 1;
|
||||
}
|
||||
if (getcwd(cwd, sizeof(cwd)))
|
||||
env_set(sh, "OLDPWD", cwd);
|
||||
if (chdir(path) != 0)
|
||||
{
|
||||
perror("minishell: cd");
|
||||
return 1;
|
||||
}
|
||||
if (getcwd(cwd, sizeof(cwd)))
|
||||
env_set(sh, "PWD", cwd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int builtin_env(t_command *cmd, t_shell *sh)
|
||||
{
|
||||
if (cmd->argc > 1)
|
||||
{
|
||||
fprintf(stderr, "minishell: env: too many arguments\n");
|
||||
return 1;
|
||||
}
|
||||
env_print(sh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int valid_identifier(const char *s)
|
||||
{
|
||||
int i = 0;
|
||||
if (!s || !ms_is_alpha((unsigned char)s[0]))
|
||||
return 0;
|
||||
while (s[i])
|
||||
{
|
||||
if (!ms_is_alnum((unsigned char)s[i]))
|
||||
return 0;
|
||||
i++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int builtin_export(t_command *cmd, t_shell *sh)
|
||||
{
|
||||
int i = 1;
|
||||
if (cmd->argc == 1)
|
||||
{
|
||||
env_print(sh);
|
||||
return 0;
|
||||
}
|
||||
while (cmd->argv[i])
|
||||
{
|
||||
char *eq = strchr(cmd->argv[i], '=');
|
||||
if (eq)
|
||||
{
|
||||
char *key = ms_strndup(cmd->argv[i], (size_t)(eq - cmd->argv[i]));
|
||||
char *val = ms_strdup(eq + 1);
|
||||
if (!valid_identifier(key))
|
||||
fprintf(stderr, "minishell: export: `%s': not a valid identifier\n", cmd->argv[i]);
|
||||
else
|
||||
env_set(sh, key, val);
|
||||
free(key);
|
||||
free(val);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!valid_identifier(cmd->argv[i]))
|
||||
fprintf(stderr, "minishell: export: `%s': not a valid identifier\n", cmd->argv[i]);
|
||||
else
|
||||
env_set(sh, cmd->argv[i], "");
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int builtin_unset(t_command *cmd, t_shell *sh)
|
||||
{
|
||||
int i = 1;
|
||||
while (cmd->argv[i])
|
||||
{
|
||||
env_unset(sh, cmd->argv[i]);
|
||||
i++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int builtin_exit(t_command *cmd, t_shell *sh)
|
||||
{
|
||||
long code = sh->exit_status;
|
||||
int i = 0;
|
||||
|
||||
if (sh->interactive)
|
||||
printf("exit\n");
|
||||
if (cmd->argc == 1)
|
||||
{
|
||||
sh->exit_requested = 1;
|
||||
sh->exit_status = (int)(code & 0xFF);
|
||||
return sh->exit_status;
|
||||
}
|
||||
if (cmd->argc > 2)
|
||||
{
|
||||
fprintf(stderr, "minishell: exit: too many arguments\n");
|
||||
return 1;
|
||||
}
|
||||
if (cmd->argv[1][i] == '+' || cmd->argv[1][i] == '-')
|
||||
i++;
|
||||
if (cmd->argv[1][i] == '\0')
|
||||
{
|
||||
fprintf(stderr, "minishell: exit: %s: numeric argument required\n", cmd->argv[1]);
|
||||
sh->exit_requested = 1;
|
||||
sh->exit_status = 2;
|
||||
return 2;
|
||||
}
|
||||
while (cmd->argv[1][i])
|
||||
{
|
||||
if (!ms_is_digit((unsigned char)cmd->argv[1][i]))
|
||||
{
|
||||
fprintf(stderr, "minishell: exit: %s: numeric argument required\n", cmd->argv[1]);
|
||||
sh->exit_requested = 1;
|
||||
sh->exit_status = 2;
|
||||
return 2;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
code = strtol(cmd->argv[1], NULL, 10);
|
||||
sh->exit_requested = 1;
|
||||
sh->exit_status = (int)(code & 0xFF);
|
||||
return sh->exit_status;
|
||||
}
|
||||
18
minishell-codex/src/core/init.c
Normal file
18
minishell-codex/src/core/init.c
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "minishell.h"
|
||||
|
||||
void ms_init(t_shell *sh, char **envp)
|
||||
{
|
||||
memset(sh, 0, sizeof(*sh));
|
||||
sh->interactive = isatty(STDIN_FILENO);
|
||||
sh->exit_status = 0;
|
||||
sh->last_status = 0;
|
||||
sh->exit_requested = 0;
|
||||
env_init(sh, envp);
|
||||
ms_setup_signals(sh);
|
||||
}
|
||||
|
||||
void ms_cleanup(t_shell *sh)
|
||||
{
|
||||
env_clear(sh);
|
||||
rl_clear_history();
|
||||
}
|
||||
46
minishell-codex/src/core/loop.c
Normal file
46
minishell-codex/src/core/loop.c
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "minishell.h"
|
||||
|
||||
static void handle_line(t_shell *sh, char *line)
|
||||
{
|
||||
int error = 0;
|
||||
t_token *toks = NULL;
|
||||
t_pipeline *p = NULL;
|
||||
|
||||
toks = lex_line(line, &error);
|
||||
if (error)
|
||||
{
|
||||
free_tokens(toks);
|
||||
return;
|
||||
}
|
||||
p = parse_tokens(toks, &error);
|
||||
free_tokens(toks);
|
||||
if (error || !p)
|
||||
{
|
||||
free_pipeline(p);
|
||||
return;
|
||||
}
|
||||
if (expand_pipeline(p, sh) != 0)
|
||||
{
|
||||
free_pipeline(p);
|
||||
return;
|
||||
}
|
||||
sh->exit_status = execute_pipeline(p, sh);
|
||||
sh->last_status = sh->exit_status;
|
||||
free_pipeline(p);
|
||||
}
|
||||
|
||||
void ms_loop(t_shell *sh)
|
||||
{
|
||||
char *line;
|
||||
|
||||
while (!sh->exit_requested)
|
||||
{
|
||||
line = readline(MS_PROMPT);
|
||||
if (!line)
|
||||
break;
|
||||
if (line[0] != '\0')
|
||||
add_history(line);
|
||||
handle_line(sh, line);
|
||||
free(line);
|
||||
}
|
||||
}
|
||||
46
minishell-codex/src/core/signals.c
Normal file
46
minishell-codex/src/core/signals.c
Normal file
@@ -0,0 +1,46 @@
|
||||
#include "minishell.h"
|
||||
|
||||
int g_signal = 0;
|
||||
|
||||
static void sigint_handler(int sig)
|
||||
{
|
||||
g_signal = sig;
|
||||
write(STDOUT_FILENO, "\n", 1);
|
||||
rl_on_new_line();
|
||||
rl_replace_line("", 0);
|
||||
rl_redisplay();
|
||||
}
|
||||
|
||||
static void sigquit_handler(int sig)
|
||||
{
|
||||
g_signal = sig;
|
||||
(void)sig;
|
||||
}
|
||||
|
||||
void ms_setup_signals(t_shell *sh)
|
||||
{
|
||||
struct sigaction sa_int;
|
||||
struct sigaction sa_quit;
|
||||
|
||||
(void)sh;
|
||||
memset(&sa_int, 0, sizeof(sa_int));
|
||||
memset(&sa_quit, 0, sizeof(sa_quit));
|
||||
sa_int.sa_handler = sigint_handler;
|
||||
sa_quit.sa_handler = sigquit_handler;
|
||||
sigemptyset(&sa_int.sa_mask);
|
||||
sigemptyset(&sa_quit.sa_mask);
|
||||
sigaction(SIGINT, &sa_int, NULL);
|
||||
sigaction(SIGQUIT, &sa_quit, NULL);
|
||||
}
|
||||
|
||||
void ms_set_child_signals(void)
|
||||
{
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGQUIT, SIG_DFL);
|
||||
}
|
||||
|
||||
void ms_set_heredoc_signals(void)
|
||||
{
|
||||
signal(SIGINT, SIG_DFL);
|
||||
signal(SIGQUIT, SIG_IGN);
|
||||
}
|
||||
171
minishell-codex/src/env/env.c
vendored
Normal file
171
minishell-codex/src/env/env.c
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
#include "minishell.h"
|
||||
|
||||
static t_env *env_new(const char *key, const char *value)
|
||||
{
|
||||
t_env *n = (t_env *)calloc(1, sizeof(t_env));
|
||||
if (!n)
|
||||
return NULL;
|
||||
n->key = ms_strdup(key);
|
||||
n->value = value ? ms_strdup(value) : ms_strdup("");
|
||||
if (!n->key || !n->value)
|
||||
{
|
||||
free(n->key);
|
||||
free(n->value);
|
||||
free(n);
|
||||
return NULL;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
void env_init(t_shell *sh, char **envp)
|
||||
{
|
||||
int i;
|
||||
char *eq;
|
||||
|
||||
sh->env = NULL;
|
||||
if (!envp)
|
||||
return;
|
||||
i = 0;
|
||||
while (envp[i])
|
||||
{
|
||||
eq = strchr(envp[i], '=');
|
||||
if (eq)
|
||||
{
|
||||
char *key = ms_strndup(envp[i], (size_t)(eq - envp[i]));
|
||||
char *val = ms_strdup(eq + 1);
|
||||
env_set(sh, key, val);
|
||||
free(key);
|
||||
free(val);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void env_clear(t_shell *sh)
|
||||
{
|
||||
t_env *cur = sh->env;
|
||||
t_env *next;
|
||||
|
||||
while (cur)
|
||||
{
|
||||
next = cur->next;
|
||||
free(cur->key);
|
||||
free(cur->value);
|
||||
free(cur);
|
||||
cur = next;
|
||||
}
|
||||
sh->env = NULL;
|
||||
}
|
||||
|
||||
char *env_get(t_shell *sh, const char *key)
|
||||
{
|
||||
t_env *cur = sh->env;
|
||||
|
||||
while (cur)
|
||||
{
|
||||
if (strcmp(cur->key, key) == 0)
|
||||
return cur->value;
|
||||
cur = cur->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int env_set(t_shell *sh, const char *key, const char *value)
|
||||
{
|
||||
t_env *cur = sh->env;
|
||||
t_env *prev = NULL;
|
||||
|
||||
while (cur)
|
||||
{
|
||||
if (strcmp(cur->key, key) == 0)
|
||||
{
|
||||
char *dup = ms_strdup(value ? value : "");
|
||||
if (!dup)
|
||||
return 1;
|
||||
free(cur->value);
|
||||
cur->value = dup;
|
||||
return 0;
|
||||
}
|
||||
prev = cur;
|
||||
cur = cur->next;
|
||||
}
|
||||
cur = env_new(key, value);
|
||||
if (!cur)
|
||||
return 1;
|
||||
if (prev)
|
||||
prev->next = cur;
|
||||
else
|
||||
sh->env = cur;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int env_unset(t_shell *sh, const char *key)
|
||||
{
|
||||
t_env *cur = sh->env;
|
||||
t_env *prev = NULL;
|
||||
|
||||
while (cur)
|
||||
{
|
||||
if (strcmp(cur->key, key) == 0)
|
||||
{
|
||||
if (prev)
|
||||
prev->next = cur->next;
|
||||
else
|
||||
sh->env = cur->next;
|
||||
free(cur->key);
|
||||
free(cur->value);
|
||||
free(cur);
|
||||
return 0;
|
||||
}
|
||||
prev = cur;
|
||||
cur = cur->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char **env_to_envp(t_shell *sh)
|
||||
{
|
||||
char **envp;
|
||||
int count = 0;
|
||||
t_env *cur = sh->env;
|
||||
int i = 0;
|
||||
|
||||
while (cur)
|
||||
{
|
||||
count++;
|
||||
cur = cur->next;
|
||||
}
|
||||
envp = (char **)calloc((size_t)count + 1, sizeof(char *));
|
||||
if (!envp)
|
||||
return NULL;
|
||||
cur = sh->env;
|
||||
while (cur)
|
||||
{
|
||||
char *kv = ms_strjoin3(cur->key, "=", cur->value);
|
||||
envp[i++] = kv;
|
||||
cur = cur->next;
|
||||
}
|
||||
envp[i] = NULL;
|
||||
return envp;
|
||||
}
|
||||
|
||||
void env_free_envp(char **envp)
|
||||
{
|
||||
int i = 0;
|
||||
if (!envp)
|
||||
return;
|
||||
while (envp[i])
|
||||
free(envp[i++]);
|
||||
free(envp);
|
||||
}
|
||||
|
||||
void env_print(t_shell *sh)
|
||||
{
|
||||
t_env *cur = sh->env;
|
||||
while (cur)
|
||||
{
|
||||
if (cur->value)
|
||||
printf("%s=%s\n", cur->key, cur->value);
|
||||
cur = cur->next;
|
||||
}
|
||||
}
|
||||
134
minishell-codex/src/executor/exec.c
Normal file
134
minishell-codex/src/executor/exec.c
Normal file
@@ -0,0 +1,134 @@
|
||||
#include "minishell.h"
|
||||
|
||||
static int exec_external(t_command *cmd, t_shell *sh)
|
||||
{
|
||||
char **envp = env_to_envp(sh);
|
||||
if (!envp)
|
||||
return 1;
|
||||
execve(cmd->path, cmd->argv, envp);
|
||||
perror(cmd->path);
|
||||
env_free_envp(envp);
|
||||
return 126;
|
||||
}
|
||||
|
||||
static int run_command_child(t_command *cmd, t_shell *sh)
|
||||
{
|
||||
int saved_in, saved_out;
|
||||
|
||||
if (apply_redirections(cmd, &saved_in, &saved_out) != 0)
|
||||
return 1;
|
||||
if (is_builtin(cmd->argv[0]))
|
||||
return exec_builtin(cmd, sh);
|
||||
return exec_external(cmd, sh);
|
||||
}
|
||||
|
||||
static int run_command_parent_builtin(t_command *cmd, t_shell *sh)
|
||||
{
|
||||
int saved_in, saved_out;
|
||||
int status;
|
||||
|
||||
if (apply_redirections(cmd, &saved_in, &saved_out) != 0)
|
||||
return 1;
|
||||
status = exec_builtin(cmd, sh);
|
||||
restore_redirections(saved_in, saved_out);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int setup_pipes(int idx, int count, int pipefd[2])
|
||||
{
|
||||
if (idx + 1 >= count)
|
||||
return 0;
|
||||
if (pipe(pipefd) == -1)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void setup_child_fds(int idx, int count, int prev_read, int pipefd[2])
|
||||
{
|
||||
if (prev_read != -1)
|
||||
{
|
||||
dup2(prev_read, STDIN_FILENO);
|
||||
close(prev_read);
|
||||
}
|
||||
if (idx + 1 < count)
|
||||
{
|
||||
dup2(pipefd[1], STDOUT_FILENO);
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
}
|
||||
}
|
||||
|
||||
static void close_parent_fds(int idx, int count, int *prev_read, int pipefd[2])
|
||||
{
|
||||
if (*prev_read != -1)
|
||||
close(*prev_read);
|
||||
if (idx + 1 < count)
|
||||
{
|
||||
close(pipefd[1]);
|
||||
*prev_read = pipefd[0];
|
||||
}
|
||||
else
|
||||
*prev_read = -1;
|
||||
}
|
||||
|
||||
int execute_pipeline(t_pipeline *p, t_shell *sh)
|
||||
{
|
||||
int i;
|
||||
int prev_read = -1;
|
||||
pid_t *pids;
|
||||
int status = 0;
|
||||
|
||||
if (!p || p->count == 0)
|
||||
return 0;
|
||||
if (prepare_heredocs(p, sh) != 0)
|
||||
return sh->exit_status;
|
||||
for (size_t k = 0; k < p->count; k++)
|
||||
{
|
||||
if (!p->cmds[k]->argv || !p->cmds[k]->argv[0])
|
||||
return 1;
|
||||
free(p->cmds[k]->path);
|
||||
p->cmds[k]->path = resolve_path(p->cmds[k]->argv[0], sh);
|
||||
}
|
||||
if (p->count == 1 && is_builtin(p->cmds[0]->argv[0]))
|
||||
return run_command_parent_builtin(p->cmds[0], sh);
|
||||
pids = (pid_t *)calloc(p->count, sizeof(pid_t));
|
||||
if (!pids)
|
||||
return 1;
|
||||
for (i = 0; i < (int)p->count; i++)
|
||||
{
|
||||
int pipefd[2] = {-1, -1};
|
||||
if (setup_pipes(i, (int)p->count, pipefd))
|
||||
break;
|
||||
pids[i] = fork();
|
||||
if (pids[i] == 0)
|
||||
{
|
||||
ms_set_child_signals();
|
||||
setup_child_fds(i, (int)p->count, prev_read, pipefd);
|
||||
if (!p->cmds[i]->path)
|
||||
{
|
||||
fprintf(stderr, "minishell: %s: command not found\n", p->cmds[i]->argv[0]);
|
||||
exit(127);
|
||||
}
|
||||
status = run_command_child(p->cmds[i], sh);
|
||||
exit(status);
|
||||
}
|
||||
close_parent_fds(i, (int)p->count, &prev_read, pipefd);
|
||||
}
|
||||
for (i = 0; i < (int)p->count; i++)
|
||||
{
|
||||
int wstatus = 0;
|
||||
if (pids[i] > 0)
|
||||
{
|
||||
waitpid(pids[i], &wstatus, 0);
|
||||
if (i == (int)p->count - 1)
|
||||
{
|
||||
if (WIFEXITED(wstatus))
|
||||
status = WEXITSTATUS(wstatus);
|
||||
else if (WIFSIGNALED(wstatus))
|
||||
status = 128 + WTERMSIG(wstatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
free(pids);
|
||||
return status;
|
||||
}
|
||||
41
minishell-codex/src/executor/path.c
Normal file
41
minishell-codex/src/executor/path.c
Normal file
@@ -0,0 +1,41 @@
|
||||
#include "minishell.h"
|
||||
|
||||
static int has_slash(const char *s)
|
||||
{
|
||||
return (s && strchr(s, '/'));
|
||||
}
|
||||
|
||||
char *resolve_path(const char *cmd, t_shell *sh)
|
||||
{
|
||||
char *path_env;
|
||||
char **parts;
|
||||
char *candidate;
|
||||
int i;
|
||||
|
||||
if (!cmd)
|
||||
return NULL;
|
||||
if (is_builtin(cmd))
|
||||
return ms_strdup(cmd);
|
||||
if (has_slash(cmd))
|
||||
return ms_strdup(cmd);
|
||||
path_env = env_get(sh, "PATH");
|
||||
if (!path_env)
|
||||
return NULL;
|
||||
parts = ms_split(path_env, ':');
|
||||
if (!parts)
|
||||
return NULL;
|
||||
i = 0;
|
||||
while (parts[i])
|
||||
{
|
||||
candidate = ms_strjoin3(parts[i], "/", cmd);
|
||||
if (candidate && access(candidate, X_OK) == 0)
|
||||
{
|
||||
ms_free_split(parts);
|
||||
return candidate;
|
||||
}
|
||||
free(candidate);
|
||||
i++;
|
||||
}
|
||||
ms_free_split(parts);
|
||||
return NULL;
|
||||
}
|
||||
108
minishell-codex/src/executor/redir.c
Normal file
108
minishell-codex/src/executor/redir.c
Normal file
@@ -0,0 +1,108 @@
|
||||
#include "minishell.h"
|
||||
|
||||
static int open_redir(t_redir *r)
|
||||
{
|
||||
if (r->type == REDIR_IN)
|
||||
return open(r->target, O_RDONLY);
|
||||
if (r->type == REDIR_OUT)
|
||||
return open(r->target, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if (r->type == REDIR_APPEND)
|
||||
return open(r->target, O_WRONLY | O_CREAT | O_APPEND, 0644);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int prepare_heredocs(t_pipeline *p, t_shell *sh)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
g_signal = 0;
|
||||
for (i = 0; i < p->count; i++)
|
||||
{
|
||||
t_redir *r = p->cmds[i]->redirs;
|
||||
while (r)
|
||||
{
|
||||
if (r->type == REDIR_HEREDOC)
|
||||
{
|
||||
int fds[2];
|
||||
char *line;
|
||||
ms_set_heredoc_signals();
|
||||
if (pipe(fds) == -1)
|
||||
return 1;
|
||||
while (1)
|
||||
{
|
||||
line = readline("> ");
|
||||
if (!line)
|
||||
break;
|
||||
if (strcmp(line, r->target) == 0)
|
||||
{
|
||||
free(line);
|
||||
break;
|
||||
}
|
||||
write(fds[1], line, strlen(line));
|
||||
write(fds[1], "\n", 1);
|
||||
free(line);
|
||||
}
|
||||
close(fds[1]);
|
||||
r->fd = fds[0];
|
||||
ms_setup_signals(sh);
|
||||
if (g_signal == SIGINT)
|
||||
{
|
||||
sh->exit_status = 130;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int apply_redirections(t_command *cmd, int *saved_stdin, int *saved_stdout)
|
||||
{
|
||||
t_redir *r = cmd->redirs;
|
||||
*saved_stdin = -1;
|
||||
*saved_stdout = -1;
|
||||
while (r)
|
||||
{
|
||||
int fd = -1;
|
||||
if (r->type == REDIR_HEREDOC)
|
||||
fd = r->fd;
|
||||
else
|
||||
fd = open_redir(r);
|
||||
if (fd == -1)
|
||||
{
|
||||
perror(r->target);
|
||||
return 1;
|
||||
}
|
||||
if (r->type == REDIR_IN || r->type == REDIR_HEREDOC)
|
||||
{
|
||||
if (*saved_stdin == -1)
|
||||
*saved_stdin = dup(STDIN_FILENO);
|
||||
dup2(fd, STDIN_FILENO);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*saved_stdout == -1)
|
||||
*saved_stdout = dup(STDOUT_FILENO);
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
}
|
||||
if (r->type != REDIR_HEREDOC)
|
||||
close(fd);
|
||||
r = r->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void restore_redirections(int saved_stdin, int saved_stdout)
|
||||
{
|
||||
if (saved_stdin != -1)
|
||||
{
|
||||
dup2(saved_stdin, STDIN_FILENO);
|
||||
close(saved_stdin);
|
||||
}
|
||||
if (saved_stdout != -1)
|
||||
{
|
||||
dup2(saved_stdout, STDOUT_FILENO);
|
||||
close(saved_stdout);
|
||||
}
|
||||
}
|
||||
17
minishell-codex/src/main.c
Normal file
17
minishell-codex/src/main.c
Normal file
@@ -0,0 +1,17 @@
|
||||
#include "minishell.h"
|
||||
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
t_shell sh;
|
||||
|
||||
(void)argv;
|
||||
if (argc != 1)
|
||||
{
|
||||
fprintf(stderr, "Usage: ./minishell\n");
|
||||
return 1;
|
||||
}
|
||||
ms_init(&sh, envp);
|
||||
ms_loop(&sh);
|
||||
ms_cleanup(&sh);
|
||||
return sh.exit_status;
|
||||
}
|
||||
113
minishell-codex/src/parser/expand.c
Normal file
113
minishell-codex/src/parser/expand.c
Normal file
@@ -0,0 +1,113 @@
|
||||
#include "minishell.h"
|
||||
|
||||
static void buf_append(char **buf, size_t *len, size_t *cap, const char *s)
|
||||
{
|
||||
size_t slen;
|
||||
char *newbuf;
|
||||
|
||||
if (!s)
|
||||
return;
|
||||
slen = strlen(s);
|
||||
if (*len + slen + 1 > *cap)
|
||||
{
|
||||
*cap = (*len + slen + 1) * 2;
|
||||
newbuf = (char *)realloc(*buf, *cap);
|
||||
if (!newbuf)
|
||||
return;
|
||||
*buf = newbuf;
|
||||
}
|
||||
memcpy(*buf + *len, s, slen);
|
||||
*len += slen;
|
||||
(*buf)[*len] = '\0';
|
||||
}
|
||||
|
||||
static void buf_append_char(char **buf, size_t *len, size_t *cap, char c)
|
||||
{
|
||||
char tmp[2];
|
||||
tmp[0] = c;
|
||||
tmp[1] = '\0';
|
||||
buf_append(buf, len, cap, tmp);
|
||||
}
|
||||
|
||||
static char *expand_word(const char *word, t_shell *sh)
|
||||
{
|
||||
int in_single = 0;
|
||||
int in_double = 0;
|
||||
size_t i = 0;
|
||||
char *out = NULL;
|
||||
size_t len = 0, cap = 0;
|
||||
|
||||
while (word && word[i])
|
||||
{
|
||||
if (word[i] == '\'' && !in_double)
|
||||
{
|
||||
in_single = !in_single;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (word[i] == '"' && !in_single)
|
||||
{
|
||||
in_double = !in_double;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (word[i] == '$' && !in_single)
|
||||
{
|
||||
if (word[i + 1] == '?')
|
||||
{
|
||||
char *v = ms_itoa(sh->last_status);
|
||||
buf_append(&out, &len, &cap, v);
|
||||
free(v);
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
if (ms_is_alpha((unsigned char)word[i + 1]))
|
||||
{
|
||||
size_t j = i + 1;
|
||||
while (word[j] && ms_is_alnum((unsigned char)word[j]))
|
||||
j++;
|
||||
char *name = ms_strndup(word + i + 1, j - (i + 1));
|
||||
char *val = env_get(sh, name);
|
||||
buf_append(&out, &len, &cap, val ? val : "");
|
||||
free(name);
|
||||
i = j;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
buf_append_char(&out, &len, &cap, word[i]);
|
||||
i++;
|
||||
}
|
||||
if (!out)
|
||||
out = ms_strdup("");
|
||||
return out;
|
||||
}
|
||||
|
||||
int expand_pipeline(t_pipeline *p, t_shell *sh)
|
||||
{
|
||||
size_t i;
|
||||
int j;
|
||||
for (i = 0; i < p->count; i++)
|
||||
{
|
||||
for (j = 0; p->cmds[i]->argv && p->cmds[i]->argv[j]; j++)
|
||||
{
|
||||
char *neww = expand_word(p->cmds[i]->argv[j], sh);
|
||||
free(p->cmds[i]->argv[j]);
|
||||
p->cmds[i]->argv[j] = neww;
|
||||
}
|
||||
if (p->cmds[i]->redirs)
|
||||
{
|
||||
t_redir *r = p->cmds[i]->redirs;
|
||||
while (r)
|
||||
{
|
||||
if (r->target)
|
||||
{
|
||||
char *nt = expand_word(r->target, sh);
|
||||
free(r->target);
|
||||
r->target = nt;
|
||||
}
|
||||
r = r->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
131
minishell-codex/src/parser/lexer.c
Normal file
131
minishell-codex/src/parser/lexer.c
Normal file
@@ -0,0 +1,131 @@
|
||||
#include "minishell.h"
|
||||
|
||||
static t_token *token_new(t_tokentype type, const char *text, size_t len)
|
||||
{
|
||||
t_token *t = (t_token *)calloc(1, sizeof(t_token));
|
||||
if (!t)
|
||||
return NULL;
|
||||
t->type = type;
|
||||
if (text)
|
||||
t->text = ms_strndup(text, len);
|
||||
return t;
|
||||
}
|
||||
|
||||
static void token_add(t_token **head, t_token *new)
|
||||
{
|
||||
t_token *cur;
|
||||
if (!new)
|
||||
return;
|
||||
if (!*head)
|
||||
{
|
||||
*head = new;
|
||||
return;
|
||||
}
|
||||
cur = *head;
|
||||
while (cur->next)
|
||||
cur = cur->next;
|
||||
cur->next = new;
|
||||
}
|
||||
|
||||
void free_tokens(t_token *toks)
|
||||
{
|
||||
t_token *n;
|
||||
while (toks)
|
||||
{
|
||||
n = toks->next;
|
||||
free(toks->text);
|
||||
free(toks);
|
||||
toks = n;
|
||||
}
|
||||
}
|
||||
|
||||
static int is_meta(char c)
|
||||
{
|
||||
return (c == '|' || c == '<' || c == '>');
|
||||
}
|
||||
|
||||
static int read_word(const char *line, size_t *i, t_token **out)
|
||||
{
|
||||
size_t start = *i;
|
||||
int in_single = 0;
|
||||
int in_double = 0;
|
||||
|
||||
while (line[*i])
|
||||
{
|
||||
if (line[*i] == '\'' && !in_double)
|
||||
in_single = !in_single;
|
||||
else if (line[*i] == '"' && !in_single)
|
||||
in_double = !in_double;
|
||||
else if (!in_single && !in_double)
|
||||
{
|
||||
if (ms_is_space(line[*i]) || is_meta(line[*i]))
|
||||
break;
|
||||
}
|
||||
(*i)++;
|
||||
}
|
||||
if (in_single || in_double)
|
||||
return 1;
|
||||
*out = token_new(TOK_WORD, line + start, *i - start);
|
||||
return 0;
|
||||
}
|
||||
|
||||
t_token *lex_line(const char *line, int *error)
|
||||
{
|
||||
t_token *toks = NULL;
|
||||
size_t i = 0;
|
||||
|
||||
*error = 0;
|
||||
while (line[i])
|
||||
{
|
||||
if (ms_is_space(line[i]))
|
||||
{
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (line[i] == '|')
|
||||
{
|
||||
token_add(&toks, token_new(TOK_PIPE, "|", 1));
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (line[i] == '<')
|
||||
{
|
||||
if (line[i + 1] == '<')
|
||||
{
|
||||
token_add(&toks, token_new(TOK_HEREDOC, "<<", 2));
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
token_add(&toks, token_new(TOK_REDIR_IN, "<", 1));
|
||||
i++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (line[i] == '>')
|
||||
{
|
||||
if (line[i + 1] == '>')
|
||||
{
|
||||
token_add(&toks, token_new(TOK_REDIR_APPEND, ">>", 2));
|
||||
i += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
token_add(&toks, token_new(TOK_REDIR_OUT, ">", 1));
|
||||
i++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
{
|
||||
t_token *w = NULL;
|
||||
if (read_word(line, &i, &w))
|
||||
{
|
||||
*error = 1;
|
||||
free_tokens(toks);
|
||||
return NULL;
|
||||
}
|
||||
token_add(&toks, w);
|
||||
}
|
||||
}
|
||||
return toks;
|
||||
}
|
||||
175
minishell-codex/src/parser/parser.c
Normal file
175
minishell-codex/src/parser/parser.c
Normal file
@@ -0,0 +1,175 @@
|
||||
#include "minishell.h"
|
||||
|
||||
static t_command *command_new(void)
|
||||
{
|
||||
t_command *cmd = (t_command *)calloc(1, sizeof(t_command));
|
||||
return cmd;
|
||||
}
|
||||
|
||||
static void command_add_arg(t_command *cmd, const char *text)
|
||||
{
|
||||
char **new_argv;
|
||||
int i;
|
||||
|
||||
if (!cmd || !text)
|
||||
return;
|
||||
i = cmd->argc;
|
||||
new_argv = (char **)calloc((size_t)i + 2, sizeof(char *));
|
||||
if (!new_argv)
|
||||
return;
|
||||
for (int j = 0; j < i; j++)
|
||||
new_argv[j] = cmd->argv[j];
|
||||
new_argv[i] = ms_strdup(text);
|
||||
new_argv[i + 1] = NULL;
|
||||
free(cmd->argv);
|
||||
cmd->argv = new_argv;
|
||||
cmd->argc += 1;
|
||||
}
|
||||
|
||||
static void command_add_redir(t_command *cmd, t_redirtype type, const char *target)
|
||||
{
|
||||
t_redir *r = (t_redir *)calloc(1, sizeof(t_redir));
|
||||
t_redir *cur;
|
||||
if (!r)
|
||||
return;
|
||||
r->type = type;
|
||||
r->target = ms_strdup(target);
|
||||
r->fd = -1;
|
||||
r->next = NULL;
|
||||
if (!cmd->redirs)
|
||||
{
|
||||
cmd->redirs = r;
|
||||
return;
|
||||
}
|
||||
cur = cmd->redirs;
|
||||
while (cur->next)
|
||||
cur = cur->next;
|
||||
cur->next = r;
|
||||
}
|
||||
|
||||
static void free_redirs(t_redir *r)
|
||||
{
|
||||
t_redir *n;
|
||||
while (r)
|
||||
{
|
||||
n = r->next;
|
||||
if (r->fd != -1)
|
||||
close(r->fd);
|
||||
free(r->target);
|
||||
free(r);
|
||||
r = n;
|
||||
}
|
||||
}
|
||||
|
||||
void free_pipeline(t_pipeline *p)
|
||||
{
|
||||
size_t i;
|
||||
if (!p)
|
||||
return;
|
||||
for (i = 0; i < p->count; i++)
|
||||
{
|
||||
if (p->cmds[i])
|
||||
{
|
||||
for (int j = 0; p->cmds[i]->argv && p->cmds[i]->argv[j]; j++)
|
||||
free(p->cmds[i]->argv[j]);
|
||||
free(p->cmds[i]->argv);
|
||||
free(p->cmds[i]->path);
|
||||
free_redirs(p->cmds[i]->redirs);
|
||||
free(p->cmds[i]);
|
||||
}
|
||||
}
|
||||
free(p->cmds);
|
||||
free(p);
|
||||
}
|
||||
|
||||
static int pipeline_add_cmd(t_pipeline *p, t_command *cmd)
|
||||
{
|
||||
t_command **new_cmds;
|
||||
size_t i;
|
||||
|
||||
new_cmds = (t_command **)calloc(p->count + 1, sizeof(t_command *));
|
||||
if (!new_cmds)
|
||||
return 1;
|
||||
for (i = 0; i < p->count; i++)
|
||||
new_cmds[i] = p->cmds[i];
|
||||
new_cmds[p->count] = cmd;
|
||||
free(p->cmds);
|
||||
p->cmds = new_cmds;
|
||||
p->count += 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
t_pipeline *parse_tokens(t_token *toks, int *error)
|
||||
{
|
||||
t_pipeline *p;
|
||||
t_command *cmd;
|
||||
t_token *cur;
|
||||
|
||||
*error = 0;
|
||||
p = (t_pipeline *)calloc(1, sizeof(t_pipeline));
|
||||
if (!p)
|
||||
return NULL;
|
||||
cmd = command_new();
|
||||
if (!cmd)
|
||||
{
|
||||
free(p);
|
||||
return NULL;
|
||||
}
|
||||
cur = toks;
|
||||
while (cur)
|
||||
{
|
||||
if (cur->type == TOK_PIPE)
|
||||
{
|
||||
if (cmd->argc == 0)
|
||||
{
|
||||
*error = 1;
|
||||
free_pipeline(p);
|
||||
free(cmd);
|
||||
return NULL;
|
||||
}
|
||||
pipeline_add_cmd(p, cmd);
|
||||
cmd = command_new();
|
||||
if (!cmd)
|
||||
{
|
||||
*error = 1;
|
||||
free_pipeline(p);
|
||||
return NULL;
|
||||
}
|
||||
cur = cur->next;
|
||||
continue;
|
||||
}
|
||||
if (cur->type == TOK_REDIR_IN || cur->type == TOK_REDIR_OUT
|
||||
|| cur->type == TOK_REDIR_APPEND || cur->type == TOK_HEREDOC)
|
||||
{
|
||||
if (!cur->next || cur->next->type != TOK_WORD)
|
||||
{
|
||||
*error = 1;
|
||||
free_pipeline(p);
|
||||
free(cmd);
|
||||
return NULL;
|
||||
}
|
||||
if (cur->type == TOK_REDIR_IN)
|
||||
command_add_redir(cmd, REDIR_IN, cur->next->text);
|
||||
else if (cur->type == TOK_REDIR_OUT)
|
||||
command_add_redir(cmd, REDIR_OUT, cur->next->text);
|
||||
else if (cur->type == TOK_REDIR_APPEND)
|
||||
command_add_redir(cmd, REDIR_APPEND, cur->next->text);
|
||||
else if (cur->type == TOK_HEREDOC)
|
||||
command_add_redir(cmd, REDIR_HEREDOC, cur->next->text);
|
||||
cur = cur->next->next;
|
||||
continue;
|
||||
}
|
||||
if (cur->type == TOK_WORD)
|
||||
command_add_arg(cmd, cur->text);
|
||||
cur = cur->next;
|
||||
}
|
||||
if (cmd->argc == 0)
|
||||
{
|
||||
*error = 1;
|
||||
free_pipeline(p);
|
||||
free(cmd);
|
||||
return NULL;
|
||||
}
|
||||
pipeline_add_cmd(p, cmd);
|
||||
return p;
|
||||
}
|
||||
6
minishell-codex/src/utils/char_utils.c
Normal file
6
minishell-codex/src/utils/char_utils.c
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "minishell.h"
|
||||
|
||||
int ms_is_space(int c) { return (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f'); }
|
||||
int ms_is_alpha(int c) { return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_'); }
|
||||
int ms_is_digit(int c) { return (c >= '0' && c <= '9'); }
|
||||
int ms_is_alnum(int c) { return (ms_is_alpha(c) || ms_is_digit(c)); }
|
||||
67
minishell-codex/src/utils/split_utils.c
Normal file
67
minishell-codex/src/utils/split_utils.c
Normal file
@@ -0,0 +1,67 @@
|
||||
#include "minishell.h"
|
||||
|
||||
static size_t count_parts(const char *s, char delim)
|
||||
{
|
||||
size_t count = 0;
|
||||
int in = 0;
|
||||
while (*s)
|
||||
{
|
||||
if (*s == delim)
|
||||
in = 0;
|
||||
else if (!in)
|
||||
{
|
||||
in = 1;
|
||||
count++;
|
||||
}
|
||||
s++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
char **ms_split(const char *s, char delim)
|
||||
{
|
||||
char **out;
|
||||
size_t parts;
|
||||
size_t i = 0;
|
||||
size_t start = 0;
|
||||
size_t len = 0;
|
||||
int in = 0;
|
||||
|
||||
if (!s)
|
||||
return NULL;
|
||||
parts = count_parts(s, delim);
|
||||
out = (char **)calloc(parts + 1, sizeof(char *));
|
||||
if (!out)
|
||||
return NULL;
|
||||
while (s[i])
|
||||
{
|
||||
if (s[i] == delim)
|
||||
{
|
||||
if (in)
|
||||
{
|
||||
out[len++] = ms_strndup(s + start, i - start);
|
||||
in = 0;
|
||||
}
|
||||
}
|
||||
else if (!in)
|
||||
{
|
||||
in = 1;
|
||||
start = i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (in)
|
||||
out[len++] = ms_strndup(s + start, i - start);
|
||||
out[len] = NULL;
|
||||
return out;
|
||||
}
|
||||
|
||||
void ms_free_split(char **sp)
|
||||
{
|
||||
size_t i = 0;
|
||||
if (!sp)
|
||||
return;
|
||||
while (sp[i])
|
||||
free(sp[i++]);
|
||||
free(sp);
|
||||
}
|
||||
100
minishell-codex/src/utils/str_utils.c
Normal file
100
minishell-codex/src/utils/str_utils.c
Normal file
@@ -0,0 +1,100 @@
|
||||
#include "minishell.h"
|
||||
|
||||
char *ms_strdup(const char *s)
|
||||
{
|
||||
char *out;
|
||||
size_t len;
|
||||
|
||||
if (!s)
|
||||
return NULL;
|
||||
len = strlen(s);
|
||||
out = (char *)malloc(len + 1);
|
||||
if (!out)
|
||||
return NULL;
|
||||
memcpy(out, s, len);
|
||||
out[len] = '\0';
|
||||
return out;
|
||||
}
|
||||
|
||||
char *ms_strndup(const char *s, size_t n)
|
||||
{
|
||||
char *out;
|
||||
if (!s)
|
||||
return NULL;
|
||||
out = (char *)malloc(n + 1);
|
||||
if (!out)
|
||||
return NULL;
|
||||
memcpy(out, s, n);
|
||||
out[n] = '\0';
|
||||
return out;
|
||||
}
|
||||
|
||||
char *ms_substr(const char *s, size_t start, size_t len)
|
||||
{
|
||||
size_t slen;
|
||||
if (!s)
|
||||
return NULL;
|
||||
slen = strlen(s);
|
||||
if (start >= slen)
|
||||
return ms_strdup("");
|
||||
if (start + len > slen)
|
||||
len = slen - start;
|
||||
return ms_strndup(s + start, len);
|
||||
}
|
||||
|
||||
char *ms_strjoin(const char *a, const char *b)
|
||||
{
|
||||
size_t la;
|
||||
size_t lb;
|
||||
char *out;
|
||||
|
||||
if (!a || !b)
|
||||
return NULL;
|
||||
la = strlen(a);
|
||||
lb = strlen(b);
|
||||
out = (char *)malloc(la + lb + 1);
|
||||
if (!out)
|
||||
return NULL;
|
||||
memcpy(out, a, la);
|
||||
memcpy(out + la, b, lb);
|
||||
out[la + lb] = '\0';
|
||||
return out;
|
||||
}
|
||||
|
||||
char *ms_strjoin3(const char *a, const char *b, const char *c)
|
||||
{
|
||||
char *ab;
|
||||
char *abc;
|
||||
|
||||
ab = ms_strjoin(a, b);
|
||||
if (!ab)
|
||||
return NULL;
|
||||
abc = ms_strjoin(ab, c);
|
||||
free(ab);
|
||||
return abc;
|
||||
}
|
||||
|
||||
char *ms_itoa(int n)
|
||||
{
|
||||
char buf[32];
|
||||
int i;
|
||||
int neg;
|
||||
long nb;
|
||||
|
||||
nb = n;
|
||||
neg = (nb < 0);
|
||||
if (neg)
|
||||
nb = -nb;
|
||||
i = 30;
|
||||
buf[31] = '\0';
|
||||
if (nb == 0)
|
||||
buf[i--] = '0';
|
||||
while (nb > 0)
|
||||
{
|
||||
buf[i--] = (char)('0' + (nb % 10));
|
||||
nb /= 10;
|
||||
}
|
||||
if (neg)
|
||||
buf[i--] = '-';
|
||||
return ms_strdup(&buf[i + 1]);
|
||||
}
|
||||
Reference in New Issue
Block a user