BUAA OS LAB6-SHELL 大作业报告

前言

本来一开始准备写swap,但在长达四天的swap奋战后,OS大作业取得了惊人的突破:转战shell

总计增删代码量为 2176 行。

相比于swap里的云里雾里,shell指导书相对清晰有逻辑。

然后,感谢懒鱼提供思路,对于内核态的修改路径帮助很大,虽然解析还是使用课程组的史山。不过能有OO纯正则解决表达式难?

BUAA-OO-unit1 | wrongization

北航2025 OS shell挑战性任务实现指导 | Lazyfish & chilly_river

实现要求

参见:任务要求

功能实现

不带 .b 后缀指令

修改spawn.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int spawn(char *prog, char **argv) {
// Step 1: Open the file 'prog' (the path of the program).
// Return the error if 'open' fails.
int fd;
debugf("");///?????????????
if ((fd = open(prog, O_RDONLY)) < 0) {
//new
char buf1[MAXPATHLEN + 3]="/";
strcat(buf1 , prog);
strcat(buf1 , ".b");
fd = open(buf1, O_RDONLY);
if (fd < 0) {
return fd;
}
//
//return fd;
}
.......
}

支持相对路径

相对路径修改进程属性,增加路径数组。

1
2
3
4
5
// Challenge shell new
int env_exit_code;
char env_dir[MAX_PATH_LEN];
int env_var_count;
struct EnvVar env_var[MAX_ENV_VAR_COUNT];

fs中open remove creat调用修改使用时合并后的绝对路径

executor里ls mkdir cd命令修改使用时合并后的绝对路径

merge_path输出合并后的绝对路径,同时简化路径,实现对... 的支持。

内建指令cd、pwd、exit

1
2
3
4
5
6
7
//详细代码参见附录
// 执行cd命令
int execute_cd(int argc, char **argv);
// 执行pwd命令
int execute_pwd(int argc, char **argv);
// 执行exit命令
int execute_exit(int argc, char **argv);

环境变量管理

本地变量通过全局数组存储,全局通过内核进程属性传递修改,相关操作新增加系统调用路径

新增:新系统调用请求类型SYS_get_env_dir

调用路径:(user) syscall_get_env_dir -> (lib) msyscall(SYS_get_env_dir, envid, dir) -> (kern) sys_get_env_dir

1
2
static int local_var_count;
static struct LocalVar local_var[MAX_LOCAL_VARCOUNT];

判断为本地或者全局后调用函数或者系统调用

1
2
3
4
5
6
7
//local变量
int set_local_var(char* key, char* value, int writable);
int remove_local_var(char* key);
//declare和unset
int execute_declare(int argc, char **argv);
int set_variable(int is_env_var, char* key, char* value, int writable) ;
int execute_unset(int argc, char **argv);

父子shell参数传递

父进程能够将工作路径和全局环境变量传递给子进程,通过修改进程fork时的系统调用exo_fork传递

1
2
3
4
5
6
7
8
9
//exo_fork new
if (curenv->env_id != 0) {
strcpy(e->env_dir, curenv->env_dir);
e->env_var_count = curenv->env_var_count;
for (int i = 0; i < curenv->env_var_count; ++i) {
e->env_var[i] = curenv->env_var[i];
}
}
//

历史指令

输出历史命令列表内建指令:

1
int execute_history(int argc, char **argv);

本地使用数组存储

1
static char cmd_history[HISTORY_FILE_SIZE + 1][MAX_COMMAND_LENGTH];

每次read_command调用store_command保存至HISTORY_FILE=="/.HISTORY_FILE"文件,每个shell启动调用history_init(),实现所有shell共享历史记录。

指令自由输入、快捷键

破除忙等待,优化效率

1
2
3
4
5
6
7
8
9
10
11
int sys_cgetc(void) {
int ch = scancharc();
return ch;
}

/*int sys_cgetc(void) {
int ch;
while ((ch = scancharc()) == 0) {
}
return ch;
}*/

之所以这么做,是因为用户态下,console.c中的cons_read()函数已经有了如下实现:

1
2
3
while ((c = syscall_cgetc()) == 0) {
syscall_yield();
}

read_command(char* command, int n)实现加载历史记录和光标控制和快捷键解析。

更多指令

使用时合并后的绝对路径

rm 调用fs的remove

touch,mkdir 新增create系统调用,创建文件和目录

修改fsipc_create,增加目录支持(新增参数isdir值判断)

新文件系统请求:(fereq)FSREQ_CREATE

新请求结构体:struct Fsreq_create

调用路径:(file)create -> (fsipc) fsipc_create -> (fsipc) fsipc(FSREQ_CREATE) -> (serv)serve_create -> (fs) file_create

1
2
3
4
5
6
7
8
9
//new
int fsipc_create(const char* path, int isdir) {
struct Fsreq_create *req;
req = (struct Fsreq_create *)fsipcbuf;
strcpy(req->req_path, path);
req->req_isdir = isdir;
return fsipc(FSREQ_CREATE, fsipcbuf, 0, 0);
}
//

实现注释功能

1
int replace_sign(char* command)

暴力找到第一次出现的#,去掉其后的字符。

实现一行多指令

1
2
3
4
//计算" ; "个数
int count_mulline(char* command)
//取出第n+1个指令
void devide_mulline(char* command, char* single, int n);

多行指令读取分号个数,按照个数通过循环依次读取单条指令sh.c中循环运行主框架代码块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main(int argc, char **argv) {
......
history_init();
for (;;) {
......
char singlebuf[MAX_COMMAND_LENGTH];
int cmds = count_mulline(buf);
for(int i=0;i<=cmds;i++){
devide_mulline(buf, singlebuf, i);
......
//主体
......
}
}
close_all();
return 0;
}

实现反引号

1
2
int replace_backquote(char* command);
static int execute_subprocess(char* cmd_buffer, char* output_buffer, int* position, int buffer_size);

通过识别反引号中的内容,放入fork出的子进程中调用execute_subprocess运行,运行结果重定向标准输出到管道写端,之后主进程通过管道读取内容,替换反引号内部内容为文件为读取内容,递归继续执行直到没有反引号。

注意最外层的dup的保存和恢复。(savedup保存原始状态,之后resetdup恢复)

实现追加重定向

与重定向输出逻辑完全相同,只需要打开文件时将指针移动到文件末尾。

1
2
fstat(fd, &stat);
seek(fd,stat.st_size);

实现指令条件执行

修改exit实现退出时保存退出码用来区分正常退出(0)和异常退出(非0)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void exit(int exit_code) {
//void exit(void)
#if !defined(LAB) || LAB >= 5
close_all();
#endif

syscall_env_exit(0, exit_code);
//syscall_env_destroy(0);
user_panic("unreachable code");
}

const volatile struct Env *env;
extern int main(int, char **);

void libmain(int argc, char **argv) {
env = &envs[ENVX(syscall_getenvid())];

int exit_code = main(argc, argv);
//main(argc, argv);

exit(exit_code);
//exit();
}

修改wait实现等待子进程完成后销毁子进程,并且传递子进程exitcode到父进程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int wait(u_int envid) {
const volatile struct Env *e;

e = &envs[ENVX(envid)];
while (e->env_id == envid) {
if (e->env_status == ENV_FREE) {
return 1;
} else if (e->env_status == ENV_END) {
int exit_code = e->env_exit_code;
syscall_env_destroy(envid);
return exit_code;
}
syscall_yield();
}
return 1;
}

管道通过此功能从右向左依次传递,管道成功与否只看最右端,实现管道的指令整体执行成功与否判断。

执行时对于未知指令和空指令,如果在管道内仍然需要执行释放重定向,returncode为其右端返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if(*rightpipe!=0  && pipeend!=PIPE_JUDGE_MAINOUT ){//-1 pipeleft 
//对应中间
resetdup(tempfd);
returncode = wait(*rightpipe);
exit(returncode);
}
if(*rightpipe==0){
//对应没有子进程的正常指令
resetdup(tempfd);
if(pipeend==PIPE_JUDGE_PIPEEND){
////对应非正常指令,管道最右侧
exit(returncode);
}
}

pipeend: 管道中间 0 管道最右侧 1 不在管道的父进程 2

judge: AND(&&) 11 OR(||) 10

对于原课程组parser增加双字符token

使用课程组内的架构,rightpipe为子进程进程号,inpipe判断是否是在管道命令内,pipeend判断指令结尾状态,judge判断指令结尾条件执行状态

当有管道时,fork出的子进程递归解每一条指令,通过dup重定向,savedup保存原始状态,之后resetdup恢复,最右边子进程exit(),左边依次等待并且获取右边的returncode,最左边根据inpipe状态判断不执行和退出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
struct tempFd {
int size;
int temp[MAXTEMPDUP][2];
};
int savedup(struct tempFd * tempfd,int from){
struct Fd* temp;
fd_alloc(&temp);
dup(from,fd2num(temp));
tempfd->temp[tempfd->size][0] = from;
tempfd->temp[tempfd->size][1] = fd2num(temp);
tempfd->size++;
}
int resetdup(struct tempFd * tempfd){
if(tempfd->size>0){
for(int i=tempfd->size;i>0;i--){
dup((tempfd->temp)[i-1][1],(tempfd->temp)[i-1][0]);
close((tempfd->temp)[i-1][1]);
tempfd->size--;
}
}
}

