diff --git a/Makefile b/Makefile
index ded5bc2..eadf415 100644
--- a/Makefile
+++ b/Makefile
@@ -188,6 +188,11 @@ UPROGS=\
$U/_grind\
$U/_wc\
$U/_zombie\
+ $U/_sleep\
+ $U/_pingpong\
+ $U/_primes\
+ $U/_find\
+ $U/_xargs\
diff --git a/time.txt b/time.txt
new file mode 100644
index 0000000..62f9457
--- /dev/null
+++ b/time.txt
@@ -0,0 +1 @@
+6
\ No newline at end of file
diff --git a/user/find.c b/user/find.c
new file mode 100644
index 0000000..75e5dcd
--- /dev/null
+++ b/user/find.c
@@ -0,0 +1,74 @@
+#include "kernel/types.h"
+#include "user/user.h"
+#include "kernel/stat.h"
+#include "kernel/fs.h"
+
+int fmtname(const char *path) {
+ int p;
+ for(p=strlen(path); p >= 0 && *(path + p) != '/'; p--);
+ p++;
+ return p;
+}
+
+void find(const char *path, const char* target) {
+ char buf[512], *p;
+ int fd;
+ struct dirent de;
+ struct stat st;
+ if((fd = open(path, 0)) < 0){
+ fprintf(2, "find: cannot open %s\n", path);
+ return;
+ }
+
+ if(fstat(fd, &st) < 0){
+ fprintf(2, "find: cannot stat %s\n", path);
+ close(fd);
+ return;
+ }
+
+ switch(st.type){
+ case T_DEVICE:
+ case T_FILE:
+ if (strcmp(path + fmtname(path), target) == 0) {
+ printf("%s\n", path);
+ }
+ break;
+
+ case T_DIR:
+ if (strcmp(path + fmtname(path), target) == 0) {
+ printf("%s\n", path);
+ }
+ if(strlen(path) + 1 + DIRSIZ + 1 > sizeof buf){
+ printf("find: path too long\n");
+ break;
+ }
+ strcpy(buf, path);
+ p = buf+strlen(buf);
+ *p++ = '/';
+ while(read(fd, &de, sizeof(de)) == sizeof(de)){
+ if(de.inum == 0)
+ continue;
+ if (strcmp(de.name, ".") == 0 || strcmp(de.name, "..") == 0)
+ continue;
+ memmove(p, de.name, DIRSIZ);
+ p[DIRSIZ] = 0;
+ if(stat(buf, &st) < 0){
+ printf("find: cannot stat %s\n", buf);
+ continue;
+ }
+ find(buf, target);
+ }
+ break;
+ }
+ close(fd);
+}
+
+int main(int argc, char** argv) {
+ if (argc != 3) {
+ printf("\x1b[31mInvalid arguments\x1b[0m\n");
+ printf("\x1b[32musage\x1b[0m: find \n");
+ exit(0);
+ }
+ find(argv[1], argv[2]);
+ exit(0);
+}
\ No newline at end of file
diff --git a/user/pingpong.c b/user/pingpong.c
new file mode 100644
index 0000000..868f67c
--- /dev/null
+++ b/user/pingpong.c
@@ -0,0 +1,27 @@
+#include "kernel/types.h"
+#include "user/user.h"
+
+int main(int argc, char** argv) {
+ int p[2]; // hell, read from p[0] and write to p[1] no matter in pa/ch
+ if (pipe(p) == -1) exit(1);
+ uint8 senddata[] = {0xAC};
+ uint8 recvdata[] = {0};
+ int pid = fork();
+ if (pid == 0) {
+ // child
+ if (read(p[0], recvdata, 1) != 1) printf("error child read\n");
+ close(p[0]);
+ printf("%d: received ping\n", getpid());
+ if (write(p[1], senddata, 1) != 1) printf("error child write\n");
+ close(p[1]);
+ exit(0);
+ }
+ else {
+ if (write(p[1], senddata, 1) != 1) printf("error parent write\n");
+ close(p[1]);
+ if (read(p[0], recvdata, 1) != 1) printf("error parent read\n");
+ printf("%d: received pong\n", getpid());
+ close(p[0]);
+ exit(0);
+ }
+}
\ No newline at end of file
diff --git a/user/primes.c b/user/primes.c
new file mode 100644
index 0000000..07d2034
--- /dev/null
+++ b/user/primes.c
@@ -0,0 +1,51 @@
+#include "kernel/types.h"
+#include "user/user.h"
+
+void pipeline(int leftfd) {
+ int pipefd[2];
+ int p; read(leftfd, &p, sizeof(p));
+ printf("prime %d\n", p);
+ int n;
+ int has_right = 0;
+ while (read(leftfd, &n, sizeof(n)) > 0) {
+ if (n % p != 0) {
+ if (!has_right) {
+ has_right = 1;
+ pipe(pipefd);
+ if (fork() == 0) {
+ close(pipefd[1]);
+ pipeline(pipefd[0]);
+ } else {
+ close(pipefd[0]);
+ }
+ }
+ write(pipefd[1], &n, sizeof(n));
+ }
+ }
+ close(leftfd);
+ if (has_right) {
+ close(pipefd[1]);
+ while(wait(0) != -1);
+ }
+ exit(0);
+}
+
+int main(int argc, char** argv) {
+ int pipefd[2];
+ pipe(pipefd);
+ int pid = fork();
+ if (pid != 0) {
+ close(pipefd[0]); // no need to read in the feeding proc
+ for (int i = 2; i <= 35; ++ i) {
+ write(pipefd[1], &i, sizeof(i));
+ }
+ close(pipefd[1]);
+ while(wait(0) != -1);
+ exit(0);
+ }
+ else {
+ close(pipefd[1]); // pipeline proc will have its own write pipe
+ pipeline(pipefd[0]);
+ }
+ exit(0);
+}
\ No newline at end of file
diff --git a/user/sleep.c b/user/sleep.c
new file mode 100644
index 0000000..39d5d0b
--- /dev/null
+++ b/user/sleep.c
@@ -0,0 +1,17 @@
+#include "kernel/types.h"
+#include "user/user.h"
+
+int main(int argc, char** argv) {
+ if (argc != 2) {
+ printf("\x1b[31mUnexpected argument\x1b[0m\n");
+ printf("usage: sleep \n");
+ exit(0);
+ }
+ int ticks = atoi(argv[1]);
+ int ret = sleep(ticks);
+ if (ret) {
+ printf("\x1b[31mFailed to sleep\x1b[0m\n");
+ exit(-1);
+ }
+ exit(0);
+}
\ No newline at end of file
diff --git a/user/xargs.c b/user/xargs.c
new file mode 100644
index 0000000..46553f3
--- /dev/null
+++ b/user/xargs.c
@@ -0,0 +1,59 @@
+#include "kernel/param.h"
+#include "kernel/types.h"
+#include "user/user.h"
+
+static inline int is_ws(const char ch) {
+ return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r';
+}
+
+int main(int argc, char** argv) {
+ int STDIN = 0;
+ char linebuf[1024];
+ char* p = linebuf;
+ char* _argv[MAXARG];
+ while (read(STDIN, p, 1) > 0) {
+ if (*p == '\n') {
+ int i;
+ int in_ws = 1;
+ for (i = 0; i < argc - 1; ++i) {
+ _argv[i] = argv[i + 1];
+ }
+ for (char* p0 = linebuf; p0 <= p; ++p0) {
+ if (is_ws(*p0)) {
+ if (!in_ws) {
+ in_ws = 1;
+ i++;
+ }
+ *p0 = '\0';
+ } else if (in_ws) {
+ in_ws = 0;
+ _argv[i] = p0;
+ }
+ if (i >= MAXARG) {
+ printf("warning: too many arguments");
+ break;
+ }
+ }
+ *p = '\0';
+ _argv[i] = 0;
+ // for (int j = 0; j < i; ++ j) {
+ // printf("%s\n", _argv[j]);
+ // }
+ if (fork() == 0) {
+ exec(_argv[0], _argv);
+ exit(0);
+ } else {
+ p = linebuf;
+ while (wait(0) != -1)
+ ;
+ }
+ } else {
+ p++;
+ if (p >= linebuf + sizeof(linebuf)) {
+ printf("line buffer exceeded\n");
+ exit(0);
+ }
+ }
+ }
+ exit(0);
+}
\ No newline at end of file