aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicholas <nbnoll@eml.cc>2021-11-17 08:22:03 -0800
committerNicholas <nbnoll@eml.cc>2021-11-17 08:22:03 -0800
commitab400c0be5577cab2a66d242183887ebbd61717f (patch)
treef0391a4eab4d84c43692d4113076ea466568dcbd
parent09c0a372823446d4559fc971d9a6128c89420b75 (diff)
checkin
-rw-r--r--Makefile5
-rw-r--r--include/base.h1
-rw-r--r--include/libfmt.h1
-rw-r--r--rules.mk4
-rw-r--r--share/paths.mk3
-rw-r--r--src/base/error/exits.c2
-rw-r--r--src/base/os/dirname.c12
-rw-r--r--src/base/os/rules.mk1
-rw-r--r--src/cmd/core/basename.c36
-rw-r--r--src/cmd/core/cat.c52
-rw-r--r--src/cmd/core/date.c37
-rw-r--r--src/cmd/core/rules.mk19
-rw-r--r--src/cmd/rc/exec.c74
-rw-r--r--src/cmd/rc/exec.h2
-rw-r--r--src/cmd/rc/lex.c17
-rw-r--r--src/cmd/rc/main.c42
-rw-r--r--src/cmd/rc/prompt.c7
-rw-r--r--src/cmd/rc/rc.h8
-rw-r--r--src/cmd/rc/rules.mk2
-rw-r--r--src/cmd/rc/var.c30
-rw-r--r--src/cmd/rules.mk3
-rw-r--r--src/libfmt/fprint.c2
-rw-r--r--src/libfmt/panic.c15
-rw-r--r--src/libfmt/rules.mk1
24 files changed, 290 insertions, 86 deletions
diff --git a/Makefile b/Makefile
index 1ff3694..f306afe 100644
--- a/Makefile
+++ b/Makefile
@@ -1,3 +1,6 @@
+MAKEFLAGS += --no-builtin-rules
+MAKEFLAGS += --no-builtin-variables
+
# Compiler, Linker, and Assembler
CC := gcc
AR := ar
@@ -32,7 +35,7 @@ ELIBS := -L$(LIB_DIR) -lc
# Named generic rules (must be evaluated lazily)
COMPILE = @echo "CC "$(@:$(OBJ_DIR)/%=%);\
- $(CC) -MD $(CFLAGS) $(TCFLAGS) $(INCS) $(TCINCS) -o $@ -c $<
+ $(CC) -MD $(CFLAGS) $(TCFLAGS) $(INCS) $(TCINCS) -o $@ -c $<
LINK = @echo "LD "$(@:$(OBJ_DIR)/%=%);\
$(CC) -MD $(CFLAGS) $(STATIC) $(TCFLAGS) -o $@ $(CINIT) $< $(CFINI) $(TCLIBS) $(ELIBS)
COMPLINK = @echo "CC "$(@:$(OBJ_DIR)/%=%);\
diff --git a/include/base.h b/include/base.h
index 822cc09..3e544e5 100644
--- a/include/base.h
+++ b/include/base.h
@@ -159,6 +159,7 @@ long io·tell(io·Stream *s);
/* basic os helpers */
int os·exists(byte *path, int flag);
+byte *os·dirname(byte *path);
byte *os·basename(byte *path);
int os·sep(void);
diff --git a/include/libfmt.h b/include/libfmt.h
index 7ba40a0..4c187fd 100644
--- a/include/libfmt.h
+++ b/include/libfmt.h
@@ -51,6 +51,7 @@ enum
/* normal printing interface */
int fmt·print(char *fmt, ...);
int fmt·fprint(int fd, char *fmt, ...);
+void fmt·panic(char *fmt, ...);
int fmt·sprint(char *buf, char *fmt, ...);
int fmt·nsprint(int len, char *buf, char *fmt, ...);
diff --git a/rules.mk b/rules.mk
index f6d1603..d30460a 100644
--- a/rules.mk
+++ b/rules.mk
@@ -64,8 +64,8 @@ $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.s
$(ASSEMBLE)
-$(OBJ_DIR)/%: $(SRC_DIR)%.c
- $(COMPLNK)
+$(OBJ_DIR)/%: $(SRC_DIR)/%.c
+ $(COMPLINK)
targets: $(SYS) $(LIBS) $(BINS) $(TEST)
diff --git a/share/paths.mk b/share/paths.mk
index 371ab7a..112c254 100644
--- a/share/paths.mk
+++ b/share/paths.mk
@@ -23,3 +23,6 @@ OBJS += $(UNIT_$(d))
TEST_$(d) := $(patsubst $(SRC_DIR)/%, $(TEST_DIR)/%, $(TEST_$(d):.c=))
TEST += $(TEST_$(d))
+
+# useful path
+o := $(patsubst $(SRC_DIR)/%, $(OBJ_DIR)/%, $(d))
diff --git a/src/base/error/exits.c b/src/base/error/exits.c
index 6be7d3b..7fd83c5 100644
--- a/src/base/error/exits.c
+++ b/src/base/error/exits.c
@@ -6,6 +6,6 @@ exits(char *s)
if(s == nil || *s == 0)
exit(0);
- fputs(s, stderr);
+ //fputs(s, stderr);
exit(1);
}
diff --git a/src/base/os/dirname.c b/src/base/os/dirname.c
new file mode 100644
index 0000000..c7ed2ee
--- /dev/null
+++ b/src/base/os/dirname.c
@@ -0,0 +1,12 @@
+#include "internal.h"
+
+char*
+os·dirname(char *path)
+{
+ char *sep;
+
+ if((sep = strrchr(path, '/')))
+ *sep = 0;
+
+ return path;
+}
diff --git a/src/base/os/rules.mk b/src/base/os/rules.mk
index bf1e71d..c6eb43e 100644
--- a/src/base/os/rules.mk
+++ b/src/base/os/rules.mk
@@ -1,4 +1,5 @@
SRCS_$(d)+=\
$(d)/os/basename.c\
+ $(d)/os/dirname.c\
$(d)/os/exists.c\
$(d)/os/sep.c\
diff --git a/src/cmd/core/basename.c b/src/cmd/core/basename.c
new file mode 100644
index 0000000..ba9d4c9
--- /dev/null
+++ b/src/cmd/core/basename.c
@@ -0,0 +1,36 @@
+#include <u.h>
+#include <base.h>
+#include <libfmt.h>
+
+static void
+usage(void)
+{
+ fmt·fprint(2,"usage: %s [-d] path [suffix]\n", argv0);
+ exits("usage");
+}
+
+int
+main(int argc, char *argv[])
+{
+ int d;
+ long n;
+ char *p, *s;
+
+ ARGBEGIN{
+ case 'd': d = 1; break;
+ default:
+ usage();
+ }ARGEND;
+
+ if(argc < 1 || argc > 2)
+ usage();
+
+ p = d ? os·dirname(argv[0]) : os·basename(argv[0]);
+ if(argc>1){
+ n = strlen(p)-strlen(argv[1]);
+ if(n >= 0 && strcmp(p+n, argv[1])==0)
+ p[n] = 0;
+ }
+ puts(p);
+ exits(nil);
+}
diff --git a/src/cmd/core/cat.c b/src/cmd/core/cat.c
new file mode 100644
index 0000000..4f0929c
--- /dev/null
+++ b/src/cmd/core/cat.c
@@ -0,0 +1,52 @@
+#include <u.h>
+#include <base.h>
+#include <libfmt.h>
+
+static void
+usage(void)
+{
+ fmt·panic("usage: %s [-u] [file ...]\n", argv0);
+ exits("usage");
+}
+
+static void
+cat(int fd, char *s)
+{
+ long n;
+ char buf[8192];
+
+ while((n=read(fd, buf, sizeof(buf)))>0){
+ if(write(1, buf, n) != n)
+ fmt·panic("write error copying %s: %r", s);
+ }
+
+ if(n<0)
+ fmt·panic("error reading %s: %r", s);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int fd;
+
+ ARGBEGIN{
+ case 'u': /* ignore */ break;
+ default:
+ usage();
+ }ARGEND;
+
+ if(!argc){
+ cat(0, "<stdin>");
+ exits(0);
+ }
+
+ while(argc-- > 0){
+ if((fd = open(*argv, O_RDONLY))<0)
+ fmt·panic("can't open %s: %r", *argv);
+
+ cat(fd, *argv);
+ close(fd);
+
+ argv++;
+ }
+}
diff --git a/src/cmd/core/date.c b/src/cmd/core/date.c
new file mode 100644
index 0000000..225333f
--- /dev/null
+++ b/src/cmd/core/date.c
@@ -0,0 +1,37 @@
+#include <u.h>
+#include <base.h>
+#include <libfmt.h>
+
+static void
+usage(void)
+{
+ fmt·fprint(2, "usage: date [-un] [seconds]\n");
+ exits("usage");
+}
+
+int
+main(int argc, char *argv[])
+{
+ int n, u;
+ ulong now;
+
+ ARGBEGIN{
+ case 'n': n = 1; break;
+ case 'u': u = 1; break;
+ default: usage();
+ }ARGEND;
+
+ if(argc == 1)
+ now = strtoul(*argv, 0, 0);
+ else
+ now = time(0);
+
+ if(n)
+ fmt·print("%ld\n", now);
+ else if(u)
+ fmt·print("%s", asctime(gmtime(now)));
+ else
+ fmt·print("%s", ctime(now));
+
+ exits(nil);
+}
diff --git a/src/cmd/core/rules.mk b/src/cmd/core/rules.mk
new file mode 100644
index 0000000..9c4eb76
--- /dev/null
+++ b/src/cmd/core/rules.mk
@@ -0,0 +1,19 @@
+include share/push.mk
+
+BINS_$(d):=\
+ $(d)/basename\
+ $(d)/cat
+
+include share/paths.mk
+libbase:=\
+ $(OBJ_DIR)/libfmt/libfmt.a\
+ $(OBJ_DIR)/libutf/libutf.a\
+ $(OBJ_DIR)/base/base.a
+
+$(o)/basename: $(d)/basename.c $(libbase)
+ $(COMPLINK)
+
+$(o)/cat: $(d)/cat.c $(libbase)
+ $(COMPLINK)
+
+include share/pop.mk
diff --git a/src/cmd/rc/exec.c b/src/cmd/rc/exec.c
index 51e352a..bd68bf5 100644
--- a/src/cmd/rc/exec.c
+++ b/src/cmd/rc/exec.c
@@ -36,8 +36,7 @@ struct Builtin builtin[]={
/* words and lists */
-static
-void
+static void
pushword(char *str)
{
if(!runner->args)
@@ -46,8 +45,7 @@ pushword(char *str)
runner->args->word = makeword(str, runner->args->word);
}
-static
-void
+static void
popword(void)
{
Word *w;
@@ -63,8 +61,7 @@ popword(void)
efree(w);
}
-static
-Word*
+static Word*
copywords(Word *a, Word *tail)
{
Word *v = nil, **end;
@@ -76,8 +73,7 @@ copywords(Word *a, Word *tail)
return v;
}
-static
-void
+static void
freewords(Word *w)
{
Word *n;
@@ -89,8 +85,7 @@ freewords(Word *w)
}
}
-static
-void
+static void
freelist(Word *w)
{
Word *n;
@@ -100,7 +95,6 @@ freelist(Word *w)
efree(w);
w = n;
}
-
}
static void
@@ -154,7 +148,7 @@ path(char *w)
if(strncmp(w, "/", 1)==0
|| strncmp(w, "./", 2)==0
|| strncmp(w, "../", 3)==0
- || (path = var("path")->val)==0)
+ || !(path = var("path")->val))
path=&nullpath;
return path;
@@ -262,7 +256,7 @@ xforkx(void)
initchild(runner, 1);
pushword("exec");
xx();
- exit(2); // reach here on failed execs
+ exit(errno); // reach here on failed execs
break;
default: // parent
initparent(runner, pid, 0);
@@ -505,7 +499,7 @@ xcd(void)
static Code dotcmd[14] =
{
- [0] = {.i = 0},
+ [0] = {.i = 1}, // offset by 1 to ensure it never gets freed
[1] = {.f = Xmark},
[2] = {.f = Xword},
[3] = {.s = "0"},
@@ -557,7 +551,7 @@ xdot(void)
}
if(fd<0){
- print(shell.err, "failed open: %s: ", base);
+ print(shell.err, "%s: ", base);
setstatus("failed open");
Xerror(".: failed open");
return;
@@ -626,41 +620,43 @@ xfg(void)
foreground(job, 1);
}
+static Code bootstrap[32] =
+{
+ [0] = {.i = 1},
+ [1] = {.f = Xmark},
+ [2] = {.f = Xword},
+ [3] = {.s = "*"},
+ [4] = {.f = Xassign},
+ [5] = {.f = Xmark},
+ [6] = {.f = Xmark},
+ [7] = {.f = Xword},
+ [8] = {.s = "*"},
+ [9] = {.f = Xdollar},
+ [10] = {.f = Xword},
+ [11] = {.s = nil}, /* XXX: to be filled in by caller */
+ [12] = {.f = Xword},
+ [13] = {.s = "."},
+ [14] = {.f = Xbasic},
+ [15] = {.f = Xexit},
+ [16] = {.i = 0}
+};
+
void
-xboot(int argc, char *argv[])
+xboot(char *exe, int argc, char *argv[])
{
int i;
- Code bootstrap[32];
char num[12];
- i = 0;
- bootstrap[i++].i = 1;
- bootstrap[i++].f = Xmark;
- bootstrap[i++].f = Xword;
- bootstrap[i++].s="*";
- bootstrap[i++].f = Xassign;
- bootstrap[i++].f = Xmark;
- bootstrap[i++].f = Xmark;
- bootstrap[i++].f = Xword;
- bootstrap[i++].s="*";
- bootstrap[i++].f = Xdollar;
- bootstrap[i++].f = Xword;
- bootstrap[i++].s = "./cmds"; /* /dev/stdin"; */
- bootstrap[i++].f = Xword;
- bootstrap[i++].s=".";
- bootstrap[i++].f = Xbasic;
- bootstrap[i++].f = Xexit;
- bootstrap[i].i = 0;
-
+ bootstrap[11].s = exe;
run(bootstrap, 1, nil, 0);
runner->pid = runner->pgid = shell.pid;
pushlist(); // prime bootstrap argv
- argv0 = strdup(argv[0]);
- for(i = argc-1; i > 0; --i)
+ /* initialize $* */
+ for(i = argc-1; i >= 0; --i)
pushword(argv[i]);
- /* main interpreter loop */
+ /* main loop */
for(;;){
runner->code.i++;
(*runner->code.exe[runner->code.i-1].f)();
diff --git a/src/cmd/rc/exec.h b/src/cmd/rc/exec.h
index 992af35..28cbd80 100644
--- a/src/cmd/rc/exec.h
+++ b/src/cmd/rc/exec.h
@@ -60,4 +60,4 @@ void xfg(void);
void xjob(void);
void xfunc(Var *v);
-void xboot(int argc, char *argv[]);
+void xboot(char *exe, int argc, char *argv[]);
diff --git a/src/cmd/rc/lex.c b/src/cmd/rc/lex.c
index 9283404..f2c0a60 100644
--- a/src/cmd/rc/lex.c
+++ b/src/cmd/rc/lex.c
@@ -147,8 +147,11 @@ readc(void)
if(runner->flag.eof)
return EOF;
- if(!prompt(&lexer.doprompt))
- exit(1); // XXX: hack for signal handling right now...
+ if(lexer.doprompt){
+ if(!prompt())
+ exit(1); // XXX: hack for signal handling right now...
+ lexer.doprompt = 0;
+ }
c = get(runner->cmd.io);
lexer.doprompt = lexer.doprompt || c=='\n' || c==EOF;
@@ -290,7 +293,7 @@ readhere(void)
yyerror("failed to make heredoc");
io = openfd(c);
- prompt(&lexer.doprompt);
+ prompt();
beg=line, s=line, end=arrend(line)-1, len=PAGESIZE;
while((c=get(runner->cmd.io))!=EOF){
@@ -318,7 +321,7 @@ readhere(void)
printstr(io, beg);
/* reset */
s = beg;
- prompt(&lexer.doprompt);
+ prompt();
printchar(io, c);
}
@@ -351,6 +354,12 @@ isidentchar(int c)
return c>' ' && !strchr("!\"#$%&'()+,-./:;<=>?@[\\]^`{|}~", c);
}
+void
+initlexer(int f)
+{
+ lexer.doprompt=f;
+}
+
int
yylex(void)
{
diff --git a/src/cmd/rc/main.c b/src/cmd/rc/main.c
index 81c7b8c..337470a 100644
--- a/src/cmd/rc/main.c
+++ b/src/cmd/rc/main.c
@@ -15,9 +15,9 @@ Shell shell = { 0 };
// functions
void
-initshell(void)
+initshell(int *iflag)
{
- if((shell.interactive=0)){ //isatty(0))){
+ if((*iflag=isatty(0))){
while(tcgetpgrp(0) != (shell.pid = getpgrp()))
kill(-shell.pid, SIGTTIN);
@@ -27,8 +27,7 @@ initshell(void)
signal(SIGTSTP, SIG_IGN);
signal(SIGTTIN, SIG_IGN);
signal(SIGTTOU, SIG_IGN);
- /*
- * NOTE: if SIGCHLD is set to SIG_IGN then
+ /* NOTE: if SIGCHLD is set to SIG_IGN then
* 1. children that terminate do not become zombies
* 2. call a to wait() will block until all children have terminated
* 3. the call to wait will fail with errno == ECHILD
@@ -49,20 +48,47 @@ initshell(void)
// -----------------------------------------------------------------------
// main point of entry
+static void
+usage(void)
+{
+ print(shell.err, "usage: %s [-cei] [file]", argv0);
+ exit(2);
+}
+
int
main(int argc, char *argv[])
{
+ int flag;
+ char *exe, *cmd = nil;
shell.err = openfd(2);
- /* yydebug=1; */
-
initenv();
initpath();
initkeywords();
- initshell();
inithistory();
+ initshell(&flag);
+
+ ARGBEGIN{
+ case 'e': shell.noerror = 1; break;
+ case 'i': shell.interactive = 1; break;
+ case 'I': shell.interactive = 0; break;
+ case 'c':
+ cmd = EARGF(usage());
+ panicf("not implemented");
+ break;
+ default:
+ usage();
+ }ARGEND;
+
+ if(!argc){
+ shell.interactive |= flag;
+ exe = "/dev/stdin";
+ }else
+ exe = *argv++, argc--;
+
+ initlexer(shell.interactive);
/* enablevi(); */
- xboot(argc, argv);
+ xboot(exe, argc, argv);
/* unreachable */
}
diff --git a/src/cmd/rc/prompt.c b/src/cmd/rc/prompt.c
index 1122d54..5579b0d 100644
--- a/src/cmd/rc/prompt.c
+++ b/src/cmd/rc/prompt.c
@@ -10,11 +10,9 @@ resetprompt(void)
}
int
-prompt(ushort *flag)
+prompt(void)
{
- int f = *flag;
-
- if(f){
+ if(runner->flag.user){
if(!readline(promptstr)){
runner->flag.eof = 1;
return 0;
@@ -29,7 +27,6 @@ prompt(ushort *flag)
promptstr = " ";
runner->line++;
- *flag = 0;
}
return 1;
diff --git a/src/cmd/rc/rc.h b/src/cmd/rc/rc.h
index f5c8a6d..f52e4e2 100644
--- a/src/cmd/rc/rc.h
+++ b/src/cmd/rc/rc.h
@@ -157,7 +157,10 @@ struct Shell
int pid;
Io *err;
int status;
- int interactive;
+ struct{
+ int interactive;
+ int noerror;
+ }; /* flags */
Thread *jobs;
};
@@ -187,7 +190,7 @@ int addhistory(char *);
/* prompt.c */
void resetprompt(void);
-int prompt(ushort *);
+int prompt(void);
/* io.c */
Io *openfd(int fd);
@@ -205,6 +208,7 @@ void printchar(Io *, int);
/* lex.c */
int yylex(void);
void yyerror(const char *msg);
+void initlexer(int);
void readhere(void);
Tree *heredoc(Tree *);
diff --git a/src/cmd/rc/rules.mk b/src/cmd/rc/rules.mk
index 206c517..0758439 100644
--- a/src/cmd/rc/rules.mk
+++ b/src/cmd/rc/rules.mk
@@ -24,7 +24,7 @@ BINS_$(d) := $(d)/rc
include share/paths.mk
$(d)/parse.h $(d)/parse.c: $(d)/syntax.y
- bison --debug --defines=$(<D)/parse.h --output=$(<D)/parse.c $(<)
+ bison --defines=$(<D)/parse.h --output=$(<D)/parse.c $(<)
# local rules
$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/libutf/libutf.a $(OBJ_DIR)/base/base.a $(d)/parse.h
diff --git a/src/cmd/rc/var.c b/src/cmd/rc/var.c
index e2d3e08..f67e8e3 100644
--- a/src/cmd/rc/var.c
+++ b/src/cmd/rc/var.c
@@ -18,8 +18,7 @@ static struct Keyword keywords[100]; // sparse map means less hits
// -----------------------------------------------------------------------
// internals
-static
-int
+static int
hash(char *s, int len)
{
int h =0, i = 1;
@@ -30,8 +29,7 @@ hash(char *s, int len)
return h < 0 ? h+len : h;
}
-static
-void
+static void
·setvar(char *name, Word *val, int call)
{
Var *v = var(name);
@@ -44,8 +42,7 @@ void
v->update(v);
}
-static
-char*
+static char*
list2strcolon(Word *words)
{
char *value, *s, *t;
@@ -70,8 +67,7 @@ list2strcolon(Word *words)
return value;
}
-static
-void
+static void
littlepath(Var *v)
{
/* convert $path to $PATH */
@@ -86,8 +82,7 @@ littlepath(Var *v)
·setvar("PATH", w, 1);
}
-static
-void
+static void
bigpath(Var *v)
{
/* convert $PATH to $path */
@@ -103,7 +98,7 @@ bigpath(Var *v)
w = nil;
l = &w;
- /* Doesn't handle escaped colon nonsense. */
+ /* we don't handle escaped colons */
if(p[0] == 0)
p = nil;
@@ -195,13 +190,6 @@ var(char *name)
return globalvar(name);
}
-static
-int
-cmpenv(const void *a, const void *b)
-{
- return strcmp(*(char**)a, *(char**)b);
-}
-
char**
mkenv(void)
{
@@ -218,7 +206,7 @@ mkenv(void)
nchr+=strlen(a->str)+1; \
}
- for(v= runner->local; v; v=v->link){
+ for(v = runner->local; v; v=v->link){
BODY
}
for(h=globals; h!=arrend(globals); h++){
@@ -262,8 +250,8 @@ mkenv(void)
*ep=0;
- qsort((char *)env, nvar, sizeof ep[0], cmpenv);
- return env;
+ sort·string(nvar, env);
+ return env;
}
void
diff --git a/src/cmd/rules.mk b/src/cmd/rules.mk
index 72cd0ce..8a9cfab 100644
--- a/src/cmd/rules.mk
+++ b/src/cmd/rules.mk
@@ -8,6 +8,9 @@ include share/push.mk
DIR := $(d)/rc
include $(DIR)/rules.mk
+DIR := $(d)/core
+include $(DIR)/rules.mk
+
DIR := $(d)/walk
include $(DIR)/rules.mk
diff --git a/src/libfmt/fprint.c b/src/libfmt/fprint.c
index 26343f7..5077359 100644
--- a/src/libfmt/fprint.c
+++ b/src/libfmt/fprint.c
@@ -1,7 +1,7 @@
#include "internal.h"
int
-fprint(int fd, char *fmt, ...)
+fmt·fprint(int fd, char *fmt, ...)
{
int n;
va_list args;
diff --git a/src/libfmt/panic.c b/src/libfmt/panic.c
new file mode 100644
index 0000000..25ee277
--- /dev/null
+++ b/src/libfmt/panic.c
@@ -0,0 +1,15 @@
+#include "internal.h"
+
+void
+fmt·panic(char *fmt, ...)
+{
+ char buf[256];
+ va_list arg;
+
+ va_start(arg, fmt);
+ fmt·vesprint(buf, arrend(buf), fmt, arg);
+ va_end(arg);
+
+ fmt·fprint(2, "%s: %s\n", argv0 ? argv0 : "<prog>", buf);
+ exits("fatal");
+}
diff --git a/src/libfmt/rules.mk b/src/libfmt/rules.mk
index 9080bba..75a7bc7 100644
--- a/src/libfmt/rules.mk
+++ b/src/libfmt/rules.mk
@@ -16,6 +16,7 @@ SRCS_$(d):=\
$(d)/vnsprint.c\
$(d)/vprint.c\
$(d)/vwrite.c\
+ $(d)/panic.c\
$(d)/write.c
LIBS_$(d):=\