主进程持续解析到条件执行符号,按照返回值条件选择是否运行

(解析照常向右进行不停止,直到结尾空或者条件运行符号)

1
2
3
if(*pipeend==PIPE_JUDGE_MAINOUT ){
continue;
}

子进程也最多解析到条件执行符号左侧,条件执行符号不会计入解析结果中

运行时只有主进程返回值覆盖returncode,子进程只运行,并且子进程运行完成后会exit()退出销毁,不会进入之后的条件运行解析里面,只有未销毁的主进程进入,直到判断主进程右侧没有条件运行符号则主进程结束,条件执行符号不会计入解析结果中但是gettoken仍然会调用,使得跳过该符号,如果右侧有则会正确找到下一部分指令的开头。

反引号调用带多行指令循环的主框架代码块,只去除read_command实时读取指令和反引号解析(因为没有递归反引号)

附录:修改代码一览

fs/fs.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int file_create(char *path, struct File **file, int isdir) {
char name[MAXNAMELEN];
int r;
struct File *dir, *f;

if ((r = walk_path(path, &dir, &f, name)) == 0) {
return -E_FILE_EXISTS;
}

if (r != -E_NOT_FOUND || dir == 0) {
return r;
}

if (dir_alloc_file(dir, &f) < 0) {
return r;
}

strcpy(f->f_name, name);
//new
f->f_type = isdir ? FTYPE_DIR : FTYPE_REG;//!!!!!!!!!!!!!!!!!!!!!!!!判断类型
//
*file = f;
return 0;
}

fs/serv.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
void serve_open(u_int envid, struct Fsreq_open *rq) {
......

if ((rq->req_omode & O_CREAT) && (r = file_create(rq->req_path, &f, 0)) < 0 &&
r != -E_FILE_EXISTS) {
///file_create(rq->req_path, &f, 0)添加参数
ipc_send(envid, r, 0, 0);
return;
}
......
}
........

//new
void serve_create(u_int envid, struct Fsreq_create *rq) {
struct File* f;
int r = file_create(rq->req_path, &f, rq->req_isdir);
ipc_send(envid, r, 0, 0);
}
//


void *serve_table[MAX_FSREQNO] = {
[FSREQ_OPEN] = serve_open, [FSREQ_MAP] = serve_map, [FSREQ_SET_SIZE] = serve_set_size,
[FSREQ_CLOSE] = serve_close, [FSREQ_DIRTY] = serve_dirty, [FSREQ_REMOVE] = serve_remove,
[FSREQ_SYNC] = serve_sync, [FSREQ_CREATE] = serve_create,
//[FSREQ_CREATE] = serve_create新添
};

fs/serv.h

1
int file_create(char *path, struct File **file, int isdir);//isdir新增

include/env.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//new
#define ENV_END 3

#define MAX_PATH_LEN 1024
#define MAX_ENV_KEY_LEN 50
#define MAX_ENV_VALUE_LEN 50
#define MAX_ENV_VAR_COUNT 50

struct EnvVar {
char key[MAX_ENV_KEY_LEN ];
char value[MAX_ENV_VALUE_LEN];
int writable;
};
void env_exit(struct Env *e, int exit_code);
//


struct Env {
.....
.....

// Challenge shell new
int env_exit_code;
char env_dir[MAX_PATH_LEN];
int env_var_count;
struct EnvVar env_var[MAX_ENV_VAR_COUNT];
};
......

include/error.h

1
2
3
4
5
6
7
8
// Env var not found
#define E_ENV_VAR_NOT_FOUND 14

// Invalid set operation to env var
#define E_INVALID_SET_ENV_VAR 15

// Env var is full
#define E_NO_FREE_ENV_VAR 16

include/string.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#ifndef _STRING_H_
#define _STRING_H_

#include <types.h>

void *memcpy(void *dst, const void *src, size_t n);
void *memset(void *dst, int c, size_t n);
size_t strlen(const char *s);
char *strcpy(char *dst, const char *src);
const char *strchr(const char *s, int c);
int strcmp(const char *p, const char *q);
//new
char * strcat (char *dest, const char *src);
int strncmp(const char* str1, const char* str2, int n);
char* strstr(const char* str1, const char* str2);
int isvar(int c) ;
int isalnum(int c);
int isalpha(int c);
char *strrchr(const char *s, int c);
char *strncpy(char *dest, const char *src, size_t n);
void* memmove(void* dest, const void* sour, size_t num);
void merge_path(char* new_dir, const char* prev_dir, const char* next_dir);
int count_mulline(char* command);
void devide_mulline(char* command, char* single, int n);
//
#endif

include/syscall.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
enum {
SYS_putchar,
SYS_print_cons,
SYS_getenvid,
SYS_yield,
SYS_env_destroy,
SYS_env_exit,//new
SYS_set_tlb_mod_entry,
SYS_mem_alloc,
SYS_mem_map,
SYS_mem_unmap,
SYS_exofork,
SYS_set_env_status,
SYS_set_trapframe,
SYS_panic,
SYS_ipc_try_send,
SYS_ipc_recv,
SYS_cgetc,
SYS_write_dev,
SYS_read_dev,
SYS_get_env_dir,//new
SYS_set_env_dir,//new
SYS_get_env_var,//new
SYS_set_env_var,//new
SYS_remove_env_var,//new
SYS_get_env_var_count,//new
SYS_get_env_id_var,//new
MAX_SYSNO,
};

kern/env.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

struct Env *env_create(const void *binary, size_t size, int priority) {
struct Env *e;
env_alloc(&e, 0);
e->env_pri = priority;
e->env_status = ENV_RUNNABLE;
//new路径
strcpy(e->env_dir, "/");
e->env_var_count = 0;
//
load_icode(e, binary, size);
TAILQ_INSERT_HEAD(&env_sched_list, e, env_sched_link);
return e;
}

//new
void env_exit(struct Env *e, int exit_code) {
e->env_exit_code = exit_code;
if (e->env_parent_id == 0) {
env_destroy(e);
} else {
TAILQ_REMOVE(&env_sched_list, (e), env_sched_link);
e->env_status = ENV_END;
if (curenv == e) {
curenv = NULL;
printk("i am exiting ...\n");
schedule(1);
}
}
}
//

kern/syscall_all.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
int sys_cgetc(void) {
int ch = scancharc();
return ch;
}

/*int sys_cgetc(void) {
int ch;
while ((ch = scancharc()) == 0) {
}
return ch;
}*/




//new
int sys_env_exit(u_int envid, int exit_code) {
struct Env *e;
try(envid2env(envid, &e, 1));

//printk("[%08x] env %08x exit, code:%d\n", curenv->env_id, e->env_id, exit_code);
env_exit(e, exit_code);
return 0;
}
//
int sys_exofork(void) {
struct Env *e;

try(env_alloc(&e, curenv->env_id));

e->env_tf = *((struct Trapframe*)KSTACKTOP - 1);

e->env_tf.regs[2] = 0;

e->env_status = ENV_NOT_RUNNABLE;
e->env_pri = curenv->env_pri;
// Copy env_dir from current env to new env. */
if (curenv->env_id != 0) {
strcpy(e->env_dir, curenv->env_dir);
e->env_var_count = curenv->env_var_count;
for (int i = 0; i < curenv->env_var_count; i++) {
e->env_var[i] = curenv->env_var[i];
}
}
//
return e->env_id;
}




//new
int sys_get_env_dir(u_int envid, char* dir) {
struct Env *e;
try(envid2env(envid, &e, 1));
strcpy(dir, e->env_dir);
return 0;
}

// There is no way to check dir, because filesystem is not in kernel mode.
int sys_set_env_dir(u_int envid, char* dir) {
struct Env *e;
try(envid2env(envid, &e, 1));
strcpy(e->env_dir, dir);
return 0;
}

int sys_get_env_var(u_int envid, char* key, char* value) {
struct Env *e;
try(envid2env(envid, &e, 1));
for (int i = 0; i < e->env_var_count; i++)
{
if (strcmp(key, e->env_var[i].key) == 0)
{
strcpy(value, e->env_var[i].value);
return 0;
}
}
return -E_ENV_VAR_NOT_FOUND;
}

int sys_set_env_var(u_int envid, char* key, char* value, int writable) {
struct Env *e;
try(envid2env(envid, &e, 1));
for (int i = 0; i < e->env_var_count; i++)
{
if (strcmp(key, e->env_var[i].key) == 0)
{
if (e->env_var[i].writable) {
strcpy(e->env_var[i].value, value);
return 0 ;
} else {
return -E_INVALID_SET_ENV_VAR;
}
}
}
if (e->env_var_count == MAXVARCOUNT) {
return -E_NO_FREE_ENV_VAR;
}
int lastvar_loca = e->env_var_count;
e->env_var_count ++;
e->env_var[lastvar_loca].writable = writable;
strcpy(e->env_var[lastvar_loca].key, key);
strcpy(e->env_var[lastvar_loca].value, value);
return 0;
}

int sys_remove_env_var(u_int envid, char* key) {
struct Env *e;
int rm_id = -1;
try(envid2env(envid, &e, 1));
for (int i = 0; i < e->env_var_count; i++)
{
if (strcmp(key, e->env_var[i].key) == 0)
{
if (e->env_var[i].writable) {
rm_id = i;
break;
} else {
return -E_INVALID_SET_ENV_VAR;
}
}
}
if (rm_id < 0) {
return -E_ENV_VAR_NOT_FOUND;
}
for (int i = rm_id; i + 1 < e->env_var_count; i++) {
e->env_var[i] = e->env_var[i + 1];
}
e->env_var_count --;
return 0;
}

int sys_get_env_var_count(u_int envid) {
struct Env *e;
try(envid2env(envid, &e, 1));
return e->env_var_count;
}

int sys_get_env_id_var(u_int envid, int id, char* key, char* value) {
struct Env *e;
try(envid2env(envid, &e, 1));
if (id < 0 || id >= e->env_var_count) {
return -E_ENV_VAR_NOT_FOUND;
}
strcpy(key, e->env_var[id].key);
strcpy(value, e->env_var[id].value);
return 0;
}
//



void *syscall_table[MAX_SYSNO] = {
[SYS_putchar] = sys_putchar,
[SYS_print_cons] = sys_print_cons,
[SYS_getenvid] = sys_getenvid,
[SYS_yield] = sys_yield,
[SYS_env_destroy] = sys_env_destroy,
[SYS_env_exit] = sys_env_exit,//new
[SYS_set_tlb_mod_entry] = sys_set_tlb_mod_entry,
[SYS_mem_alloc] = sys_mem_alloc,
[SYS_mem_map] = sys_mem_map,
[SYS_mem_unmap] = sys_mem_unmap,
[SYS_exofork] = sys_exofork,
[SYS_set_env_status] = sys_set_env_status,
[SYS_set_trapframe] = sys_set_trapframe,
[SYS_panic] = sys_panic,
[SYS_ipc_try_send] = sys_ipc_try_send,
[SYS_ipc_recv] = sys_ipc_recv,
[SYS_cgetc] = sys_cgetc,
[SYS_write_dev] = sys_write_dev,
[SYS_read_dev] = sys_read_dev,
[SYS_get_env_dir] = sys_get_env_dir,//new
[SYS_set_env_dir] = sys_set_env_dir,//new
[SYS_get_env_var] = sys_get_env_var,//new
[SYS_set_env_var] = sys_set_env_var,//new
[SYS_remove_env_var] = sys_remove_env_var,//new
[SYS_get_env_var_count] = sys_get_env_var_count,//new
[SYS_get_env_id_var] = sys_get_env_id_var, //new
};

lib/string.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
//new
char * strcat (char *dest, const char *src)
{
//通过strcpy来实现strcat函数
strcpy (dest + strlen (dest), src);
return dest;
}
int strncmp(const char* str1, const char* str2, int n)
{
if (!n) //n=0时,无字符要比,直接return 0
return 0;
while (--n && *str1 && *str1 == *str2) //当字符相等且不为’\0‘时比较下个字符,知道n=0比完
{
str1++;
str2++;
}
return *str1 - *str2;//字符不相等时,(*str1 - *str2)可以满足返回值正负的需求
}
char* strstr(const char* str1, const char* str2)
{
if(str1 ==NULL || str2==NULL){
return str1;
}
const char* s1 = str1;
const char* s2 = str2;
const char* p = str1;
while (*p!='\0')
{
s1 =p ;
s2 = str2;
while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return (char*)p;
}
p++;
}
return NULL;
}
int isvar(int c) {
if (isalnum(c)){
return 1;
} else if (c == '_') {
return 1;
}
return 0;
}
int isalnum(int c)
{
if ( (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') ) return 8;

return 0;
}
int isalpha(int c)
{
if ( (c>='a' && c<='z') || (c>='A' && c<='Z') ) return 1024;

return 0;
}

char *strrchr(const char *s, int c)
{
if(s == NULL)
{
return NULL;
}

char *p_char = NULL;
while(*s != '\0')
{
if(*s == (char)c)
{
p_char = (char *)s;
}
s++;
}

return p_char;
}
char *strncpy(char *dest, const char *src, size_t n) {
char *dest_save = dest;
while (n && *src) {
*dest++ = *src++;
n--;
}
while (n--) {
*dest++ = '\0'; // 如果 n 大于 src 字符串的长度,填充剩余位置为空字符
}
return dest_save;
}

void* memmove(void* dest, const void* sour, size_t num)
{
if (dest < sour)
{
for (int i = 0; i <= num; i++)
{
*((char*)dest + i) = *((char*)sour + i);
}
return dest;
}
else if (dest > sour)
{
for (int i = num; i >=0; i--)
{
*((char*)dest + i) = *((char*)sour + i);
}
return dest;
}
else
{
return dest;
}
}
/合并路径
void merge_path(char* new_dir, const char* prev_dir, const char* next_dir) {
// 处理绝对路径情况
if (next_dir[0] == '/') {
strcpy(new_dir, next_dir);
}
// 处理相对路径拼接
else {
strcpy(new_dir, prev_dir);
size_t len = strlen(new_dir);

// 确保目录结尾有分隔符
if (len > 0 && new_dir[len - 1] != '/') {
strcat(new_dir, "/");
}
strcat(new_dir, next_dir);
}

// 移除末尾冗余分隔符(根目录除外)
size_t len = strlen(new_dir);
if (len > 1 && new_dir[len - 1] == '/') {
new_dir[len - 1] = '\0';
}

// 路径规范化处理 - 修复:使用原始字符串的副本进行处理
char temp_path[1024];
strcpy(temp_path, new_dir); // 创建原始路径的副本

char buf[1024] = {0}; // 结果缓冲区
int buf_len = 0; // 缓冲区长度

char *component = temp_path;
if (*component == '/') {
// 处理绝对路径起始
buf[buf_len++] = '/';
component++;
}

// 分割路径为组件
while (*component) {
char *next_slash = strchr(component, '/');
int comp_len = next_slash ? next_slash - component : strlen(component);

// 处理当前组件
if (comp_len == 2 && strncmp(component, "..", 2) == 0) {
// 处理上级目录:回退一级
while (buf_len > 0 && buf[buf_len - 1] != '/') {
buf_len--;
}
if (buf_len > 0) buf_len--; // 移除前一个'/'
}
else if (!(comp_len == 1 && *component == '.')) {
// 忽略".",处理其他组件
if (buf_len > 0 && buf[buf_len - 1] != '/') {
buf[buf_len++] = '/'; // 添加分隔符
}
strncpy(buf + buf_len, component, comp_len);
buf_len += comp_len;
}

// 移动到下一个组件
component = next_slash ? next_slash + 1 : component + comp_len;
}

// 处理根目录情况
if (buf_len == 0) {
strcpy(new_dir, "/");
} else {
buf[buf_len] = '\0';
strcpy(new_dir, buf);
}
}
//计算" ; "个数
int count_mulline(char* command){
char * c=command;
int num=0;
while(*c!='\0'){
if(*c==';'){
num++;
}
c++;
}
return num;
}
//取出第n+1个指令
void devide_mulline(char* command, char* single, int n) {
int count = 0; // 用于计数当前是第几个子串
char* start = command; // 当前子串的起始位置
char* p = command; // 遍历指针

// 遍历整个字符串
while (*p != '\0') {
if (*p == ';') {
// 当遇到分号时,检查是否是需要提取的子串
if (count == n) {
// 复制当前子串到single
while (start < p) {
*single++ = *start++;
}
*single = '\0'; // 添加字符串结束符
return;
}
count++; // 子串计数增加
start = p + 1; // 下一个子串的起始位置
}
p++;
}

// 处理字符串末尾的情况(最后一个子串)
if (count == n) {
// 复制最后一个子串
while (*start != '\0' && *start != ';') {
*single++ = *start++;
}
*single = '\0';
} else {
// 如果n超出范围(即没有足够的子串),返回空字符串
*single = '\0';
}
}

user/include/fsreq.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
enum {
FSREQ_OPEN,
FSREQ_MAP,
FSREQ_SET_SIZE,
FSREQ_CLOSE,
FSREQ_DIRTY,
FSREQ_REMOVE,
FSREQ_SYNC,
FSREQ_CREATE,//new
MAX_FSREQNO,
};

struct Fsreq_create {
char req_path[MAXPATHLEN];
int req_isdir;
};

user/include/lib.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

// libos
void exit(int exit_code) __attribute__((noreturn));
//void exit(void) __attribute__((noreturn));

/// syscalls
int syscall_env_exit(u_int envid, int exit_code);
...
int syscall_get_env_dir(u_int envid, char* dir);
int syscall_set_env_dir(u_int envid, char* dir);
int syscall_get_env_var(u_int envid, char* key, char* value);
int syscall_set_env_var(u_int envid, char* key, char* value, int writable);
int syscall_remove_env_var(u_int envid, char* key);
int syscall_get_env_var_count(u_int envid);
int syscall_get_env_id_var(u_int envid, int id, char* key, char* value);

// wait.c
int wait(u_int envid);
//void wait(u_int envid);

// fsipc.c
int fsipc_create(const char*, int);

// file.c
int create(const char *path, int isdir);

user/lib/debugf.c

1
2
3
4
5
6
7
8
9
10
void _user_panic(const char *file, int line, const char *fmt, ...) {
debugf("panic at %s:%d: ", file, line);
va_list ap;
va_start(ap, fmt);
vdebugf(fmt, ap);
va_end(ap);
debugf("\n");
exit(1);
//exit();
}

user/lib/file.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
int open(const char *path, int mode) {
//new
char prev_path[MAXPATHLEN];
char new_path[MAXPATHLEN];
try(syscall_get_env_dir(0, prev_path));
merge_path(new_path, prev_path, path);
//
......
}
int remove(const char *path) {
//new
char prev_path[MAXPATHLEN];
char new_path[MAXPATHLEN];
try(syscall_get_env_dir(0, prev_path));
merge_path(new_path, prev_path, path);
//


return fsipc_remove(new_path);
//return fsipc_remove(path);
}
//new
int create(const char *path, int isdir) {
char prev_path[MAXPATHLEN];
char new_path[MAXPATHLEN];
try(syscall_get_env_dir(0, prev_path));
merge_path(new_path, prev_path, path);
return fsipc_create(new_path, isdir);
}
//

user/lib/fsipc.c

1
2
3
4
5
6
7
8
9
//new
int fsipc_create(const char* path, int isdir) {
struct Fsreq_create *req;
req = (struct Fsreq_create *)fsipcbuf;
strcpy(req->req_path, path);
req->req_isdir = isdir;
return fsipc(FSREQ_CREATE, fsipcbuf, 0, 0);
}
//

user/lib/libos.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void exit(int exit_code) {
//void exit(void)
#if !defined(LAB) || LAB >= 5
close_all();
#endif

syscall_env_exit(0, exit_code);
//syscall_env_destroy(0);
user_panic("unreachable code");
}

const volatile struct Env *env;
extern int main(int, char **);

void libmain(int argc, char **argv) {
env = &envs[ENVX(syscall_getenvid())];

int exit_code = main(argc, argv);
//main(argc, argv);


exit(exit_code);
//exit();
}

user/lib/spawn.c

不知道为啥必须debugf("")一下,不然syscall_set_trapframe会报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int spawn(char *prog, char **argv) {
// Step 1: Open the file 'prog' (the path of the program).
// Return the error if 'open' fails.
int fd;
debugf("");///?????????????
if ((fd = open(prog, O_RDONLY)) < 0) {
//new
char buf1[MAXPATHLEN + 3]="/";
strcat(buf1 , prog);
strcat(buf1 , ".b");
fd = open(buf1, O_RDONLY);
if (fd < 0) {
return fd;
}
//
//return fd;
}
.......
}

user/lib/syscall_lib.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
int syscall_env_exit(u_int envid, int exit_code) {
return msyscall(SYS_env_exit, envid, exit_code);
}

int syscall_get_env_dir(u_int envid, char* dir) {
return msyscall(SYS_get_env_dir, envid, dir);
}

int syscall_set_env_dir(u_int envid, char* dir) {
return msyscall(SYS_set_env_dir, envid, dir);
}

int syscall_get_env_var(u_int envid, char* key, char* value) {
return msyscall(SYS_get_env_var, envid, key, value);
}

int syscall_set_env_var(u_int envid, char* key, char* value, int writable) {
return msyscall(SYS_set_env_var, envid, key, value, writable);
}

int syscall_remove_env_var(u_int envid, char* key) {
return msyscall(SYS_remove_env_var, envid, key);
}

int syscall_get_env_var_count(u_int envid) {
return msyscall(SYS_get_env_var_count, envid);
}

int syscall_get_env_id_var(u_int envid, int id, char* key, char* value) {
return msyscall(SYS_get_env_id_var, envid, id, key, value);
}

user/lib/wait.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int wait(u_int envid) {
//void wait(u_int envid) {
//
const volatile struct Env *e;

e = &envs[ENVX(envid)];
while (e->env_id == envid) {
//new
if (e->env_status == ENV_FREE) {
return 1;
} else if (e->env_status == ENV_END) {
int exit_code = e->env_exit_code;
syscall_env_destroy(envid);
return exit_code;
}
//
syscall_yield();
}
//new return ;
return 1;
//
}

user/ls.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
void usage(void) {
printf("usage: ls [-dFl] [file...]\n");
exit(1);
//exit();
}

int main(int argc, char **argv) {
int i;

ARGBEGIN {
default:
usage();
case 'd':
case 'F':
case 'l':
flag[(u_char)ARGC()]++;
break;
}
ARGEND
//new
char cur_dir[MAXPATHLEN];
syscall_get_env_dir(0, cur_dir);
char dir[MAXPATHLEN];
//
if (argc == 0) {
ls(".", cur_dir);
//ls("/", "");
} else {
for (i = 0; i < argc; i++) {
//new
merge_path(dir, cur_dir, argv[i]);
//
ls(argv[i], dir);
//ls(argv[i], argv[i]);
}
}
printf("\n");
return 0;
}

user/testfdsharing.c

user/testpipe.c

user/testfpiperace.c

user/testptelibrary.c

1
2
3
//修改所有
exit(1);
//exit();

user/new.mk

1
2
3
4
5
6
7
8
9
10
INITAPPS +=

USERLIB += lib/shellio.o \
lib/parser.o \
lib/executor.o

USERAPPS += mkdir.b \
touch.b \
rm.b

user/mkdir.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <lib.h>


int flag[256];

int mkdir(char* path) {
struct Stat st;
int r = stat(path, &st);
if (r >= 0) {
if (flag['p']) {
return 0;
} else {
printf("mkdir: cannot create directory '%s': File exists\n", path);
return 1;
}
}
if (flag['p']) {
char cur_path[MAXPATHLEN];
syscall_get_env_dir(0, cur_path); // 获取当前目录

char full_path[MAXPATHLEN];
merge_path(full_path, cur_path, path); // 构建完整路径

// 定位最后一个目录分隔符
char *last_slash = strrchr(full_path, '/');

// 处理根目录特殊情况
if (last_slash == full_path) {
last_slash++; // 根目录保持为"/"
}

// 截断路径获取父目录
char parent_path[MAXPATHLEN];
if (last_slash) {
size_t parent_len = last_slash - full_path;
strncpy(parent_path, full_path, parent_len);
parent_path[parent_len] = '\0';
} else {
strcpy(parent_path, "."); // 无分隔符时使用当前目录
}

// 检查并创建父目录
int r = stat(parent_path, &st);
if (r == -E_NOT_FOUND) {
if ((r = mkdir(parent_path)) < 0) {
return r;
}
} else if (r < 0) {
return r;
}
}

r = create(path, 1);
if (r == -E_NOT_FOUND) {
printf("mkdir: cannot create directory '%s': No such file or directory\n", path);
return 1;
} else {
return 0;
}
}

void usage() {
printf("usage: mkdir [-p] <file>\n");
exit(1);
}

int main(int argc, char **argv) {
ARGBEGIN {
default:
usage();
case 'p':
flag[(u_char)ARGC()]++;
break;
}
ARGEND
if (argc != 1) {
usage();
}
int ret = mkdir(argv[0]);
return ret;
}

user/rm.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <lib.h>

int flag[256];

int rm(char *path)
{
struct Stat st;
int r = stat(path, &st);
if (r < 0)
{
if (flag['r'] && flag['f'])
{
return 0;
}
else
{
printf("rm: cannot remove '%s': No such file or directory\n", path);
return 1;
}
}
if (st.st_isdir && !flag['r'])
{
printf("rm: cannot remove '%s': Is a directory\n", path);
return 1;
}
r = remove(path);
return 0;
}

void usage() {
printf("usage: rm [-rf] <file>\n");
exit(1);
}

int main(int argc, char **argv) {
ARGBEGIN {
default:
usage();
case 'r':
case 'f':
flag[(u_char)ARGC()]++;
break;
}
ARGEND
if (argc != 1) {
usage();
}
int ret = rm(argv[0]);
return ret;
}

user/touch.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <lib.h>

int touch(char *path)
{
struct Stat st;
int r = stat(path, &st);
if (r >= 0)
{
return 0;
}
r = create(path, 0);
if (r < 0)
{
printf("touch: cannot touch '%s': No such file or directory\n", path);
return 1;
}
else
{
return 0;
}
}

void usage() {
printf("usage: touch <file>\n");
exit(1);
}

int main(int argc, char **argv) {
if (argc != 2) {
usage();
}
int ret = touch(argv[1]);
return ret;
}

user/include/shell.h

//new

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#ifndef SHELL_H
#define SHELL_H
//tokens
#define WHITESPACE " \n\t\r"
#define SYMBOLS "<|>&;()`"
#define TOKEN_WHITESPACE 0

#define TOKEN_WORD 257
#define TOKEN_APPEND 258// >>
#define TOKEN_AND 259// &&
#define TOKEN_OR 260// ||

#define PIPE_JUDGE_NORMAL 0
#define PIPE_JUDGE_PIPEEND 1
#define PIPE_JUDGE_MAINOUT 2

#define JUDGE_AND 11
#define JUDGE_OR 10

#define MAXARGS 80
#define MAXTEMPDUP 60

#define MAX_COMMAND_LENGTH 1024

#define MAX_LOCAL_KEYLEN 25
#define MAX_LOCAL_VALUELEN 25
#define MAX_LOCAL_VARCOUNT 25

#define HISTORY_FILE "/.HISTORY_FILE"
#define HISTORY_FILE_SIZE 20
struct LocalVar {
char key[MAX_LOCAL_KEYLEN];
char value[MAX_LOCAL_VALUELEN];
int writable;
};
struct tempFd {
int size;
int temp[MAXTEMPDUP][2];
};


//shellio.c

int replace_sign(char* command);
void read_command(char* cmd_buf, int max_len) ;
void history_init();
int replace_backquote(char* command);


//paser.c
int _gettoken(char *s, char **p1, char **p2);
int gettoken(char *s, char **p1);
int parsecmd(char **argv, int *rightpipe,struct tempFd * tempfd,int *pipeend,int* inpipe,int * judge);
int resetdup(struct tempFd * tempfd);
int savedup(struct tempFd * tempfd,int from);
int is_builtin(char *cmd) ;

//exec

//内建
int execute_cd(int argc, char **argv);
int execute_pwd(int argc, char **argv);
int execute_exit(int argc, char **argv);
int execute_history(int argc, char **argv);
//local
int set_local_var(char* key, char* value, int writable);
int remove_local_var(char* key);
//declare
int execute_declare(int argc, char **argv);
//unset
int execute_unset(int argc, char **argv);

int runcmd(char *s,struct tempFd *tempfd,int* rightpipe,int argc,char **argv,int pipeend);

#endif

user/lib/sh.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#include <args.h>
#include <lib.h>
#include <shell.h>
void init_shell_body(int year) {
printf("\n:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n");
printf(":: ::\n");
printf(":: MOS Shell %d ::\n", year);
printf(":: ::\n");
printf(":::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::\n");
}

char buf[MAX_COMMAND_LENGTH];

void sh_usage(void) {
printf("usage: sh [-ix] [script-file]\n");
exit(1);
}

int main(int argc, char **argv) {
int r;
int interactive = iscons(0);
int echocmds = 0;
init_shell_body(2024);
ARGBEGIN {
case 'i':
interactive = 1;
break;
case 'x':
echocmds = 1;
break;
default:
sh_usage();
}
ARGEND

if (argc > 1) {
sh_usage();
}
if (argc == 1) {
close(0);
if ((r = open(argv[0], O_RDONLY)) < 0) {
user_panic("open %s: %d", argv[0], r);
}
user_assert(r == 0);
}

history_init();
for (;;) {
if (interactive) {
printf("\n$ ");
}
read_command(buf, sizeof buf);
replace_sign(buf);
replace_backquote(buf);

if (echocmds) {
printf("# %s\n", buf);
}
char singlebuf[MAX_COMMAND_LENGTH];

int cmds = count_mulline(buf);

for(int i=0;i<=cmds;i++){
devide_mulline(buf, singlebuf, i);

struct tempFd tempfd;
tempfd.size=0;

gettoken(singlebuf, 0);

char *argv[MAXARGS];
int rightpipe = 0;
int pipeend = 0;
int inpipe = 0;
int judge = 0;
int argc = parsecmd(argv, &rightpipe,&tempfd,&pipeend,&inpipe,&judge);
int returncode=0;


//debugf("pipeend %d argc : %d\n",pipeend,argc);
if(inpipe){
runcmd(singlebuf,&tempfd,&rightpipe,argc,argv,pipeend);
}
else{

returncode=runcmd(singlebuf,&tempfd,&rightpipe,argc,argv,pipeend);

}
//debugf(">>judge:%d argc:%d returncode:%d\n",judge,argc,returncode);
while(judge!=0){
int should=0;
if(judge==JUDGE_AND){
should = (returncode==0);
}
if(judge==JUDGE_OR){
should = (returncode!=0);
}
//debugf(">>>>>%d\n",should);
rightpipe = 0;
pipeend = 0;
inpipe = 0;
judge = 0;

argc = parsecmd(argv, &rightpipe,&tempfd,&pipeend,&inpipe,&judge);
//debugf(">>%d %d %d\n",judge,argc,returncode);

if(should){
if(inpipe){
runcmd(singlebuf,&tempfd,&rightpipe,argc,argv,pipeend);
}
else{
returncode=runcmd(singlebuf,&tempfd,&rightpipe,argc,argv,pipeend);
}
}

}
}



}
close_all();
return 0;
}

user/lib/executor.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
#include <shell.h>
#include <lib.h>
#include <fd.h>

static int local_var_count;
static struct LocalVar local_var[MAX_LOCAL_VARCOUNT];

// 执行cd命令
int execute_cd(int argc, char **argv) {
// 参数个数为0时切换到根目录
char* path;
if (argc > 2) {
printf("Too many args for cd command\n");
return 1;
} else if (argc == 1) {
path = "/";
} else {
path = argv[1];
}

// 处理路径
char cur_path[MAXPATHLEN];
char abs_path[MAXPATHLEN];
syscall_get_env_dir(0, cur_path);
merge_path(abs_path, cur_path, path);

struct Stat st;
int r;
r = stat(abs_path, &st);
// 检查路径是否存在
if (r < 0) {
printf("cd: The directory '%s' does not exist\n", path);
return 1;
}
// 检查是否为目录
if (!st.st_isdir) {
printf("cd: '%s' is not a directory\n", path);
return 1;
}
// 设置工作目录
if (syscall_set_env_dir(0, abs_path) < 0) {
printf("cd: failed to set directory\n");
return 1;
}

return 0;
}
// 执行pwd命令
int execute_pwd(int argc, char **argv) {
if (argc != 1) {
printf("pwd: expected 0 arguments; got %d\n", argc - 1);
return 2;
}

char buf[MAXPATHLEN];
if (syscall_get_env_dir(0, buf) < 0) {
strcpy(buf, "/");
}
printf("%s\n", buf);
return 0;
}
// 执行exit命令
int execute_exit(int argc, char **argv) {
exit(0);//正常退出
return 0;
}
// 执行history命令
int execute_history(int argc, char **argv) {
int fd = open(HISTORY_FILE, O_RDONLY);
if (fd < 0) {
debugf("error: failed to open %s\n", HISTORY_FILE);
return 1;
}
static char buf[MAX_COMMAND_LENGTH*HISTORY_FILE_SIZE];
int r;
int n;
while ((n = read(fd, buf, (long)sizeof buf)) > 0) {
if ((r = write(1, buf, n)) != n) {
debugf("write error copying %s: %d\n", HISTORY_FILE, r);
}
}
if (n < 0) {
debugf("read error in file %s: %d\n", HISTORY_FILE, n);
}
close(fd);
return 0;
}

// local 环境变量
int set_local_var(char* key, char* value, int writable) {
// 检查键值是否已存在
for (int i = 0; i < local_var_count; ++i) {
if (strcmp(key, local_var[i].key) == 0) {
if (!local_var[i].writable) {
return -E_INVALID_SET_ENV_VAR;
}
strcpy(local_var[i].value, value);
return 0;
}
}

// 检查变量表是否已满
if (local_var_count >= MAXVARCOUNT) {
return -E_NO_FREE_ENV_VAR;
}

// 添加新变量
struct LocalVar *var = &local_var[local_var_count++];
strcpy(var->key, key);
strcpy(var->value, value);
var->writable = writable;

return 0;
}
int remove_local_var(char* key) {
// 查找变量并检查可写性
for (int i = 0; i < local_var_count; ++i) {
if (strcmp(key, local_var[i].key) == 0) {
if (!local_var[i].writable) {
return -E_INVALID_SET_ENV_VAR;
}

// 移除变量:将后续元素前移
local_var_count--;
if (i < local_var_count)
{
memmove(&local_var[i], &local_var[i+1], (local_var_count - i) * sizeof(struct LocalVar));
}
return 0;
}
}
return -E_ENV_VAR_NOT_FOUND;
}
// 全局 环境变量
int execute_declare(int argc, char **argv) {
// 解析选项
int is_env_var = 0;
int writable = 1;

ARGBEGIN {
default:
printf("usage: declare [-xr] [NAME[=VALUE]]\n");
return 1;
case 'x':
is_env_var = 1;
break;
case 'r':
writable = 0;
break;
} ARGEND;

// 处理无参数情况:打印所有变量
if (argc == 0) {
print_all_variables();
return 0;
}

// 检查参数数量
if (argc != 1) {
printf("usage: declare [-xr] [NAME[=VALUE]]\n");
return 1;
}

// 解析键值对
char *input = argv[0];
char *delim = strchr(input, '=');

char key[MAX_LOCAL_KEYLEN];
char value[MAX_LOCAL_VALUELEN] = ""; // 默认为空字符串

if (delim) {
// 提取键和值
size_t key_len = delim - input;
if (key_len >= MAX_LOCAL_KEYLEN) key_len = MAX_LOCAL_KEYLEN - 1;
strncpy(key, input, key_len);
key[key_len] = '\0';

strncpy(value, delim + 1, MAX_LOCAL_VALUELEN - 1);
value[MAX_LOCAL_VALUELEN - 1] = '\0';
} else {
// 无值的情况
strncpy(key, input, MAX_LOCAL_KEYLEN - 1);
key[MAX_LOCAL_KEYLEN - 1] = '\0';
}

// 设置变量
return set_variable(is_env_var, key, value, writable);
}

// 辅助函数:打印所有变量
void print_all_variables() {
// 打印环境变量
int env_count = syscall_get_env_var_count(0);
for (int i = 0; i < env_count; ++i) {
char key[MAX_LOCAL_KEYLEN], value[MAX_LOCAL_VALUELEN];
syscall_get_env_id_var(0, i, key, value);
printf("%s=%s\n", key, value);
}

// 打印局部变量
for (int i = 0; i < local_var_count; ++i) {
printf("%s=%s\n", local_var[i].key, local_var[i].value);
}
}

// 辅助函数:设置变量
int set_variable(int is_env_var, char* key, char* value, int writable) {
if (is_env_var) {
// 设置环境变量
remove_local_var(key); // 忽略错误,可能不存在
return syscall_set_env_var(0, key, value, writable) ? 1 : 0;
} else {
// 设置局部变量
syscall_remove_env_var(0, key); // 忽略错误,可能不存在
return set_local_var(key, value, writable) ? 1 : 0;
}
}
int execute_unset(int argc, char **argv) {
if (argc != 2) {
printf("usage: unset NAME\n");
return 1;
}
char* key = argv[1];
if (remove_local_var(key) == -E_INVALID_SET_ENV_VAR) {
return 1;
}
if (syscall_remove_env_var(0, key) == -E_INVALID_SET_ENV_VAR) {
return 1;
}
return 0;
}
int get_var(char* key, char* value) {
for (int i = 0; i < local_var_count; i++) {
if (strcmp(key, local_var[i].key) == 0) {
strcpy(value, local_var[i].value);
return 0;
}
}
return syscall_get_env_var(0, key, value);
}
// 替换环境变量
void replace_command_var(char* arg) {
char result[MAX_COMMAND_LENGTH] = {0}; // 结果缓冲区
char current_var[MAX_LOCAL_KEYLEN] = {0}; // 当前变量名
char var_value[MAX_LOCAL_VALUELEN] = {0}; // 变量值

char *src = arg; // 源字符串指针
char *dest = result; // 目标缓冲区指针
char *var_ptr = current_var; // 变量名收集指针

while (*src) {
if (*src == '$') {
// 开始变量替换
src++; // 跳过'$'

// 收集变量名
while (*src && isvar(*src)) {
*var_ptr++ = *src++;
}
*var_ptr = '\0'; // 终止变量名字符串

// 获取变量值(不存在则置空)
get_var(current_var, var_value) ? (var_value[0] = '\0') : 0;

// 复制变量值到结果
char *val_ptr = var_value;
while (*val_ptr) {
*dest++ = *val_ptr++;
}

// 重置变量名收集器
var_ptr = current_var;
*var_ptr = '\0';
} else {
// 直接复制普通字符
*dest++ = *src++;
}
}
*dest = '\0'; // 确保结果字符串终止

// 将结果复制回原始参数
strcpy(arg, result);
}

int runcmd(char *s,struct tempFd *tempfd,int* rightpipe,int argc,char **argv,int pipeend) {
int returncode =0;
if(pipeend==PIPE_JUDGE_MAINOUT ){
returncode = wait(* rightpipe);
return returncode;
}
if (argc <= 0) {
returncode= -1;
}
if(returncode==0){
argv[argc] = 0;

// 替换命令中的环境变量
for (int i = 0; i < argc; i++) {
if (strchr(argv[i], '$')) {
replace_command_var(argv[i]);
}
}
if (is_builtin(argv[0])) {
// 执行内建命令
if (strcmp(argv[0], "cd") == 0) {
returncode = execute_cd(argc, argv);

} else if (strcmp(argv[0], "pwd") == 0) {
returncode = execute_pwd(argc, argv);

} else if (strcmp(argv[0], "exit") == 0) {
returncode = execute_exit(argc, argv);

} else if (strcmp(argv[0], "declare") == 0) {
returncode = execute_declare(argc, argv);

} else if (strcmp(argv[0], "unset") == 0) {
returncode = execute_unset(argc, argv);
} else if (strcmp(argv[0], "history") == 0) {
returncode = execute_history(argc, argv);
}
}

// 处理外部命令
else if (strcmp(argv[0], "touch") == 0) {
if (argc != 2) {
printf("touch: expected 1 argument\n");
returncode=-1;
}
if(returncode==0){
char *touch_argv[] = {"touch", argv[1], NULL};
int child = spawn("touch", touch_argv);
returncode=wait(child);
}
}
else if (strcmp(argv[0], "mkdir") == 0) {
if (argc < 2) {
printf("mkdir: expected at least 1 argument\n");
returncode=-1;
}
if(returncode==0){
char *mkdir_argv[argc+1];
mkdir_argv[0] = "mkdir";
for (int i = 1; i < argc; i++) {
mkdir_argv[i] = argv[i];
}
mkdir_argv[argc] = NULL;
int child = spawn("mkdir", mkdir_argv);
returncode=wait(child);
}
}
else if (strcmp(argv[0], "rm") == 0) {
if (argc < 2) {
printf("rm: expected at least 1 argument\n");
returncode=-1;
}
if(returncode==0){
char *rm_argv[argc+1];
rm_argv[0] = "rm";
for (int i = 1; i < argc; i++) {
rm_argv[i] = argv[i];
}
rm_argv[argc] = NULL;
int child = spawn("rm", rm_argv);
returncode=wait(child);
}
}
else {
// 其他命令
int child = spawn(argv[0], argv);
if (child >= 0) {
returncode=wait(child);
} else {
returncode=-1;
debugf("spawn %s: %d\n", argv[0], child);
}
}
}

if(*rightpipe!=0 && pipeend!=PIPE_JUDGE_MAINOUT ){//-1 pipeleft
//对应最左侧父进程不退出//对应中间
resetdup(tempfd);
int this = returncode;
returncode = wait(*rightpipe);
if(this!=0){
returncode=this;
}
exit(returncode);
}
if(*rightpipe==0){
//对应没有子进程的正常指令
resetdup(tempfd);
if(pipeend==PIPE_JUDGE_PIPEEND){
////对应非正常指令,管道最右侧
exit(returncode);
}
}

return returncode;
}

user/lib/parser.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
#include <shell.h>
#include <lib.h>


int _gettoken(char *s, char **p1, char **p2) {
*p1 = 0;
*p2 = 0;
if (s == 0) {
return 0;
}

while (strchr(WHITESPACE, *s)) {
*s++ = 0;
}
if (*s == 0) {
return 0;
}


if (strchr(SYMBOLS, *s)) {
// 处理双字符符号">>"
if (*s == '>' && *(s+1) == '>') {
*p1 = s; // p1指向第一个'>'
*s++ = 0; // 终止第一个'>'
*s++ = 0; // 终止第二个'>',s指向下一个字符
*p2 = s;
return TOKEN_APPEND;
}
if (*s == '&' && *(s+1) == '&') {
*p1 = s;
*s++ = 0;
*s++ = 0;
*p2 = s;
return TOKEN_AND;
}
if (*s == '|' && *(s+1) == '|') {
*p1 = s; // p1指向第一个'>'
*s++ = 0; // 终止第一个'>'
*s++ = 0; // 终止第二个'>',s指向下一个字符
*p2 = s;
return TOKEN_OR;
}
// 其他单字符符号
int t = *s;
*p1 = s;
*s++ = 0;
*p2 = s;
return t;
}

*p1 = s;
while (*s && !strchr(WHITESPACE SYMBOLS, *s)) {
s++;
}
*p2 = s;
return TOKEN_WORD;
}

int gettoken(char *s, char **p1) {
static int c, nc;
static char *np1, *np2;

if (s) {
nc = _gettoken(s, &np1, &np2);
return 0;
}
c = nc;
*p1 = np1;
nc = _gettoken(np2, &np1, &np2);
return c;
}

int is_builtin(char *cmd) {
return strcmp(cmd, "cd") == 0 || strcmp(cmd, "pwd") == 0 ||
strcmp(cmd, "exit") == 0 || strcmp(cmd, "declare") == 0 ||
strcmp(cmd, "unset") == 0 || strcmp(cmd, "history") == 0;
}
int savedup(struct tempFd * tempfd,int from){
struct Fd* temp;
fd_alloc(&temp);
dup(from,fd2num(temp));
//debugf("save dup %d -> %d\n",from,fd2num(temp));
tempfd->temp[tempfd->size][0] = from;
tempfd->temp[tempfd->size][1] = fd2num(temp);
tempfd->size++;
}
int resetdup(struct tempFd * tempfd){
//debugf("reset dup size %d \n",tempfd->size);
if(tempfd->size>0){
for(int i=tempfd->size;i>0;i--){
//debugf("reset dup %d -> %d\n",(tempfd->temp)[i-1][1],(tempfd->temp)[i-1][0]);
dup((tempfd->temp)[i-1][1],(tempfd->temp)[i-1][0]);
close((tempfd->temp)[i-1][1]);
tempfd->size--;
}
}
}

int parsecmd(char **argv, int *rightpipe,struct tempFd * tempfd,int *pipeend,int *inpipe,int * judge) {
int argc = 0;
while (1) {
char *t;
int fd, r;
int c = gettoken(0, &t);
struct Stat stat;
* judge=0;
if (*inpipe) {
*pipeend=PIPE_JUDGE_PIPEEND;
}
else{
if(*pipeend!=PIPE_JUDGE_MAINOUT ) {
*pipeend=PIPE_JUDGE_NORMAL;
}
}


switch (c) {
case 0:
return argc;
case TOKEN_WORD:
if (argc >= MAXARGS) {
debugf("too many arguments\n");
exit(1);
}
argv[argc++] = t;
if(*pipeend==PIPE_JUDGE_MAINOUT ){
continue;
}
break;
case '<':
if (gettoken(0, &t) != TOKEN_WORD) {
debugf("syntax error: < not followed by word\n");
exit(1);
}
if(*pipeend==PIPE_JUDGE_MAINOUT ){
continue;
}
fd = open(t, O_RDONLY);
if (fd < 0) {
debugf("failed to open '%s'\n", t);
exit(1);
}
savedup( tempfd,0);
dup(fd, 0);
close(fd);
break;
case '>':
if (gettoken(0, &t) != TOKEN_WORD) {
debugf("syntax error: > not followed by word\n");
exit(1);
}
if(*pipeend==PIPE_JUDGE_MAINOUT ){
continue;
}
fd = open(t, O_WRONLY | O_CREAT | O_TRUNC);
if (fd < 0) {
debugf("failed to open '%s'\n", t);
exit(1);
}
savedup( tempfd,1);
//debugf("dup %d -> %d\n",fd,1);
dup(fd, 1);
close(fd);
break;
// 处理追加重定向
case TOKEN_APPEND:
if (gettoken(0, &t) != TOKEN_WORD) {
debugf("syntax error: >> not followed by word\n");
exit(1);
}
if(*pipeend==PIPE_JUDGE_MAINOUT ){
continue;
}
// 使用O_APPEND标志打开文件
fd = open(t, O_WRONLY | O_CREAT);

if (fd < 0) {
debugf("failed to open '%s'\n", t);
exit(1);
}
fstat(fd, &stat);
seek(fd,stat.st_size);
savedup( tempfd,1);
dup(fd, 1);
close(fd);
break;
case TOKEN_AND:
*judge = JUDGE_AND;
return argc;
case TOKEN_OR:
*judge = JUDGE_OR;
return argc;
case '|':
if(*pipeend==PIPE_JUDGE_MAINOUT ){
continue;
}
else{
*pipeend=PIPE_JUDGE_NORMAL;
}
r=0;
if(!*inpipe)
{
r = fork();
}
if (r == 0) {
int p[2];
r = pipe(p);
if (r != 0) {
debugf("pipefaild: %d\n", r);
exit(1);
}
r = fork();
if (r < 0) {
exit(1);
}
*inpipe = 1;
if (r == 0) {
savedup( tempfd,0);
dup(p[0], 0);
close(p[0]);
close(p[1]);
int no=0;
return parsecmd(argv, rightpipe,tempfd,pipeend,inpipe,&no);
} else {
*rightpipe = r;
savedup( tempfd,1);
dup(p[1], 1);
close(p[1]);
close(p[0]);
return argc;
}
break;
}
else {
*rightpipe = r;
*pipeend=PIPE_JUDGE_MAINOUT ;
}

}
}

return argc;
}

user/lib/shellio.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
#include <shell.h>
#include <lib.h>

static char cmd_history[HISTORY_FILE_SIZE + 1][MAX_COMMAND_LENGTH];
static int total_history;
static int active_cmd_idx;
static int prev_cursor;
static int cursor_pos;
static int cmd_length;
void history_init() {
int result;
int file_desc = open(HISTORY_FILE, O_RDONLY);
total_history = 0;
if (file_desc < 0) {
// debugf("error: failed to open %s\n", HISTORY_FILE);
return;
}
while (total_history < HISTORY_FILE_SIZE) {
int char_pos = 0;
char character;
while (1) {
if ((result = read(file_desc, &character, 1)) != 1) {
if (result < 0) {
debugf("read error in file %s: %d\n", HISTORY_FILE, result);
}
break;
}
if (character == '\r' || character == '\n') {
break;
}
cmd_history[total_history][char_pos] = character;
char_pos += 1;
}
if (result != 1) {
break;
}
cmd_history[total_history][char_pos] = '\0';
total_history += 1;
}
close(file_desc);
return;
}

// 移动光标通用函数
static void move_cursor(char direction, int count) {
if (count <= 0) return;
switch(direction) {
case 'L': printf("\e[%dD", count); break; // Left
case 'R': printf("\e[%dC", count); break; // Right
case 'U': printf("\e[%dA", count); break; // Up
case 'D': printf("\e[%dB", count); break; // Down
}
}

static void clear_current_line() {
printf("\e[K");
}

static void store_command(char* cmd_text) {
int result;
static char buffer[HISTORY_FILE_SIZE][MAX_COMMAND_LENGTH];
int file_desc = open(HISTORY_FILE, O_RDONLY);
int buffer_count = 0;
if (file_desc >= 0) {
while (buffer_count < HISTORY_FILE_SIZE) {
int char_idx = 0;
char ch;
while (1) {
if ((result = read(file_desc, &ch, 1)) != 1) {
if (result < 0) {
debugf("read error in file %s: %d\n", HISTORY_FILE, result);
}
break;
}
if (ch == '\r' || ch == '\n') {
break;
}
buffer[buffer_count][char_idx] = ch;
char_idx += 1;
}
if (result != 1) {
break;
}
buffer[buffer_count][char_idx] = '\0';
buffer_count += 1;
}
close(file_desc);
}
file_desc = open(HISTORY_FILE, O_WRONLY | O_CREAT | O_TRUNC);
if (file_desc < 0) {
debugf("error: failed to open %s to save command.\n", HISTORY_FILE);
return;
}
for (int i = 0; i < buffer_count; ++i) {
if (i == 0 && buffer_count == HISTORY_FILE_SIZE) {
continue;
}
if ((result = write(file_desc, buffer[i], strlen(buffer[i]))) < 0) {
debugf("write error in saving command\n");
return;
}
if ((result = write(file_desc, "\n", 1)) < 0) {
debugf("write error in saving command\n");
return;
}
}
if ((result = write(file_desc, cmd_text, strlen(cmd_text))) < 0) {
debugf("write error in saving command\n");
return;
}
if ((result = write(file_desc, "\n", 1)) < 0) {
debugf("write error in saving command\n");
return;
}
close(file_desc);
}


static void refresh_display(char* cmd_text) {
move_cursor('L', prev_cursor);
clear_current_line();
printf("%s", cmd_text);
move_cursor('L', cmd_length - cursor_pos);
}

static void remove_char(char* cmd_text) {
if (cursor_pos > 0) {
cursor_pos -= 1;
for (int i = cursor_pos; i < cmd_length; ++i) {
cmd_text[i] = cmd_text[i + 1];
}
cmd_length -= 1;
cmd_text[cmd_length] = '\0';
}
refresh_display(cmd_text);
}

static void handle_arrow_keys() {
char key_code;
int result;
if ((result = read(0, &key_code, 1)) != 1) {
if (result < 0) {
debugf("read error: %d\n", result);
}
exit(1);
}
if (key_code != '[') {
debugf("read error: invalid control key\n");
exit(1);
}
if ((result = read(0, &key_code, 1)) != 1) {
if (result < 0) {
debugf("read error: %d\n", result);
}
exit(1);
}
switch (key_code) {
case 'D': // move left
if (cursor_pos > 0) {
prev_cursor -= 1;
cursor_pos -= 1;
} else {
move_cursor('R', 1);
}
break;
case 'C': // move right
if (cursor_pos < cmd_length) {
prev_cursor += 1;
cursor_pos += 1;
} else {
move_cursor('L', 1);
}
break;
case 'A': // move up
move_cursor('D', 1);
if (active_cmd_idx > 0) {
active_cmd_idx -= 1;
cursor_pos = cmd_length = strlen(cmd_history[active_cmd_idx]);
refresh_display(cmd_history[active_cmd_idx]);
}
break;
case 'B': // move down
if (active_cmd_idx < total_history) {
active_cmd_idx += 1;
cursor_pos = cmd_length = strlen(cmd_history[active_cmd_idx]);
refresh_display(cmd_history[active_cmd_idx]);
}
break;
default:
break;
}
}

// 光标移动和删除通用函数
static void edit_command_line(char* cmd_text, int edit_type) {
switch(edit_type) {
case 1: // cursor_to_beginning
cursor_pos = 0;
break;
case 2: // cursor_to_end
cursor_pos = cmd_length;
break;
case 3: // delete_to_end
move_cursor('U', 1);
cmd_length = cursor_pos;
cmd_text[cursor_pos] = '\0';
break;
case 4: // delete_to_begin
for (int i = cursor_pos; i < cmd_length; ++i) {
cmd_text[i - cursor_pos] = cmd_text[i];
}
cmd_length -= cursor_pos;
cursor_pos = 0;
cmd_text[cmd_length] = '\0';
break;
case 5: // delete_pre_word
{
int del_pos = cursor_pos;
while (del_pos >= 0 && strchr(WHITESPACE, cmd_text[del_pos])) {
--del_pos;
}
while (del_pos >= 0 && !strchr(WHITESPACE, cmd_text[del_pos])) {
--del_pos;
}
++del_pos;
for (int i = cursor_pos; i < cmd_length; ++i) {
cmd_text[i - cursor_pos + del_pos] = cmd_text[i];
}
cmd_length -= cursor_pos - del_pos;
cursor_pos = del_pos;
cmd_text[cmd_length] = '\0';
}
break;
}
refresh_display(cmd_text);
}

static void add_character(char* cmd_text, char ch) {
move_cursor('L', 1);
for (int i = cmd_length - 1; i >= cursor_pos; --i) {
cmd_text[i + 1] = cmd_text[i];
}
cmd_text[cursor_pos] = ch;
cursor_pos += 1;
cmd_length += 1;
cmd_text[cmd_length] = '\0';
refresh_display(cmd_text);
}

void read_command(char* command, int n) {
int result;
char input_char;

active_cmd_idx = total_history;
cmd_length = 0;
prev_cursor = 0;
cursor_pos = 0;
cmd_history[active_cmd_idx][0] = '\0';

while (cmd_length < n - 1) { // 保证有空间存放'\0'
result = read(0, &input_char, 1);
if (result != 1) {
if (result < 0) debugf("read error: %d\n", result);
exit(1);
}
prev_cursor = cursor_pos;
switch (input_char) {
case '\b':
case '\x7f': // backspace
remove_char(cmd_history[active_cmd_idx]);
break;
case '\x1b': // arrow key
handle_arrow_keys();
break;
case '\x01': // Ctrl-A
edit_command_line(cmd_history[active_cmd_idx], 1);
break;
case '\x05': // Ctrl-E
edit_command_line(cmd_history[active_cmd_idx], 2);
break;
case '\x0b': // Ctrl-K
edit_command_line(cmd_history[active_cmd_idx], 3);
break;
case '\x15': // Ctrl-U
edit_command_line(cmd_history[active_cmd_idx], 4);
break;
case '\x17': // Ctrl-W
edit_command_line(cmd_history[active_cmd_idx], 5);
break;
case '\r':
case '\n':
cmd_history[active_cmd_idx][cmd_length] = '\0';
strncpy(command, cmd_history[active_cmd_idx], n - 1);
command[n - 1] = '\0';
store_command(command);
if (total_history == HISTORY_FILE_SIZE) {
memmove(cmd_history, cmd_history + 1, sizeof(cmd_history[0]) * (HISTORY_FILE_SIZE - 1));
strncpy(cmd_history[HISTORY_FILE_SIZE - 1], command, MAX_COMMAND_LENGTH - 1);
cmd_history[HISTORY_FILE_SIZE - 1][MAX_COMMAND_LENGTH - 1] = '\0';
} else {
strncpy(cmd_history[total_history], command, MAX_COMMAND_LENGTH - 1);
cmd_history[total_history][MAX_COMMAND_LENGTH - 1] = '\0';
total_history++;
}
return;
default:
if ((unsigned char)input_char >= 32 && (unsigned char)input_char < 127) { // 可打印字符
add_character(cmd_history[active_cmd_idx], input_char);
}
break;
}
}
debugf("line too long\n");
while ((result = read(0, &input_char, 1)) == 1 && input_char != '\r' && input_char != '\n');
command[0] = '\0';
}





static int execute_subprocess(char* cmd_buffer, char* output_buffer, int* position, int buffer_size) {
int result;
int pos = *position;
int pipe_fds[2];
char* cmd_ptr = cmd_buffer;

if ((result = pipe(pipe_fds)) != 0) {
debugf("error allocating pipe:%d\n", result);
return 1;
}
if ((result = fork()) < 0) {
debugf("error to fork: %d\n", result);
return 1;
}
if (result == 0) {
struct tempFd tempfd;
savedup(&tempfd,1);
dup(pipe_fds[1], 1);
close(pipe_fds[1]);
close(pipe_fds[0]);

replace_sign(cmd_ptr);
debugf("sub:%s\n",cmd_ptr);

char single_buffer[MAX_COMMAND_LENGTH];
int command_count = count_mulline(cmd_ptr);

for(int i=0;i<=command_count;i++){
devide_mulline(cmd_ptr, single_buffer, i);
struct tempFd temp_fd;
temp_fd.size=0;

gettoken(single_buffer, 0);

char *arg_vector[MAXARGS];
int right_pipe = 0;
int pipe_end = 0;
int in_pipe = 0;
int judge_flag = 0;
int arg_count = parsecmd(arg_vector, &right_pipe,&temp_fd,&pipe_end,&in_pipe,&judge_flag);
int return_code=0;

if(in_pipe){
runcmd(single_buffer,&temp_fd,&right_pipe,arg_count,arg_vector,pipe_end);
}
else{
return_code=runcmd(single_buffer,&temp_fd,&right_pipe,arg_count,arg_vector,pipe_end);
}

while(judge_flag!=0){
int should_execute=0;
if(judge_flag==JUDGE_AND){
should_execute = (return_code==0);
}
if(judge_flag==JUDGE_OR){
should_execute = (return_code!=0);
}
right_pipe = 0;
pipe_end = 0;
in_pipe = 0;
judge_flag = 0;

arg_count = parsecmd(arg_vector, &right_pipe,&temp_fd,&pipe_end,&in_pipe,&judge_flag);

if(should_execute){
if(in_pipe){
runcmd(single_buffer,&temp_fd,&right_pipe,arg_count,arg_vector,pipe_end);
}
else{
return_code=runcmd(single_buffer,&temp_fd,&right_pipe,arg_count,arg_vector,pipe_end);
}
}
}
}
resetdup(&tempfd);
exit(result);
close_all();
} else {
close(pipe_fds[1]);
int child_pid = result;
result = readn(pipe_fds[0], output_buffer + pos, buffer_size - pos);
close(pipe_fds[0]);
wait(child_pid);
if (result < 0) {
debugf("error to read from child shell: %d\n", result);
return 1;
}
pos += result;
if (pos >= buffer_size) {
debugf("line too long\n");
return 1;
}
}

(*position) = pos;
return 0;
}


int replace_backquote(char* command) {
char temp_buffer[1024];
char sub_command[1024];
int temp_len = 0;
int sub_len = 0;
int in_backquote = 0;
int i = 0, result;

for (; command[i] != '\0'; ++i) {
if (command[i] == '`') {
if (in_backquote) {
sub_command[sub_len] = '\0';
int pos = 0;
char sub_output[1024] = {0};
result = execute_subprocess(sub_command, sub_output, &pos, sizeof(sub_output));
if (result != 0) return result;
// 去除末尾换行
while (pos > 0 && (sub_output[pos - 1] == '\n' || sub_output[pos - 1] == '\r')) {
sub_output[--pos] = '\0';
}
for (int j = 0; sub_output[j] && temp_len < (int)sizeof(temp_buffer) - 1; ++j) {
temp_buffer[temp_len++] = sub_output[j];
}
sub_len = 0;
}
in_backquote = !in_backquote;
} else if (in_backquote) {
if (sub_len < (int)sizeof(sub_command) - 1)
sub_command[sub_len++] = command[i];
} else {
if (temp_len < (int)sizeof(temp_buffer) - 1)
temp_buffer[temp_len++] = command[i];
}
}
temp_buffer[temp_len] = '\0';
strcpy(command, temp_buffer);
return in_backquote ? 2 : 0;
}
int replace_sign(char* command){
char *pointer=command;
int comment_found=0;
while(*pointer!='\0'){
if(*pointer=='#'){
comment_found = 1;
}
if(comment_found == 1){
*pointer='\0';
}
pointer++;
}
return comment_found;
}